【狂神說】JAVA Mybatis 筆記+源碼
- 2020 年 3 月 31 日
- 筆記
簡介
GitHub源碼: https://github.com/Donkequan/Mybatis-Study
分享自寫源碼和筆記
配置用的
- jdk13.0.2 (jdk1.7以上均可)
- Maven
- MySQL 5.7 (mysql5.6以上均可)
1. 配置
官網文檔: https://mybatis.org/mybatis-3/zh/getting-started.html
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>batis</groupId> <artifactId>batis-maven</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>mybatis-01</module> </modules> <!--導入依賴--> <dependencies> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
src/main/resources
<?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://111.230.212.103:3306/mybatis?userSSL=true& userUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="hdk123"/> </dataSource> </environment> </environments> </configuration>
src/main/java
package com.hou.utils; 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; //sqlSessionFactory --> sqlSession public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { //使用mybatis第一步:獲取sqlSessionFactory對象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的實例。SqlSession 提供了在數據庫執行 SQL 命令所需的所有方法。 // 你可以通過 SqlSession 實例來直接執行已映射的 SQL 語句 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
編寫代碼
-
實體類
src/main/java
package com.hou.pogo; public class User { private int id; private String name; private String pwd; public User() { } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + ''' + ", pwd='" + pwd + ''' + '}'; } }
- Dao接口
package com.hou.dao; import com.hou.pogo.User; import java.util.List; public interface UserDao { List<User> getUserList(); }
- 接口實現類
<?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"> <!--namespace綁定一個對應的mapper接口--> <mapper namespace="com.hou.dao.UserDao"> <!--id方法名--> <select id="getUserList" resultType="com.hou.pogo.User"> select * from mybatis.user </select> </mapper>
測試
注意點:
org.apache.ibatis.binding.BindingException: Type interface com.hou.dao.UserDao is not known to the MapperRegistry.
mybatis-config.xml
<?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://111.230.212.103:3306/mybatis?userSSL=true& userUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="hdk123"/> </dataSource> </environment> </environments> <!--每一個mapper.xml都需要註冊--> <mappers> <mapper resource="com/hou/dao/UserMapper.xml"/> </mappers> </configuration>
在兩個pom.xml中加入
<!--在build中配置resources,來防止我們資源導出失敗的問題--> <build> <resources> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> </resource> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
步驟
- 導入包
- 配置數據庫
- 建造工具類
SqlSessionFactoryBuilder
這個類可以被實例化、使用和丟棄,一旦創建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 實例的最佳作用域是方法作用域(也就是局部方法變量)。 你可以重用 SqlSessionFactoryBuilder 來創建多個 SqlSessionFactory 實例,但最好還是不要一直保留着它,以保證所有的 XML 解析資源可以被釋放給更重要的事情。
SqlSessionFactory
SqlSessionFactory 一旦被創建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創建另一個實例。 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重複創建多次,多次重建 SqlSessionFactory 被視為一種代碼「壞習慣」。因此 SqlSessionFactory 的最佳作用域是應用作用域。 有很多方法可以做到,最簡單的就是使用單例模式或者靜態單例模式。
SqlSession
每個線程都應該有它自己的 SqlSession 實例。SqlSession 的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。 絕對不能將 SqlSession 實例的引用放在一個類的靜態域,甚至一個類的實例變量也不行。 也絕不能將 SqlSession 實例的引用放在任何類型的託管作用域中,比如 Servlet 框架中的 HttpSession。 如果你現在正在使用一種 Web 框架,考慮將 SqlSession 放在一個和 HTTP 請求相似的作用域中。 換句話說,每次收到 HTTP 請求,就可以打開一個 SqlSession,返回一個響應後,就關閉它。 這個關閉操作很重要,為了確保每次都能執行關閉操作,你應該把這個關閉操作放到 finally 塊中。 下面的示例就是一個確保 SqlSession 關閉的標準模式
2. 增刪改查
1. namespace
namespace中的包名要和接口一致
2. select
- id:就是對應的namespace的方法名
- resultType:sql語句的返回值!
- parameterType: 參數類型!
- 編寫接口
- 編寫對應的mapper中的對應語句
- 測試
UserMapper
package com.hou.dao; import com.hou.pogo.User; import java.util.List; public interface UserMapper { //查詢全部用戶 List<User> getUserList(); //根據id查詢用戶 User getUserById(int id); //插入用戶 void addUser(User user); //修改用戶 int updateUser(User user); //刪除用戶 int deleteUser(int id); }
UserMapper.xml
<?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"> <!--namespace綁定一個對應的mapper接口--> <mapper namespace="com.hou.dao.UserMapper"> <!--id方法名--> <select id="getUserList" resultType="com.hou.pogo.User"> select * from mybatis.user </select> <select id="getUserById" resultType="com.hou.pogo.User" parameterType="int"> select * from mybatis.user where id = #{id} </select> <!--對象中的屬性可以直接取出來--> <insert id="addUser" parameterType="com.hou.pogo.User"> insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd}); </insert> <update id="updateUser" parameterType="com.hou.pogo.User"> update mybatis.user set name=#{name}, pwd=#{pwd} where id =#{id}; </update> <delete id="deleteUser" parameterType="int"> delete from mybatis.user where id=#{id}; </delete> </mapper>
Test
package com.hou.dao; import com.hou.pogo.User; import com.hou.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class UserDaoTest { @Test public void test(){ // 獲得sqlsession對象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try{ // 1.執行 getmapper UserMapper userDao = sqlSession.getMapper(UserMapper.class); List<User> userList = userDao.getUserList(); // method 2 // List<User> userList = sqlSession.selectList("com.hou.dao.UserDao.getUserList"); for(User user: userList){ System.out.println(user); } }catch(Exception e){ e.printStackTrace(); }finally{ //關閉 sqlSession.close(); } } @Test public void getUserById(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.getUserById(1); System.out.println(user); sqlSession.close(); } //增刪改需要提交事務 @Test public void addUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.addUser(new User(5,"hou","123456")); //提交事務 sqlSession.commit(); sqlSession.close(); } @Test public void updateUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.updateUser(new User(4,"hou","123")); //提交事務 sqlSession.commit(); sqlSession.close(); } @Test public void deleteUser(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); mapper.deleteUser(5); //提交事務 sqlSession.commit(); sqlSession.close(); } }
注意點:增刪改需要提交事務。
3. Map
假如我們的實體類屬性過多,用map,傳遞map的key
<insert id="addUser2" parameterType="map"> insert into mybatis.user (id, name, pwd) values (#{id1}, #{name1}, #{pwd1}); </insert>
int addUser2(Map<String, Object> map);
@Test public void addUser2(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String, Object> map = new HashMap<String, Object>(); map.put("id1",5); map.put("name1","dong"); map.put("pwd1","12345"); mapper.addUser2(map); //提交事務 sqlSession.commit(); sqlSession.close(); }
4.模糊查詢
java代碼執行的時候,傳遞通配符%
<select id="getUserLike" resultType="com.hou.pogo.User"> select * from mybatis.user where name like #{value} </select>
@Test public void getUserLike(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> list = mapper.getUserLike("%o%"); for(User user : list){ System.out.println(user); } sqlSession.close(); }
3. 配置解析
1. 核心配置文件
- mybatis-config.xml
configuration(配置) properties(屬性) settings(設置) typeAliases(類型別名) typeHandlers(類型處理器) objectFactory(對象工廠) plugins(插件) environments(環境配置) environment(環境變量) transactionManager(事務管理器) dataSource(數據源) databaseIdProvider(數據庫廠商標識) mappers(映射器)
2. 環境配置(environments)
MyBatis 可以配置成適應多種環境
不過要記住:儘管可以配置多個環境,但每個 SqlSessionFactory 實例只能選擇一種環境。
Mybatis 默認的事務管理器是JDBC,連接池:POOLED
3. 屬性
我們可以通過properties屬性來引用配置文件
這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性文件中配置這些屬性,也可以在 properties 元素的子元素中設置。 (db.properties)
編寫一個配置文件
db.properties
driver = com.mysql.jdbc.Driver url = "jdbc:mysql://111.230.212.103:3306/mybatis?userSSL=true&userUnicode=true&characterEncoding=UTF-8" username = root password = hdk123
在核心配置文件中引入
mybatis-config.xml (同時有的話,優先走外面properties)
<?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> <!--引入外部配置文件--> <!--<properties resource="db.properties"/>--> <properties resource="db.properties"> <property name="username" value="root"></property> <property name="password" value="hdk123"></property> </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> <!--每一個mapper.xml都需要註冊--> <mappers> <mapper resource="com/hou/dao/UserMapper.xml"/> </mappers> </configuration>
4. 類型別名(typeAliases)
類型別名可為 Java 類型設置一個縮寫名字。
<typeAliases> <typeAlias type="com.hou.pogo.User" alias="User"></typeAlias> </typeAliases>
掃描實體類的包,默認別名就為這個類的類名首字母小寫
<typeAliases> <package name="com.hou.pogo"></package> </typeAliases>
在實體類,比較少的時候使用第一種,實體類多使用第二種。
第一種可以自定義,第二則不行,但是 若有註解,則別名為其註解值 。
@Alias("hello") public class User { }
5. 設置
設置名 | 描述 | 有效值 | 默認值 |
---|---|---|---|
cacheEnabled | 全局性地開啟或關閉所有映射器配置文件中已配置的任何緩存。 | true | false | true |
lazyLoadingEnabled | 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關係中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。 |
true | false | false |
logImpl | 指定 MyBatis 所用日誌的具體實現,未指定時將自動查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未設置 |
6. 其他
- typeHandlers(類型處理器)
- objectFactory(對象工廠)
- plugins(插件)
- mybatis-generator-core
- mybatis-plus
- 通用mapper
7. 映射器
方式一: [推薦使用]
<mappers> <mapper resource="com/hou/dao/UserMapper.xml"/> </mappers>
方式二:
<mappers> <mapper class="com.hou.dao.UserMapper" /> </mappers>
- 接口和它的Mapper必須同名
- 接口和他的Mapper必須在同一包下
方式三:
<mappers> <!--<mapper resource="com/hou/dao/UserMapper.xml"/>--> <!--<mapper class="com.hou.dao.UserMapper" />--> <package name="com.hou.dao" /> </mappers>
- 接口和它的Mapper必須同名
- 接口和他的Mapper必須在同一包下
8.生命周期和作用域
作用域和生命周期類別是至關重要的,因為錯誤的使用會導致非常嚴重的並發問題。
SqlSessionFactoryBuilder:
- 一旦創建了 SqlSessionFactory,就不再需要它了 。
- 局部變量
SqlSessionFactory:
- 就是數據庫連接池。
- 一旦被創建就應該在應用的運行期間一直存在 ,沒有任何理由丟棄它或重新創建另一個實例 。 多次重建 SqlSessionFactory 被視為一種代碼「壞習慣」。
- 因此 SqlSessionFactory 的最佳作用域是應用作用域。
- 最簡單的就是使用單例模式或者靜態單例模式。
SqlSession:
- 每個線程都應該有它自己的 SqlSession 實例。
- 連接到連接池的請求!
- SqlSession 的實例不是線程安全的,因此是不能被共享的 ,所以它的最佳的作用域是請求或方法作用域。
- 用完之後趕緊關閉,否則資源被佔用。
4. 解決屬性名和字段名不一致的問題
數據庫中的字段
新建一個項目,拷貝之前,測試實體字段不一致的情況
User
package com.hou.pogo; public class User { private int id; private String name; private String password; }
問題:
User{id=2, name=’wang’, password=’null’}
解決方法:
核心配置文件
- 起別名
<select id="getUserById" resultType="User" parameterType="int"> select id,name,pwd as password from mybatis.user where id = #{id} </select>
- resultMap 結果集映射
<?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"> <!--namespace綁定一個對應的mapper接口--> <mapper namespace="com.hou.dao.UserMapper"> <select id="getUserById" resultMap="UserMap" parameterType="int"> select * from mybatis.user where id = #{id} </select> <!--結果集映射--> <resultMap id="UserMap" type="User"> <!--colunm 數據庫中的字段,property實體中的屬性--> <result column="id" property="id"></result> <result column="name" property="name"></result> <result column="pwd" property="password"></result> </resultMap> </mapper>
-
resultMap
元素是 MyBatis 中最重要最強大的元素。 -
ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。
<resultMap id="UserMap" type="User"> <!--colunm 數據庫中的字段,property實體中的屬性--> <!--<result column="id" property="id"></result>--> <!--<result column="name" property="name"></result>--> <result column="pwd" property="password"></result> </resultMap>
5. 日誌
1. 日誌工廠
如果一個數據庫操作出現了異常,我們需要排錯。日誌就是最好的助手。
曾經:sout,debug
現在:日誌工廠
logImpl
- SLF4J
- LOG4J [掌握]
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING [掌握]
- NO_LOGGING
具體使用哪一個,在設置中設定
STDOUT_LOGGING 標誌日誌輸出
mybatis-confi中
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
2. Log4j
-
先導包
pom.xml下
<dependencies> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies>
-
新建log4j.properties文件
### set log levels ### 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/hou.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
- 配置實現
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
- Log4j使用
package com.hou.dao; import com.hou.pojo.User; import com.hou.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.apache.log4j.Logger; import org.junit.Test; public class UserDaoTest { static Logger logger = Logger.getLogger(UserDaoTest.class); @Test public void test(){ // 獲得sqlsession對象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try{ // 1.執行 getmapper UserMapper userDao = sqlSession.getMapper(UserMapper.class); logger.info("測試"); User user = userDao.getUserById(2); System.out.println(user); }catch(Exception e){ e.printStackTrace(); }finally{ //關閉 sqlSession.close(); } } @Test public void testLog4j(){ logger.info("info:進入了testlog4j"); logger.debug("debug:進入了testlog4j"); logger.error("error:進入了testlog4j"); } }
6. 分頁
1. Limit 分頁
語法:
SELECT * from user limit startIndex,pageSize; SELECT * from user limit 0,2;
package com.hou.dao; import com.hou.pojo.User; import java.util.List; import java.util.Map; public interface UserMapper { //根據id查詢用戶 User getUserById(int id); List<User> getUserByLimit(Map<String, Integer> map); }
xml
<?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"> <!--namespace綁定一個對應的mapper接口--> <mapper namespace="com.hou.dao.UserMapper"> <select id="getUserById" resultMap="UserMap" parameterType="int"> select * from mybatis.user where id = #{id} </select> <!--結果集映射--> <resultMap id="UserMap" type="User"> <!--colunm 數據庫中的字段,property實體中的屬性--> <!--<result column="id" property="id"></result>--> <!--<result column="name" property="name"></result>--> <result column="pwd" property="password"></result> </resultMap> <select id="getUserByLimit" parameterType="map" resultType="User" resultMap="UserMap"> select * from mybatis.user limit #{startIndex},#{pageSize} </select> </mapper>
test類
@Test public void getByLimit(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String, Integer> map = new HashMap<String, Integer>(); map.put("startIndex", 1); map.put("pageSize", 2); List<User> userList = mapper.getUserByLimit(map); for(User user:userList){ System.out.println(user); } sqlSession.close(); }
2. RowBounds分頁
@Test
@Test public void getUserByRow(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); //RowBounds實現 RowBounds rowBounds = new RowBounds(1, 2); //通過java代碼層面 List<User> userList = sqlSession.selectList ("com.hou.dao.UserMapper.getUserByRowBounds", null,rowBounds); for (User user : userList) { System.out.println(user); } sqlSession.close(); }
3. 分頁插件
- pageHelper
7. 使用註解開發
-
刪除 UserMapper.xml
-
UserMapper
package com.hou.dao; import com.hou.pojo.User; import org.apache.ibatis.annotations.Select; import java.util.List; public interface UserMapper { @Select("select * from user") List<User> getUsers(); }
-
核心配置 mybatis-config.xml
<?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> <!--引入外部配置文件--> <properties resource="db.properties"/> <!--可以給實體類起別名--> <typeAliases> <typeAlias type="com.hou.pojo.User" alias="User"></typeAlias> </typeAliases> <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 class="com.hou.dao.UserMapper"></mapper> </mappers> </configuration>
本質:反射機制
底層:動態代理!
Mybatis詳細執行流程:
-
Resource獲取全局配置文件
-
實例化SqlsessionFactoryBuilder
-
解析配置文件流XMLCondigBuilder
-
Configration所有的配置信息
-
SqlSessionFactory實例化
-
trasactional事務管理
-
創建executor執行器
-
創建SqlSession
-
實現CRUD
-
查看是否執行成功
-
提交事務
-
關閉
註解CRUD
package com.hou.dao; import com.hou.pojo.User; import org.apache.ibatis.annotations.*; import java.util.List; public interface UserMapper { @Select("select * from user") List<User> getUsers(); //方法存在多個參數,所有的參數必須加@Param @Select("select * from user where id = #{id}") User getUserById(@Param("id") int id); @Insert("insert into user (id, name, pwd) values" + "(#{id},#{name},#{password})") int addUser(User user); @Update("update user set name=#{name}, pwd=#{password} " + "where id=#{id}") int updateUser(User user); @Delete("delete from user where id=#{id}") int deleteUser(@Param("id") int id); }
MybatisUtile
package com.hou.utils; 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; //sqlSessionFactory --> sqlSession public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try { //使用mybatis第一步:獲取sqlSessionFactory對象 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(true); } }
Test
package com.hou.dao; import com.hou.pojo.User; import com.hou.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.List; public class UserDaoTest { @Test public void test(){ // 獲得sqlsession對象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try{ // 1.執行 getmapper UserMapper userDao = sqlSession.getMapper(UserMapper.class); List<User> userList = userDao.getUsers(); for (User user : userList) { System.out.println(user); } }catch(Exception e){ e.printStackTrace(); }finally{ //關閉 sqlSession.close(); } } @Test public void getuserById(){ // 獲得sqlsession對象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try{ // 1.執行 getmapper UserMapper userDao = sqlSession.getMapper(UserMapper.class); User user = userDao.getUserById(1); System.out.println(user); }catch(Exception e){ e.printStackTrace(); }finally{ //關閉 sqlSession.close(); } } @Test public void addUser(){ // 獲得sqlsession對象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try{ // 1.執行 getmapper UserMapper userDao = sqlSession.getMapper(UserMapper.class); userDao.addUser(new User(6, "kun","123")); }catch(Exception e){ e.printStackTrace(); }finally{ //關閉 sqlSession.close(); } } @Test public void updateUser(){ // 獲得sqlsession對象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try{ // 1.執行 getmapper UserMapper userDao = sqlSession.getMapper(UserMapper.class); userDao.updateUser(new User(6, "fang","123")); }catch(Exception e){ e.printStackTrace(); }finally{ //關閉 sqlSession.close(); } } @Test public void deleteUser(){ // 獲得sqlsession對象 SqlSession sqlSession = MybatisUtils.getSqlSession(); try{ // 1.執行 getmapper UserMapper userDao = sqlSession.getMapper(UserMapper.class); userDao.deleteUser(6); }catch(Exception e){ e.printStackTrace(); }finally{ //關閉 sqlSession.close(); } } }
8. Lombok
-
在IDEA中安裝lombok插件
-
配置
<dependencies> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency> </dependencies>
-
@Getter and @Setter @FieldNameConstants @ToString @EqualsAndHashCode @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog @Data @Builder @SuperBuilder @Singular @Delegate @Value @Accessors @Wither @With @SneakyThrows
@Data: 無參構造,get,set,toString,hashCode
在實體類上加註解
package com.hou.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor public class User { private int id; private String name; private String password; }
9. 多對一處理
-
多個學生關聯一個老師(多對一)
-
集合(一對多)
1. 建表
CREATE TABLE `teacher` ( `id` INT(10) NOT NULL PRIMARY KEY, `name` VARCHAR(30) DEFAULT NULL )ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO teacher (`id`, `name`) VALUES (1, 'hou'); CREATE TABLE `student` ( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid` (`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO student (`id`, `name`, `tid`) VALUES (1, 'xiao1', 1); INSERT INTO student (`id`, `name`, `tid`) VALUES (2, 'xiao2', 1); INSERT INTO student (`id`, `name`, `tid`) VALUES (3, 'xiao3', 1); INSERT INTO student (`id`, `name`, `tid`) VALUES (4, 'xiao4', 1); INSERT INTO student (`id`, `name`, `tid`) VALUES (5, 'xiao5', 1);
-
新建實體類
package com.hou.pojo; import lombok.Data; @Data public class Student { private int id; private String name; //學生需要關聯一個老師 private Teacher teacher; }
package com.hou.pojo; import lombok.Data; @Data public class Teacher { private int id; private String name; }
-
建立Mapper接口
-
建立Mapper.xml
-
測試是否能夠成功
2. 按照查詢嵌套處理
StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.hou.dao.StudentMapper"> <select id="getStudent" resultMap="StudentTeacher"> select * from student; </select> <resultMap id="StudentTeacher" type="com.hou.pojo.Student"> <result property="id" column="id"></result> <result property="name" column="name"></result> <!--對象使用assiociation--> <!--集合用collection--> <association property="teacher" column="tid" javaType="com.hou.pojo.Teacher" select="getTeacher"></association> </resultMap> <select id="getTeacher" resultType="com.hou.pojo.Teacher"> select * from teacher where id = #{id}; </select> </mapper>
3. 按照結果嵌套處理
select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid=t.id;
<select id="getStudent2" resultMap="StudentTeacher2"> select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid=t.id; </select> <resultMap id="StudentTeacher2" type="com.hou.pojo.Student"> <result property="id" column="sid"></result> <result property="name" column="sname"></result> <association property="teacher" javaType="com.hou.pojo.Teacher"> <result property="name" column="tname"></result> </association> </resultMap>
property 映射到列結果的字段或屬性。
column 數據庫中的列名,或者是列的別名。
10. 一對多
一個老師擁有多個學生
對於老師而言就是一對多
1.環境搭建
實體類
package com.hou.pojo; import lombok.Data; import java.util.List; @Data public class Teacher { private int id; private String name; private List<Student> studentList; }
package com.hou.pojo; import lombok.Data; @Data public class Student { private int id; private String name; private int tid; }
2. 按照結果查詢
<select id="getTeacher" resultMap="TeacherStudent"> select s.id sid, s.name sname, t.name tname, t.id tid from student s, teacher t where s.tid = t.id and t.id = #{id}; </select> <resultMap id="TeacherStudent" type="com.hou.pojo.Teacher"> <result property="id" column="tid"></result> <result property="name" column="tname"></result> <!--集合中的泛型信息,我們用oftype獲取--> <collection property="studentList" ofType="com.hou.pojo.Student"> <result property="id" column="sid"></result> <result property="name" column="sname"></result> </collection> </resultMap>
3. 按照查詢嵌套處理
<select id="getTeacher2" resultMap="TeacherStudent2"> select * from mybatis.teacher where id = #{id} </select> <resultMap id="TeacherStudent2" type="com.hou.pojo.Teacher"> <collection property="studentList" column="id" javaType="ArrayList" ofType="com.hou.pojo.Student" select="getStudentByTeacherId"></collection> </resultMap> <select id="getStudentByTeacherId" resultType="com.hou.pojo.Student"> select * from mybatis.student where tid = #{id} </select>
小結
- 關聯 – association 多對一
- 集合 – collection 一對多
- javaType & ofType
- JavaType用來指定實體中屬性類型
- ofType映射到list中的類型,泛型中的約束類型
注意點:
- 保證sql可讀性,盡量保證通俗易懂
- 注意字段問題
- 如果問題不好排查錯誤,使用日誌
11. 動態sql
動態sql:根據不同的條件生成不同的SQL語句
1. 搭建環境
create table `blog`( `id` varchar(50) not null comment '博客id', `title` varchar(100) not null comment '博客標題', `author` varchar(30) not null comment '博客作者', `create_time` datetime not null comment '創建時間', `views` int(30) not null comment '瀏覽量' )ENGINE=InnoDB DEFAULT CHARSET=utf8
實體類
package com.hou.pojo; import lombok.Data; import java.util.Date; @Data public class Blog { private String id; private String title; private String author; private Date createTime; private int views; }
核心配置
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.hou.mapper.BlogMapper"> <insert id="addBlog" parameterType="Blog"> insert into mybatis.blog (id, title, author, create_time, views) values (#{id}, #{title}, #{author}, #{create_time}, #{views}); </insert> </mapper>
新建隨機生成ID包
package com.hou.utils; import org.junit.Test; import java.util.UUID; @SuppressWarnings("all") public class IDUtiles { public static String getId(){ return UUID.randomUUID().toString().replaceAll("-",""); } @Test public void test(){ System.out.println(getId()); } }
測試類:添加數據
import com.hou.mapper.BlogMapper; import com.hou.pojo.Blog; import com.hou.utils.IDUtiles; import com.hou.utils.MybatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.Date; public class MyTest { @Test public void addBlog(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setId(IDUtiles.getId()); blog.setAuthor("houdongun"); blog.setCreateTime(new Date()); blog.setViews(999); blog.setTitle("first"); blogMapper.addBlog(blog); blog.setId(IDUtiles.getId()); blog.setTitle("second"); blogMapper.addBlog(blog); blog.setId(IDUtiles.getId()); blog.setTitle("third"); blogMapper.addBlog(blog); blog.setId(IDUtiles.getId()); blog.setTitle("forth"); blogMapper.addBlog(blog); sqlSession.close(); } }
2. if
<select id="queryBlogIF" parameterType="map" resultType="Blog"> select * from mybatis.blog where 1=1 <if test="title != null"> and title = #{title} </if> <if test="author != author"> and author = #{author} </if> </select>
test
@Test public void queryBlogIF(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); Map map = new HashMap(); // map.put("title", "second"); map.put("author", "houdongun"); List<Blog> list = blogMapper.queryBlogIF(map); for (Blog blog : list) { System.out.println(blog); } sqlSession.close(); }
3. choose、when、otherwise
<select id="queryBlogchoose" parameterType="map" resultType="Blog"> select * from mybatis.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>
4. trim、where、set
<update id="updateBlog" parameterType="map"> update mybatis.blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id} </update>
trim 可以自定義
SQL片段
有些時候我們有一些公共部分
-
使用sql便簽抽取公共部分
-
在使用的地方使用include標籤
<sql id="if-title-author"> <if test="title != null"> title = #{title} </if> <if test="author != null"> and author = #{author} </if> </sql> <select id="queryBlogIF" parameterType="map" resultType="Blog"> select * from mybatis.blog <where> <include refid="if-title-author"></include> </where> </select>
注意:
- 最好基於單表
- sql里不要存在where標籤
5. for-each
<!--ids是傳的,#{id}是遍歷的--> <select id="queryBlogForeach" parameterType="map" resultType="Blog"> select * from mybatis.blog <where> <foreach collection="ids" item="id" open="and (" close=")" separator="or"> id=#{id} </foreach> </where> </select>
test
@Test public void queryBlogForeach(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); Map map = new HashMap(); ArrayList<Integer> ids = new ArrayList<Integer>(); ids.add(1); ids.add(3); map.put("ids",ids); List<Blog> list = blogMapper.queryBlogForeach(map); for (Blog blog : list) { System.out.println(blog); } sqlSession.close(); }
12. 緩存(了解)
1. 一級緩存
- 開啟日誌
- 測試一個session中查詢兩次相同記錄。
緩存失效:
- 映射語句文件中的所有 insert、update 和 delete 語句會刷新緩存。
- 查詢不同的mapper.xml
- 手動清除緩存
一級緩存默認開啟,只在一次sqlseesion中有效
2. 二級緩存
- 開啟全局緩存
<setting name="cacheEnabled" value="true"/>
- 在當前mapper.xml中使用二級緩存
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
test
@Test public void test(){ SqlSession sqlSession = MybatisUtils.getSqlSession(); SqlSession sqlSession1 = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.queryUserByid(1); System.out.println(user); sqlSession.close(); UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class); User user1 = userMapper1.queryUserByid(1); System.out.println(user1); System.out.println(user==user1); sqlSession1.close(); }
只用cache時加序列化
<cache/>
實體類
package com.hou.pojo; import lombok.Data; import java.io.Serializable; @Data public class User implements Serializable { private int id; private String name; private String pwd; public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } }
小結:
- 只有開啟了二級緩存,在Mapper下有效
- 所有數據都會先放在一級緩存
- 只有當回話提交,或者關閉的時候,才會提交到二級緩存
3. 自定義緩存-ehcache
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.0</version> </dependency>
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="java.io.tmpdir/Tmp_EhCache"/> <!-- 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,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。 --> <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"/> </ehcache>