環境說明:html
學習前須要掌握:java
如何得到 MyBatis?mysql
maven倉庫:git
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> 123456
Github : https://github.com/mybatis/mybatis-3/releasesgithub
數據持久化shell
爲何須要須要持久化?數據庫
Dao 層,Service 層,Controller 層…apache
最重要的一點:使用的人多!緩存
CREATE DATABASE `mybatis`; USE `mybatis`; DROP TABLE IF EXISTS `user`; CREATE TABLE `user` ( `id` int(20) NOT NULL, `name` varchar(30) DEFAULT NULL, `pwd` varchar(30) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; insert into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'張三','abcdef'),(3,'李四','987654');
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency>
能夠根據幫助文檔來進行編寫
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/kuang/dao/userMapper.xml"/> </mappers> </configuration>
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //獲取SqlSession鏈接 public static SqlSession getSession(){ return sqlSessionFactory.openSession(); } }
public class User { private int id; //id private String name; //姓名 private String pwd; //密碼 //構造,有參,無參 //set/get //toString() }
import com.kuang.pojo.User; import java.util.List; public interface UserMapper { List<User> selectUser(); }
注意 namespace
不要寫錯!
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.dao.UserMapper"> <select id="selectUser" resultType="com.kuang.pojo.User"> select * from user </select> </mapper>
**junit 包測試 **
public class MyTest { @Test public void selectUser() { SqlSession session = MybatisUtils.getSession(); //方法一: //List<User> users = session.selectList("com.kuang.mapper.UserMapper.selectUser"); //方法二: UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user: users){ System.out.println(user); } session.close(); } }
一、Maven 靜態資源過濾問題
<resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> </resources>
select標籤是mybatis中最經常使用的標籤之一
select語句有不少屬性能夠詳細配置每一條SQL語句
練習 1 :根據 id 查詢 用戶
1.在 UserMapper 中添加對應方法
public interface UserMapper { //查詢所有用戶 List<User> selectUser(); //根據id查詢用戶 User selectUserById(int id); }
2.在UserMapper.xml中添加 select
語句
<select id="selectUserById" resultType="com.anti.pojo.User"> select * from user where id = #{id} </select>
3.在測試類中測試
@Test public void tsetSelectUserById() { SqlSession session = MybatisUtils.getSession(); //獲取SqlSession鏈接 UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); System.out.println(user); session.close(); }
4.運行結果
練習2:根據 密碼 和名字 查詢用戶
方法一:直接在方法中傳遞參數
@Param
屬性。@Param
中設置的值便可,不須要到單獨設置參數類型。//經過密碼和名字查詢用戶 User selectUserByNP(@Param("username") String username,@Param("pwd") String pwd); //mapper.xml <select id="selectUserByNP" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select>
方法二:萬能Map
User selectUserByNP2(Map<String,Object> map);
parameterType="map"
<select id="selectUserByNP2" parameterType="map" resultType="com.kuang.pojo.User"> select * from user where name = #{username} and pwd = #{pwd} </select>
@Test public void test03(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("username","張三"); map.put("pwd","abcdef"); User user = mapper.selectUserByNP2(map); System.out.println(user); }
若是參數過多,咱們能夠考慮直接使用 Map 實現,若是參數比較少,直接傳遞參數便可。
咱們通常使用 insert 標籤進行插入操做,它的配置和 select 標籤差很少.
練習1:增長一個用戶
1.在 UserMapper 接口中添加對應的方法
//添加一個用戶 int addUser(User user);
二、在UserMapper.xml中添加insert語句
<insert id="addUser" parameterType="com.anti.pojo.User"> insert into user (id,name,pwd) values (#{id},#{name},#{pwd}) </insert>
3.測試
@Test public void testAddUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(5,"王五","zxcvbn"); int i = mapper.addUser(user); System.out.println(i); session.commit(); //提交事務,重點!不寫的話不會提交到數據庫 session.close(); }
注意點:增、刪、改操做須要提交事務!
咱們通常使用update標籤進行更新操做,它的配置和select標籤差很少。
練習:修改用戶的信息
一、同理,編寫接口方法
//修改一個用戶 int updateUser(User user);
二、編寫對應的配置文件SQL
<update id="updateUser" parameterType="com.kuang.pojo.User"> update user set name=#{name},pwd=#{pwd} where id = #{id} </update>
三、測試
@Test public void testUpdateUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1); user.setPwd("asdfgh"); int i = mapper.updateUser(user); System.out.println(i); session.commit(); //提交事務,重點!不寫的話不會提交到數據庫 session.close(); }
需求:根據id刪除一個用戶
一、同理,編寫接口方法
//根據id刪除用戶 int deleteUser(int id);
二、編寫對應的配置文件SQL
<delete id="deleteUser" parameterType="int"> delete from user where id = #{id} </delete>
三、測試
@Test public void testDeleteUser() { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int i = mapper.deleteUser(5); System.out.println(i); session.commit(); //提交事務,重點!不寫的話不會提交到數據庫 session.close(); }
小結:
@Param
參數,尤爲是多個參數時,必須寫上!parameterType
和 resultType
都寫上!第1種(推薦):在 Java代碼中添加 SQL通配符。
List<User> users = mapper.selectLikeUser("%朱%");
<select id="selectLikeUser"> select * from user where name like #{name} </select>
第2種(不推薦):在 SQL 語句中拼接通配符,會引發 SQL 注入。
String name = "朱"; List<User> users = mapper.selectLikeUser(name);
<select id="selectLikeUser"> select * from user where name like "%" #{name} "%" </select>
configuration(配置) properties(屬性) settings(設置) typeAliases(類型別名) typeHandlers(類型處理器) objectFactory(對象工廠) plugins(插件) environments(環境配置) environment(環境變量) transactionManager(事務管理器) dataSource(數據源) databaseIdProvider(數據庫廠商標識) mappers(映射器) <!-- 注意元素節點的順序!順序不對會報錯 -->
咱們能夠閱讀 mybatis-config.xml
上面的 dtd
的頭文件!
<environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments>
配置 MyBatis 的多套運行環境,將 SQL 映射到多個不一樣的數據庫上,必須指定其中一個爲默認運行環境(經過default指定)
子元素節點:environment
dataSource 元素使用標準的 JDBC 數據源接口來配置 JDBC 鏈接對象的資源。
數據源是必須配置的。
有三種內建的數據源類型
type="[UNPOOLED|POOLED|JNDI]")
UNPOOLED
:這個數據源的實現只是每次被請求時打開和關閉鏈接。
POOLED
:這種數據源的實現利用「池」的概念將 JDBC 鏈接對象組織起來 , 這是一種使得併發 Web 應用快速響應請求的流行處理方式。
JNDI
:這個數據源的實現是爲了能在如 Spring 或應用服務器這類容器中使用,容器能夠集中或在外部配置數據源,而後放置一個 JNDI 上下文的引用。
數據源也有不少第三方的實現,好比dbcp,c3p0,druid等等....
子元素節點:transactionManager
- [ 事務管理器 ]
<!-- 語法 --> <transactionManager type="[ JDBC | MANAGED ]"/>
mappers
file:///
的 URL),或類名和包名等。映射器是MyBatis中最核心的組件之一,在MyBatis 3以前,只支持xml映射器,即:全部的SQL語句都必須在xml文件中配置。而從MyBatis 3開始,還支持接口映射器,這種映射器方式容許以Java代碼的方式註解定義SQL語句,很是簡潔。<!-- 使用相對於類路徑的資源引用 --> <mappers> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
<!-- 使用徹底限定資源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> </mappers>
<!-- 使用映射器接口實現類的徹底限定類名 須要配置文件名稱和接口名稱一致,而且位於同一目錄下 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> </mappers>
<!-- 將包內的映射器接口實現所有註冊爲映射器 可是須要配置文件名稱和接口名稱一致,而且位於同一目錄下 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
mapper文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.mapper.UserMapper"> </mapper>
namespace 中文意思:命名空間,做用以下:
namespace 和子元素的 id 聯合保證惟一 , 區別不一樣的mapper
綁定 DAO 接口
namespace 命名規則 : 包名 + 類名
數據庫這些屬性都是可外部配置且可動態替換的,既能夠在典型的 Java 屬性文件中配置,亦可經過 properties 元素的子元素來傳遞。具體的請參考官方文檔
咱們來優化咱們的配置文件
第一步 ; 在resources
資源目錄下新建一個 db.properties
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=UTC username=root password=123456
第二步 : 將文件導入 properties
配置文件
<configuration> <!--導入properties文件--> <properties resource="db.properties"/> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration>
類型別名是爲 Java 類型設置一個短的名字。它只和 XML 配置有關,存在的意義僅在於用來減小類徹底限定名的冗餘。
<!--配置別名,注意順序--> <typeAliases> <typeAlias type="com.anti.pojo.User" alias="User"/> </typeAliases>
當這樣配置時,User
能夠用在任何使用 com.kuang.pojo.User
的地方。
也能夠指定一個包名,MyBatis 會在包名下面搜索須要的 Java Bean,好比:
<typeAliases> <package name="com.anti.pojo"/> </typeAliases>
每個在包 com.anti.pojo
中的 Java Bean,在沒有註解的狀況下,會使用 Bean 的首字母小寫的非限定類名來做爲它的別名。
如有註解,則別名爲其註解值。見下面的例子:
@Alias("user") public class User { ... }
去官網查看一下Mybatis默認的一些類型別名: https://mybatis.org/mybatis-3/zh/configuration.html#typeAliases
設置(settings)相關 => 查看幫助文檔
懶加載
日誌實現
緩存開啓關閉
一個配置完整的 settings 元素的示例以下:
<settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="autoMappingBehavior" value="PARTIAL"/> <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="25"/> <setting name="defaultFetchSize" value="100"/> <setting name="safeRowBoundsEnabled" value="false"/> <setting name="mapUnderscoreToCamelCase" value="false"/> <setting name="localCacheScope" value="SESSION"/> <setting name="jdbcTypeForNull" value="OTHER"/> <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/> </settings>
理解咱們目前已經討論過的不一樣做用域和生命週期類是相當重要的,由於錯誤的使用會致使很是嚴重的併發問題。
咱們能夠先畫一個流程圖,分析一下 Mybatis 的執行過程!
null
能夠看到查詢出來的結果集中 username
屬性爲 null
。
MyBatis 會根據這些查詢的列名(會將列名轉化爲小寫,數據庫不區分大小寫) , 去對應的實體類中查找相應列名的 set方法
設值 , 因爲找不到 setUsernmae()
, 因此 username 返回 null ; 【自動映射】
1.【不推薦】修改實體類的屬性名,使其和數據庫字段名一致。
2.【不推薦】在 SQL 語句中使用別名對應實體類中的屬性名。
3.【推薦】使用在 xxMapper.xml` 中使用 ResultMap 進行結果集映射。
【多對一】的處理:
【一對多】的處理:
一、關聯 - association
二、集合 - collection
三、因此 association
是用於一對一和多對一,而 collection
是用於一對多的關係
四、JavaType
和 ofType
都是用來指定對象類型的
JavaType
是用來指定 pojo
中屬性的類型ofType
指定的是映射到 List
集合屬性中 pojo
的類型。注意說明:
一、保證SQL的可讀性,儘可能通俗易懂
二、根據實際要求,儘可能編寫性能更高的SQL語句
三、注意屬性名和字段不一致的問題
四、注意一對多和多對一 中:字段和屬性對應的問題
五、儘可能使用Log4j,經過日誌來查看本身的錯誤
咱們在測試 SQL 的時候,要是可以在控制檯輸出 SQL 的話,是否是就可以有更快的排錯效率?
若是一個 數據庫相關的操做出現了問題,咱們能夠根據輸出的 SQL 語句快速排查問題。
對於以往的開發過程,咱們會常用到debug模式來調節,跟蹤咱們的代碼執行過程。可是如今使用 Mybatis 是基於接口,配置文件的源代碼執行過程。所以,咱們必須選擇日誌工具來做爲咱們開發,調節程序的工具。
Mybatis內置的日誌工廠提供日誌功能,具體的日誌實現有如下幾種工具:
具體選擇哪一個日誌實現工具由 MyBatis 的內置日誌工廠肯定。它會使用最早找到的(按上文列舉的順序查找)。若是一個都未找到,日誌功能就會被禁用。
指定 MyBatis 應該使用哪一個日誌記錄實現。若是此設置不存在,則會自動發現日誌記錄實現。
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
測試,能夠看到控制檯有大量的輸出!咱們能夠經過這些輸出來判斷程序到底哪裏出了Bug
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. PooledDataSource forcefully closed/removed all connections. Opening JDBC Connection Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary. Created connection 355115154. Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@152aa092] ==> Preparing: select * from teacher where id = ? ==> Parameters: 1(Integer) <== Columns: id, name <== Row: 1, 秦老師 ====> Preparing: select * from student ====> Parameters: <==== Columns: id, name, tid <==== Row: 1, 小明, 1 <==== Row: 2, 小紅, 1 <==== Row: 3, 小張, 1 <==== Row: 4, 小李, 1 <==== Row: 5, 小王, 1 <==== Total: 5 <== Total: 1 Teacher(id=null, name=秦老師, students=[Student(id=1, name=小明), Student(id=2, name=小紅), Student(id=3, name=小張), Student(id=4, name=小李), Student(id=5, name=小王)])
簡介:
使用步驟:
一、導入log4j的包
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
二、配置文件編寫
#將等級爲DEBUG的日誌信息輸出到console和file這兩個目的地,console和file的定義在下面的代碼 log4j.rootLogger=DEBUG,console,file #控制檯輸出的相關設置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #文件輸出的相關設置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日誌輸出級別 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
三、setting設置日誌實現
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
輸出結果:
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 71706941. [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@446293d] [com.anti.dao.TeacherMapper.getTeacherById]-==> Preparing: select * from teacher where id = ? [com.anti.dao.TeacherMapper.getTeacherById]-==> Parameters: 1(Integer) [com.anti.dao.TeacherMapper.student]-====> Preparing: select * from student [com.anti.dao.TeacherMapper.student]-====> Parameters: [com.anti.dao.TeacherMapper.student]-<==== Total: 5 [com.anti.dao.TeacherMapper.getTeacherById]-<== Total: 1 Teacher(id=null, name=秦老師, students=[Student(id=1, name=小明), Student(id=2, name=小紅), Student(id=3, name=小張), Student(id=4, name=小李), Student(id=5, name=小王)])
在學習 MyBatis 等持久層框架的時候,會常常對數據進行增刪改查操做,使用最多的是對數據庫進行查詢操做,若是查詢大量數據的時候,咱們每每使用分頁進行查詢,也就是每次處理小部分數據,這樣對數據庫壓力就在可控範圍內。
使用Limit實現分頁
#語法 SELECT * FROM table LIMIT page,pageSize page = 當前頁數 = (page-1)*pageSize pageSize = 一頁多少條數據
UserMapper.java
public interface UserMapper { //選擇所有用戶實現分頁 List<User> selectUser(Map<String,Integer> map); }
UserMapper.xml
<select id="selectUser" parameterType="map" resultType="com.anti.pojo.User"> select * from user limit #{page},#{pageSize} </select>
MyTest.java
@Test public void test01(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); HashMap<String, Integer> map = new HashMap<String, Integer>(); int page = 1; //第幾頁 int limit = 10; //每頁多少數據 map.put("page",(page-1) * limit); map.put("limit",limit); List<User> users = mapper.selectUser(map); for (User user : users) { System.out.println(user); } session.close(); }
結果 :
==> Preparing: select * from user limit ?,? ==> Parameters: 0(Integer), 10(Integer) <== Columns: uid, name, gender, birthday, dept, cno, address, phone, remark, password, type <== Row: 999, 管理員, M, 2020/09/02, AAA, 0, AAA, null, null, 123, 0 <== Row: 10101, 怡香, M, 2020/02/08, Accounting, 1, Cameroon, 823-954-4217, null, 1, 1 <== Row: 10102, 惟楓, M, 2020/01/25, Accounting, 2, Palestinian Territory, 978-827-9275, null, 2, 1 <== Row: 10103, 海程, M, 2020/09/05, Human Resources, 3, Thailand, 978-712-9955, null, 3, 1 <== Row: 10104, 琪煜, M, 2020/10/07, Accounting, 4, Palestinian Territory, 730-153-0025, null, 4, 1 <== Row: 10105, 彥軍, F, 2020/11/05, Services, 5, China, 504-460-1356, null, 5, 1 <== Row: 10106, 宇涵, F, 2020/11/08, Product Management, 6, Argentina, 252-143-6848, null, 6, 1 <== Row: 10107, 辰華, M, 2019/11/25, Business Development, 7, Philippines, 884-928-7856, null, 7, 1 <== Row: 10108, 曉烽, M, 2020/08/05, Engineering, 8, Philippines, 152-366-5638, null, 8, 1 <== Row: 10109, 尹智, F, 2020/01/12, Human Resources, 9, Argentina, 803-602-3704, null, 9, 1 <== Total: 10
MyBatis最初配置信息是基於 XML ,映射語句(SQL)也是定義在 XML 中的。而到MyBatis 3提供了新的基於註解的配置。不幸的是,java 註解的的表達力和靈活性十分有限。
最強大的 MyBatis 映射並不能用註解來構建
@select ()
@update ()
@Insert ()
@delete ()
注意:利用註解開發就不須要mapper.xml映射文件了 .
1.在接口中添加註解
public interface UserMapper { //查詢所有用戶 @Select("select * from user") List<User> getUsers(); }
2.在mybatis的核心配置文件中注入
<!--使用class綁定接口--> <mappers> <mapper class="com.anti.mapper.UserMapper"/> </mappers>
3.測試
@Test public void test01(){ UserMapper mapper = MybatisUtils.getSession().getMapper(UserMapper.class); List<User> users = mapper.getUsers(); for (User user : users) { System.out.println(user); } }
4.結果
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@3c0be339] ==> Preparing: select * from user ==> Parameters: <== Columns: id, name, pwd <== Row: 1, 狂神, 123456 <== Row: 2, 張三, abcdef <== Row: 3, 李四, 987654 <== Row: 5, 王五, zxcvbn <== Total: 4 User(id=1, name=狂神, pwd=123456) User(id=2, name=張三, pwd=abcdef) User(id=3, name=李四, pwd=987654) User(id=5, name=王五, pwd=zxcvbn) Process finished with exit code 0
<!--需求1: 根據做者名字和博客名字來查詢博客! 若是做者名字爲空,那麼只根據博客名字查詢,反之,則根據做者名來查詢 select * from blog where title = #{title} and author = #{author} --> <select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog where <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>
這樣寫咱們能夠看到,若是 author
等於 null
,那麼查詢語句爲 select * from user where title=#{title}
,可是若是 title
爲空呢?那麼查詢語句爲 select * from user where and author=#{author}
,這是錯誤的 SQL 語句,如何解決呢?請看下面的 where 語句!
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </where> </select>
這個「where」標籤會知道若是它包含的標籤中有返回值的話,它就插入一個‘where’。此外,若是標籤返回的內容是以AND 或OR 開頭的,則它會剔除掉。
同理,上面的對於查詢 SQL 語句包含 where 關鍵字,若是在進行更新操做的時候,含有 set 關鍵詞,咱們怎麼處理呢?
<!--注意set是用的逗號隔開--> <update id="updateBlog" parameterType="map"> update blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id}; </update>
有時候,咱們不想用到全部的查詢條件,只想選擇其中的一個,查詢條件有一個知足便可,使用 choose 標籤能夠解決此類問題,相似於 Java 的 switch 語句。
choose 與 if 的區別:
<select id="queryBlogChoose" parameterType="map" resultType="blog"> select * from blog <where> <choose> <when test="title != null"> title = #{title} </when> <when test="author != null"> and author = #{author} </when> <otherwise> and views = #{views} </otherwise> </choose> </where> </select>
有時候可能某個 sql 語句咱們用的特別多,爲了增長代碼的重用性,簡化代碼,咱們須要將這些代碼抽取出來,而後使用時直接調用。
提取SQL片斷:
<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql>
引用SQL片斷:
<select id="queryBlogIf" parameterType="map" resultType="blog"> select * from blog <where> <!-- 引用 sql 片斷,若是refid 指定的不在本文件中,那麼須要在前面加上 namespace --> <include refid="if-title-author"></include> <!-- 在這裏還能夠引用其餘的 sql 片斷 --> </where> </select>
注意:
需求:咱們須要查詢 blog 表中 id 分別爲1,2,3的博客信息
<select id="queryBlogForeach" parameterType="map" resultType="blog"> select * from blog <where> <!-- collection:指定輸入對象中的集合屬性 item:每次遍歷生成的對象 open:開始遍歷時的拼接字符串 close:結束時拼接的字符串 separator:遍歷對象之間須要拼接的字符串 select * from blog where 1=1 and (id=1 or id=2 or id=3) --> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>
小結:
其實動態 sql 語句的編寫每每就是一個拼接的問題,爲了保證拼接準確,咱們最好首先要寫原生的 sql 語句出來,而後在經過 mybatis 動態sql 對照着改,防止出錯。多在實踐中使用纔是熟練掌握它的技巧。
一、什麼是緩存 [ Cache ]?
二、爲何使用緩存?
三、什麼樣的數據能使用緩存?
MyBatis包含一個很是強大的查詢緩存特性,它能夠很是方便地定製和配置緩存。緩存能夠極大的提高查詢效率。
MyBatis系統中默認定義了兩級緩存:一級緩存 和 二級緩存
默認狀況下,只有一級緩存開啓。(SqlSession級別的緩存,也稱爲本地緩存)
二級緩存須要手動開啓和配置,他是基於namespace級別的緩存。
爲了提升擴展性,MyBatis定義了緩存接口Cache。咱們能夠經過實現Cache接口來自定義二級緩存
一級緩存也叫本地緩存:
與數據庫同一次會話期間查詢到的數據會放在本地緩存中。
之後若是須要獲取相同的數據,直接從緩存中拿,沒必須再去查詢數據庫;
1.在mybatis中加入日誌,方便測試結果
2.編寫接口方法
//根據id查詢用戶 User queryUserById(@Param("id") int id);
3.接口對應的Mapper文件
<select id="queryUserById" resultType="user"> select * from user where id = #{id} </select>
4.測試
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }
5.結果分析
一級緩存是SqlSession級別的緩存,是一直開啓的,咱們關閉不了它;
一級緩存失效狀況:沒有使用到當前的一級緩存,效果就是,還須要再向數據庫中發起一次查詢請求!
1.sqlSession不一樣
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); SqlSession session2 = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); session2.close(); }
觀察結果:發現發送了兩條SQL語句!
結論:每一個sqlSession中的緩存相互獨立
2.sqlSession相同,查詢條件不一樣
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); User user2 = mapper2.queryUserById(2); System.out.println(user2); System.out.println(user==user2); session.close(); }
觀察結果:發現發送了兩條SQL語句!很正常的理解
結論:當前緩存中,不存在這個數據
3.sqlSession相同,兩次查詢之間執行了增刪改操做!
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); HashMap map = new HashMap(); map.put("name","kuangshen"); map.put("id",4); mapper.updateUser(map); User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }
觀察結果:查詢在中間執行了增刪改操做後,從新執行了
結論:由於增刪改操做可能會對當前數據產生影響
4.sqlSession相同,手動清除一級緩存
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); session.clearCache();//手動清除緩存 User user2 = mapper.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session.close(); }
一級緩存就是一個map
1.開啓全局緩存 【mybatis-config.xml】
<setting name="cacheEnabled" value="true"/>
2.去每一個mapper.xml中配置使用二級緩存,這個配置很是簡單;【xxxMapper.xml】
<cache/> 官方示例=====>查看官方文檔 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true" /> 這個更高級的配置建立了一個 FIFO 緩存,每隔 60 秒刷新,最多能夠存儲結果對象或列表的 512 個引用,並且返回的對象被認爲是隻讀的,所以對它們進行修改可能會在不一樣線程中的調用者產生衝突。
3.代碼測試
@Test public void testQueryUserById(){ SqlSession session = MybatisUtils.getSession(); SqlSession session2 = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); UserMapper mapper2 = session2.getMapper(UserMapper.class); User user = mapper.queryUserById(1); System.out.println(user); session.close(); User user2 = mapper2.queryUserById(1); System.out.println(user2); System.out.println(user==user2); session2.close(); }
結論:
緩存原理圖:
Ehcache是一種普遍使用的 java 分佈式緩存,用於通用緩存;
1.要在應用程序中使用Ehcache,須要引入依賴的 jar 包
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.1.0</version> </dependency>
2.在mapper.xml中使用對應的緩存便可
<mapper namespace = 「org.acme.FooMapper」 > <cache type = 「org.mybatis.caches.ehcache.EhcacheCache」 /> </mapper>
3.編寫ehcache.xml文件,若是在加載時未找到/ehcache.xml資源或出現問題,則將使用默認配置。
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <!-- diskStore:爲緩存路徑,ehcache分爲內存和磁盤兩級,此屬性定義磁盤的緩存位置。參數解釋以下: user.home – 用戶主目錄 user.dir – 用戶當前工做目錄 java.io.tmpdir – 默認臨時文件路徑 --> <diskStore path="./tmpdir/Tmp_EhCache"/> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/> <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> <!-- defaultCache:默認緩存策略,當ehcache找不到定義的緩存時,則使用這個緩存策略。只能定義一個。 --> <!-- name:緩存名稱。 maxElementsInMemory:緩存最大數目 maxElementsOnDisk:硬盤最大緩存個數。 eternal:對象是否永久有效,一但設置了,timeout將不起做用。 overflowToDisk:是否保存到磁盤,當系統當機時 timeToIdleSeconds:設置對象在失效前的容許閒置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閒置時間無窮大。 timeToLiveSeconds:設置對象在失效前容許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。 diskPersistent:是否緩存虛擬機重啓期數據 Whether the disk store persists between restarts of the Virtual Machine. The default value is false. diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每一個Cache都應該有本身的一個緩衝區。 diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。 memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你能夠設置爲FIFO(先進先出)或是LFU(較少使用)。 clearOnFlush:內存數量最大時是否清除。 memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,默認策略)、FIFO(先進先出)、LFU(最少訪問次數)。 FIFO,first in first out,這個是你們最熟的,先進先出。 LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,緩存的元素有一個hit屬性,hit值最小的將會被清出緩存。 LRU,Least Recently Used,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又須要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。 --> </ehcache>