一、MyBatis簡介
MyBatis是面向sql的持久層框架,他封裝了jdbc訪問數據庫的過程,我們開發,只需專注於sql語句本身的拼裝,其它賦值的過程全部可以交給MyBatis去完成。
與Hibernate比較:
1.Hibernate學習門檻不低,要精通門檻更高。門檻高在怎么設計O/R映射,在性能和對象模型之間如何權衡取得平衡,以及怎樣用好Hibernate緩存與數據加載策略方面需要你的經驗和能力都很強才行。國內目前前的情況精通hibernate技術大牛非常少。
2.sql優化方面,Hibernate的查詢會將表中的所有字段查詢出來,這一點會有性能消耗。當然了,Hibernate也可以自己寫SQL來指定需要查詢的字段,但這樣就破壞了Hibernate開發的簡潔性。說得更深入一些,如果有個查詢要關聯多張表,比如5張表,10張表時,而且,我們要取的字段只是其中幾張表的部分字段。這時用hibernate時就會顯得非常力不從心。就算用hibernate的sqlquery,后續的維護工作也會讓人發狂。
二、入門案例
1、數據庫的准備(創表語句)
1 -- ---------------------------- 2 -- Table structure for `user` 3 -- ---------------------------- 4 DROP TABLE IF EXISTS `user`; 5 CREATE TABLE `user` ( 6 `id` int(11) NOT NULL AUTO_INCREMENT, 7 `username` varchar(32) NOT NULL COMMENT '用戶名稱', 8 `birthday` date DEFAULT NULL COMMENT '生日', 9 `sex` char(1) DEFAULT NULL COMMENT '性別', 10 `address` varchar(256) DEFAULT NULL COMMENT '地址', 11 PRIMARY KEY (`id`) 12 ) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8; 13 14 -- ---------------------------- 15 -- Records of user 16 -- ---------------------------- 17 INSERT INTO `user` VALUES ('1', '王五', null, '2', null); 18 INSERT INTO `user` VALUES ('10', '張三', '2014-07-10', '1', '北京市'); 19 INSERT INTO `user` VALUES ('16', '張小明', null, '1', '河南鄭州'); 20 INSERT INTO `user` VALUES ('22', '陳小明', null, '1', '河南鄭州'); 21 INSERT INTO `user` VALUES ('24', '張三豐', null, '1', '河南鄭州'); 22 INSERT INTO `user` VALUES ('25', '陳小明', null, '1', '河南鄭州'); 23 INSERT INTO `user` VALUES ('26', '王五', null, null, null);
2、使用idea新建maven-archetype-quickstart項目
3、引入依賴
1 <dependency> 2 <groupId>mysql</groupId> 3 <artifactId>mysql-connector-java</artifactId> 4 <version>5.1.45</version> 5 </dependency> 6 <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> 7 <dependency> 8 <groupId>org.mybatis</groupId> 9 <artifactId>mybatis</artifactId> 10 <version>3.2.7</version> 11 </dependency>
4、於resources文件夾中創建SqlMapConfig.xml
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE configuration 3 PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-config.dtd"> 5 <configuration> 6 <!-- 和spring整合后 environments配置將廢除 --> 7 <environments default="development"> 8 <environment id="development"> 9 <!-- 使用jdbc事務管理 --> 10 <transactionManager type="JDBC" /> 11 <!-- 數據庫連接池 --> 12 <dataSource type="POOLED"> 13 <property name="driver" value="com.mysql.jdbc.Driver" /> 14 <property name="url" 15 value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8" /> 16 <property name="username" value="root" /> 17 <property name="password" value="" /> 18 </dataSource> 19 </environment> 20 </environments> 21 22 <mappers> 23 <!-- 第一種方式,加載 resource--> 24 <mapper resource="User.xml"></mapper> 25 <mapper resource="UserMapper.xml"/> 26 27 <!-- 第三種方式,包掃描器要求(推薦使用此方式): 28 1、映射文件與接口同一目錄下 29 2、映射文件名必需與接口文件名稱一致 30 --> 31 <!--<package name="com.cenobitor.mapper"/>--> 32 </mappers> 33 </configuration>
5、創建實體類User
1 package com.cenobitor.pojo; 2 3 import java.util.Date; 4 5 public class User { 6 7 private Integer id; 8 private String username;// 用戶姓名 9 private String sex;// 性別 10 private Date birthday;// 生日 11 private String address;// 地址 12 private String uuid; 13 14 ...... 15 }
6、配置SQL查詢的映射文件(resources目錄)
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <!-- namespace:命名空間,類似於java包,主要用於隔離sql語句的,后續有重要作用 6 #{}:占位符,相當於jdbc的? 7 ${}:字符串拼接指令,注意如果入參為普通數據類型時,括號里面只能寫value 8 --> 9 <mapper namespace="user"> 10 <!-- id:sql id標識sql語句的唯一標識 11 parameterType:入參的數據類型 12 resultType:返回結果的數據類型 13 --> 14 <select id="getUserById" parameterType="int" resultType="com.cenobitor.pojo.User"> 15 SELECT 16 `id`, 17 `username`, 18 `birthday`, 19 `sex`, 20 `address` 21 FROM `user` 22 WHERE id = #{id} 23 </select> 24 25 <!-- resultType:如果返回結果是集合時,只需要設置為元素的數據類型就可 --> 26 <select id="getUserByName" parameterType="String" resultType="com.cenobitor.pojo.User"> 27 SELECT 28 `id`, 29 `username`, 30 `birthday`, 31 `sex`, 32 `address` 33 FROM `user` 34 WHERE username LIKE '%${value}%' 35 </select> 36 37 <insert id="insertUser" parameterType="com.cenobitor.pojo.User"> 38 INSERT INTO USER (`username`,`birthday`,`sex`,`address`) 39 VALUES (#{username},#{birthday},#{sex},#{address}) 40 </insert> 41 42 <!--返回MySql自增主鍵--> 43 <!-- useGeneratedKeys:標識插入使用自增id 44 keyProperty:與useGeneratedKeys配套使用,用於綁定主鍵接收的pojo屬性 45 --> 46 <insert id="insertUserKey" parameterType="com.cenobitor.pojo.User" 47 useGeneratedKeys="true" keyProperty="id"> 48 49 <!-- selectKey:用於配置主鍵返回 50 keyProperty:要綁定的pojo屬性 51 resultType:屬性數據類型 52 order:指定什么時候執行,AFTER之后 53 --> 54 <!-- <selectKey keyProperty="id" resultType="int" order="AFTER"> 55 SELECT LAST_INSERT_ID() 56 </selectKey> --> 57 58 INSERT INTO USER (`username`,`birthday`,`sex`,`address`) 59 VALUES (#{username},#{birthday},#{sex},#{address}) 60 </insert> 61 62 <!--返回MySql的uuid返回主鍵--> 63 <insert id="insertUserUUID" parameterType="com.cenobitor.pojo.User"> 64 65 <!-- selectKey:用於配置主鍵返回 66 keyProperty:要綁定的pojo屬性 67 resultType:屬性數據類型 68 order:指定什么時候執行,AFTER之后 69 --> 70 <selectKey keyProperty="uuid" resultType="String" order="BEFORE"> 71 SELECT UUID() 72 </selectKey> 73 74 INSERT INTO USER (`username`,`birthday`,`sex`,`address`,`uuid`) 75 VALUES (#{username},#{birthday},#{sex},#{address},#{uuid}) 76 </insert> 77 78 <update id="updateUser" parameterType="com.cenobitor.pojo.User"> 79 UPDATE USER SET username = #{username} WHERE id = #{id} 80 </update> 81 82 <delete id="deleteUser" parameterType="com.cenobitor.pojo.User"> 83 DELETE FROM `user` WHERE `id` = #{id} 84 </delete> 85 86 </mapper>
7、加載映射文件,在SqlMapConfig.xml配置mappers節點
8、編寫測試類
1 package com.cenobitor; 2 3 import com.cenobitor.Utils.SqlSessionFactoryUtils; 4 import com.cenobitor.pojo.User; 5 import junit.framework.Test; 6 import junit.framework.TestCase; 7 import junit.framework.TestSuite; 8 import org.apache.ibatis.io.Resources; 9 import org.apache.ibatis.session.SqlSession; 10 import org.apache.ibatis.session.SqlSessionFactory; 11 import org.apache.ibatis.session.SqlSessionFactoryBuilder; 12 13 import java.io.IOException; 14 import java.io.InputStream; 15 import java.util.Date; 16 import java.util.List; 17 18 /** 19 * Unit test for simple App. 20 */ 21 public class AppTest extends TestCase { 22 //根據id查找用戶 23 public void testGetUserById() throws IOException { 24 25 //創建SqlSessionFactoryBuilder對象 26 SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder(); 27 //查找配置文件,創建輸入流 28 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 29 //加載配置文件,創建SqlSessionFactory 30 SqlSessionFactory sqlSessionFactory = sfb.build(inputStream); 31 //創建SqlSession 32 SqlSession sqlSession = sqlSessionFactory.openSession(); 33 //執行查詢,參數一:要查詢的statementId,參數二:sql語句入參 34 User user = sqlSession.selectOne("user.getUserById", 1); 35 //輸入查詢結果 36 System.out.println(user); 37 38 //釋放資源 39 sqlSession.close(); 40 } 41 42 //根據用戶名查找用戶列表 43 public void testGetUserByName(){ 44 SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory(); 45 SqlSession sqlSession = sqlSessionFactory.openSession(); 46 List<User> list = sqlSession.selectList("user.getUserByName", "張"); 47 for (User user : list) { 48 System.out.println(user); 49 } 50 51 sqlSession.close(); 52 } 53 54 //插入用戶 55 public void testInsertUser(){ 56 SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory(); 57 SqlSession sqlSession = sqlSessionFactory.openSession(); 58 59 User user = new User(); 60 user.setUsername("貂蟬"); 61 user.setSex("0"); 62 user.setBirthday(new Date()); 63 user.setAddress("呂布"); 64 65 //執行插入語句 66 sqlSession.insert("user.insertUser",user); 67 //提交事務 68 sqlSession.commit(); 69 //釋放資源 70 sqlSession.close(); 71 } 72 73 //Mysql自增返回 74 public void testInsertUserKey(){ 75 SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory(); 76 SqlSession sqlSession = sqlSessionFactory.openSession(); 77 78 User user = new User(); 79 user.setUsername("楊玉環"); 80 user.setSex("0"); 81 user.setBirthday(new Date()); 82 user.setAddress("李隆基"); 83 84 //執行插入語句 85 sqlSession.insert("user.insertUserKey", user); 86 System.out.println(user); 87 //提交事務 88 sqlSession.commit(); 89 //釋放資源 90 sqlSession.close(); 91 } 92 93 //Mysql的uuid返回主鍵 94 //注:在使用uuid之前數據庫user表要先加上uuid2字段、user的pojo也要加上相應屬性 95 public void testInsertUserUUID(){ 96 SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory(); 97 SqlSession sqlSession = sqlSessionFactory.openSession(); 98 99 User user = new User(); 100 user.setUsername("孫尚香"); 101 user.setSex("0"); 102 user.setBirthday(new Date()); 103 user.setAddress("劉備"); 104 105 //執行插入語句 106 sqlSession.insert("user.insertUserUUID", user); 107 System.out.println(user); 108 //提交事務 109 sqlSession.commit(); 110 //釋放資源 111 sqlSession.close(); 112 } 113 114 //修改用戶 115 public void testUpdateUser(){ 116 SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory(); 117 SqlSession sqlSession = sqlSessionFactory.openSession(); 118 119 User user = new User(); 120 user.setUsername("呂雉"); 121 user.setId(32); 122 123 //執行插入語句 124 sqlSession.update("user.updateUser",user); 125 126 //提交事務 127 sqlSession.commit(); 128 //釋放資源 129 sqlSession.close(); 130 } 131 132 //刪除用戶 133 public void testDeleteUser(){ 134 SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getSqlSessionFactory(); 135 SqlSession sqlSession = sqlSessionFactory.openSession(); 136 sqlSession.delete("user.deleteUser",32); 137 sqlSession.commit(); 138 sqlSession.close(); 139 } 140 }
9、抽取SqlSessionFactoryUtils工具類,共享SqlSessionFactory的對象
1 public class SqlSessionFactoryUtils { 2 private SqlSessionFactoryUtils(){} 3 4 private static class SqlSessionFactoryInstance{ 5 6 public static SqlSessionFactory sqlSessionFactory; 7 8 static { 9 try { 10 sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("SqlMapConfig.xml")); 11 } catch (IOException e) { 12 e.printStackTrace(); 13 } 14 } 15 } 16 17 public static SqlSessionFactory getSqlSessionFactory(){ 18 return SqlSessionFactoryInstance.sqlSessionFactory; 19 } 20 21 }
三、MyBatis架構圖
四、MyBatis 動態代理Dao開發
1、開發規則
- namespace必須是接口的全路徑名
- 接口的方法名必須與映射文件的sql id 一致
- 接口的輸入參數必須與映射文件的parameterType類型一致
- 接口的返回類型必須與映射文件的resultType類型一致
2、動態代理Dao開發步驟
①創建UserMapper.xml映射文件
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE mapper 3 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 4 "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 5 <mapper namespace="com.cenobitor.mapper.UserMapper"> 6 7 <select id="getUserById" parameterType="int" resultType="com.cenobitor.pojo.User"> 8 SELECT 9 `id`, 10 `username`, 11 `birthday`, 12 `sex`, 13 `address` 14 FROM `user` 15 WHERE id = #{id} 16 </select> 17 18 <select id="getUserByName" parameterType="String" resultType="com.cenobitor.pojo.User"> 19 SELECT 20 `id`, 21 `username`, 22 `birthday`, 23 `sex`, 24 `address` 25 FROM `user` 26 WHERE username LIKE '%${value}%' 27 </select> 28 29 <insert id="insertUser" parameterType="com.cenobitor.pojo.User"> 30 INSERT INTO USER (`username`,`birthday`,`sex`,`address`) 31 VALUES (#{username},#{birthday},#{sex},#{address}) 32 </insert> 33 34 </mapper>
②創建UserMapper接口
1 package com.cenobitor.mapper; 2 3 import com.cenobitor.pojo.User; 4 import java.util.List; 5 6 public interface UserMapper { 7 8 /**根據用戶ID查詢用戶信息 9 * @param id 10 * @return 11 */ 12 User getUserById(Integer id); 13 14 /** 15 * 根據用戶名查找用戶列表 16 * @param name 17 * @return 18 */ 19 List<User> getUserByName(String name); 20 21 /** 22 * 添加用戶 23 * @param user 24 */ 25 void insertUser(User user); 26 27 }
③加載UserMpper.xml
④建立測試類
1 public class UserMapperTest { 2 3 @Test 4 public void getUserById() { 5 SqlSessionFactory sqlSessionFactory = 6 SqlSessionFactoryUtils.getSqlSessionFactory(); 7 SqlSession sqlSession = sqlSessionFactory.openSession(); 8 UserMapper mapper = sqlSession.getMapper(UserMapper.class); 9 User user = mapper.getUserById(31); 10 System.out.println(user);//User{id=31, username='楊玉環', sex='0', birthday=Sat Apr 07 00:00:00 CST 2018, address='李隆基', uuid='null'} 11 sqlSession.close(); 12 } 13 14 @Test 15 public void getUserByName() { 16 SqlSessionFactory sqlSessionFactory = 17 SqlSessionFactoryUtils.getSqlSessionFactory(); 18 SqlSession sqlSession = sqlSessionFactory.openSession(); 19 UserMapper mapper = sqlSession.getMapper(UserMapper.class); 20 List<User> users = mapper.getUserByName("張"); 21 for (User user : users) { 22 System.out.println(user); 23 } 24 /*User{id=10, username='張三', sex='1', birthday=Thu Jul 10 00:00:00 CST 2014, address='北京市', uuid='null'} 25 User{id=16, username='張小明', sex='1', birthday=null, address='河南鄭州', uuid='null'} 26 User{id=24, username='張三豐', sex='1', birthday=null, address='河南鄭州', uuid='null'}*/ 27 sqlSession.close(); 28 } 29 30 @Test 31 public void insertUser() { 32 SqlSessionFactory sqlSessionFactory = 33 SqlSessionFactoryUtils.getSqlSessionFactory(); 34 SqlSession sqlSession = sqlSessionFactory.openSession(); 35 UserMapper mapper = sqlSession.getMapper(UserMapper.class); 36 User user = new User(); 37 user.setUsername("lisi"); 38 user.setSex("1"); 39 user.setBirthday(new Date()); 40 user.setAddress("北京"); 41 mapper.insertUser(user); 42 sqlSession.commit(); 43 sqlSession.close(); 44 } 45 }
五、SqlMapConfig.xml配置
1、properties
①屬於核心文件配置
1 <!-- 加載規則,首先加載標簽內部屬性,再加載外部文件,名稱相同時,會替換相同名稱的內容 --> 2 <properties resource="jdbc.properties"> 3 <property name="jdbc.username" value="root1"/> 4 <property name="jdbc.password" value="root"/> 5 </properties>
②jdbc.properties
1 jdbc.driver=com.mysql.jdbc.Driver 2 jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8 3 jdbc.username=root 4 jdbc.password=root
2、typeAliases
自定義別名
1 <typeAliases> 2 <!-- 單個別名定義 --> 3 <!-- <typeAlias type="com.itheima.mybatis.pojo.User" alias="user"/> --> 4 <!-- 別名包掃描器(推薦使用此方式),整個包下的類都被定義別名,別名為類名,不區分大小寫--> 5 <package name="com.itheima.mybatis.pojo"/> 6 </typeAliases>
3、mapper
1 <mappers> 2 <!-- 第一種方式,加載 resource--> 3 <mapper resource="mapper/user.xml"/> 4 <!-- <mapper resource="mapper/UserMapper.xml"/> --> 5 6 <!-- 第二種方式,class掃描器要求: 7 1、映射文件與接口同一目錄下 8 2、映射文件名必需與接口文件名稱一致 9 --> 10 <!-- <mapper class="com.itheima.mybatis.mapper.UserMapper"/> --> 11 12 <!-- 第三種方式,包掃描器要求(推薦使用此方式): 13 1、映射文件與接口同一目錄下 14 2、映射文件名必需與接口文件名稱一致 15 --> 16 <package name="com.itheima.mybatis.mapper"/> 17 </mappers>
六、小結
1、#{} 和${}
#{} 表示一個占位符號,通過#{} 可以實現preparedStatement向占位符中設置值,自動進行java類型和jdbc類型轉換。#{} 可以有效防止sql注入。#{}可以接受簡單類型值或pojo屬性值。如果parameterType傳輸單個簡單類型值,#{}括號中可以是value或其他名稱。
${}表示拼接sql串,通過${}可以將parameterType傳入的內容拼接在sql中且不進行jdbc類型轉換,${}可以接受簡單類型值或pojo屬性值,如果parameterType傳輸單個簡單類型值,${}括號中只能是value。
2、parameterType和resultype
parameterType:指定輸入參數類型,mybatis通過ognl從輸入對象中獲取參數值拼接在sql中。
resultType:指定類型的對象。如果有多條數據,則分別進行映射,並把對象放到容器List中。