一、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中。