Mybatis
一. 入门
1. JDBC 回顾
(1). 准备数据库
- 新建 mybatis_learn 数据库
- 执行 sql 脚本
1 -- ---------------------------- 2 -- Table structure for `Blog` 3 -- ---------------------------- 4 DROP TABLE IF EXISTS `Blog`; 5 CREATE TABLE `Blog` ( 6 `id` int(11) NOT NULL AUTO_INCREMENT, 7 `author` varchar(128) NOT NULL COMMENT ‘作者‘, 8 `title` varchar(128) NOT NULL COMMENT ‘博客标题‘, 9 PRIMARY KEY (`id`) 10 ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8; 11 ? 12 -- ---------------------------- 13 -- Records of Blog 14 -- ---------------------------- 15 INSERT INTO `Blog` VALUES (‘1‘, ‘张三‘, ‘Java基础概述‘); 16 INSERT INTO `Blog` VALUES (‘2‘, ‘李四‘, ‘面向对象概述‘); 17 INSERT INTO `Blog` VALUES (‘3‘, ‘王五‘, ‘什么框架‘);
(2). 原生 JDBC 编程步骤
- 加载数据库驱动
- 创建并获取数据库链接
- 设置 sql 语句
- 创建 JDBC Statement对象
- 设置 sql 语句中的参数(使用PreparedStatement)
- 通过 Statement 执行 sql 并获取结果
- 对 sql 执行结果进行解析处理
- 释放资源(ResultSet、Preparedstatement、Connection)
(3). 原生 JDBC 程序
1 package com.richer.jdbc; 2 ? 3 import java.sql.Connection; 4 import java.sql.DriverManager; 5 import java.sql.PreparedStatement; 6 import java.sql.ResultSet; 7 import java.sql.SQLException; 8 ? 9 public class JDBCTest { 10 ? 11 public static void main(String[] args) { 12 Connection connection = null; 13 PreparedStatement preparedStatement = null; 14 ResultSet resultSet = null; 15 ? 16 try { 17 // 1、加载数据库驱动 18 Class.forName("com.mysql.jdbc.Driver"); 19 ? 20 // 2、获取数据库链接 21 /** 22 * 问题1:频繁获取释放数据库连接影响数据库性能 23 * 解决:数据库连接池,C3P0 DBCP DRUID(德鲁伊,阿里巴巴出品) 24 */ 25 connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis_learn?characterEncoding=utf-8", "root", "root"); 26 // 3、定义sql语句 ?表示占位符 27 /** 28 * 问题2:sql语句硬编码,后期难以维护 29 * 如果sql语句和java代码分离,比如放到配置文件当中。mybatis就是这么干的 30 */ 31 String sql = "select * from blog where author = ?"; 32 // 4、获取预处理statement 33 preparedStatement = connection.prepareStatement(sql); 34 // 5、设置参数,第一个参数为sql语句中参数的序号(从1开始),第二个参数为设置的参数值 35 /** 36 * 问题3:参数和占位符一一对应,sql语句where条件发生变化,修改java代码,后期难以维护 37 */ 38 preparedStatement.setString(1, "张三"); 39 // 6、向数据库发出sql执行查询,查询出结果集 40 resultSet = preparedStatement.executeQuery(); 41 // 7、解析处理结果集 42 /** 43 * 问题4:解析麻烦,查询列硬编码 44 * 期望,如果单条记录,给我对象,如果多条记录,给我对象集合 45 */ 46 while (resultSet.next()) { 47 System.out.println(resultSet.getString("id") + " " + resultSet.getString("author")); 48 } 49 } catch (Exception e) { 50 e.printStackTrace(); 51 } finally { 52 // 8、释放资源 53 if (resultSet != null) { 54 try { 55 resultSet.close(); 56 } catch (SQLException e) { 57 e.printStackTrace(); 58 } 59 } 60 if (preparedStatement != null) { 61 try { 62 preparedStatement.close(); 63 } catch (SQLException e) { 64 e.printStackTrace(); 65 } 66 } 67 if (connection != null) { 68 try { 69 connection.close(); 70 } catch (SQLException e) { 71 e.printStackTrace(); 72 } 73 } 74 } 75 } 76 }
(4). 原生 JDBC 问题总结
- 频繁创建、释放数据库连接造成系统资源浪费,影响系统性能。使用数据库连接池技术可以解决此问题。
- sql 语句写在代码中造成代码不易维护,实际应用中 sql 变化的可能较大,sql 变动需要改变 java 代码。
- 向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数一一对应。
- 对结果集解析麻烦(列名硬编码),Sql变化会导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 Pojo 对象解析比较方便。
2. Mybatis 介绍及架构原理
(1) Mybatis 介绍
Mybatis 是一个持久层框架,它的前身是由 Apache 管理的一个顶级项目叫 ibatis,但是在2010年后该项目交给 Google code 管理,改名为 Mybatis,并在2013年迁移到 Github 上。
Mybatis 在 ibatis 的基础上做了一些封装及优化。比如通过 Mapper 动态代理的方式开发 Dao 等。
Mybatis 的特点是使用 Xml 文件来配置 sql 语句。程序员自己写 sql 语句,当需求变化时我们只需要修改配置文件即可,比较灵活。
另外,Mybatis 向 sql 语句传参可以直接传入 java 对象,不必参数和占位符一一对应;把查询到的结果集映射为 java 对象。
(2). Hibernate 架构回顾
(3) Mybatis 架构
Mybatis配置
- 全局配置文件 SqlMapConfig.xml,配置了 Mybatis 的运行环境等信息。Mapper.xml 文件即 sql 映射文件,文件中配置了操作数据库的 sql 语句。此文件需要在 SqlMapConfig.xml 中加载。
- 通过 Mybatis 环境等配置信息构造 SqlSessionFactory,即会话工厂。
- 由会话工厂创建 SqlSession 即会话,操作数据库需要通过 SqlSession 进行。
- Mybatis 底层自定义了 Executor 执行器接口操作数据库,Executor 执行器调用具体的MappedStatement 对象执行数据库操作动作。
- MappedStatement 也是 Mybatis 一个底层封装对象,它包装了 Mybatis 配置信息及 sql 映射信息等。Mapper.xml 文件中一个 sql 对应一个 Mapped Statement 对象,sql 的 id 即是 Mapped statement 的id。
- Executor 通过 MappedStatement 在执 sql 前将输入的 java 对象映射至 sql 中,输入参数映射的意思就是 JDBC 编程中对 PreparedStatement 设置参数。Executor 通过 MappedStatement 在执行 sql 后将输出结果映射至 java 对象中,输出结果映射过程相当于 jdbc 编程中对结果的解析处理过程。
(4) Mybatis 与 Hibernate 的区别
越来越多的企业在用Mybatis,它的市场占有率越来越高。与之相比,Hibernate的市场占有率却在走低
- Hibernate 是一个完全的 ORM 框架,Mybatis 是一个不完全的 ORM 框架
Hibernate 自动化程度高,只需配置 OR 映射关系,不需要写 Sql 语句,怎么执行由框架底层控制。
Mybatis 虽然将 Sql 与 Java 对象做了关系映射,但需要程序员自己写 Sql。
- Hibernate 学习门槛高,Mybatis 学习门槛较低
Hibernate 学习门槛高,精通门槛更高,设计 O/R 映射和 Hibernate 调优都需要很强的经验和能力。
Mybatis 比较容易上手和掌握,重点关注 Sql 语句即可;
- Hibernate 灵活度差但数据库无关性好,Mybatis 灵活度高,但牺牲了数据库无关性
Hibernate 不能干预具体 Sql 的执行,但数据库无关性好,切换不同数据库时只需要切换数据库类型即可。
Mybatis 可严格控制 Sql 的执行性能,灵活度高,适合于软件的需求变化快而且多的软件,但灵活的前提是牺牲了数据库的无关性,如果要实现支持多种数据库的软件则需要自定义多套 Sql 映射文件,工作量大。
选型原则
总之,满足需求的前提下,只要做出维护性、扩展性好的软件架构都是好架构,框架只有合适的才是最好的。
选型建议
访问量小性能要求不高的内网项目,推荐使用全自动的 Hibernate 框架,可以提高开发效率。
访问量大性能要求高的内网或者互联网项目,推荐使用 Mybatis 框架。
3. 第一个 Mybatis 程序
(1). 引入资源 pom.xml
1 <dependencies> 2 <!-- junit --> 3 <dependency> 4 <groupId>junit</groupId> 5 <artifactId>junit</artifactId> 6 <version>4.12</version> 7 <scope>test</scope> 8 </dependency> 9 <!-- mybatis --> 10 <dependency> 11 <groupId>org.mybatis</groupId> 12 <artifactId>mybatis</artifactId> 13 <version>3.2.7</version> 14 </dependency> 15 <!-- mysql 驱动 --> 16 <dependency> 17 <groupId>mysql</groupId> 18 <artifactId>mysql-connector-java</artifactId> 19 <version>5.1.46</version> 20 </dependency> 21 <!-- log4j --> 22 <dependency> 23 <groupId>log4j</groupId> 24 <artifactId>log4j</artifactId> 25 <version>1.2.17</version> 26 </dependency> 27 </dependencies>
(2). 创建 pojo
1 package com.richer.mybatis.pojo; 2 ? 3 public class Blog { 4 private int id; 5 private String author; 6 private String title; 7 ? 8 public int getId() { 9 return id; 10 } 11 ? 12 public void setId(int id) { 13 this.id = id; 14 } 15 ? 16 public String getAuthor() { 17 return author; 18 } 19 ? 20 public void setAuthor(String author) { 21 this.author = author; 22 } 23 ? 24 public String getTitle() { 25 return title; 26 } 27 ? 28 public void setTitle(String title) { 29 this.title = title; 30 } 31 ? 32 @Override 33 public String toString() { 34 return "Blog [id=" + id + ", author=" + author + ", title=" + title + "]"; 35 } 36 }
(3). 创建 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 <environments default="development"> 7 <environment id="development"> 8 <transactionManager type="JDBC" /> 9 <dataSource type="POOLED"> 10 <property name="driver" value="com.mysql.jdbc.Driver" /> 11 <property name="url" 12 value="jdbc:mysql://localhost:3306/mybatis_learn?characterEncoding=utf-8" /> 13 <property name="username" value="root" /> 14 <property name="password" value="root" /> 15 </dataSource> 16 </environment> 17 </environments> 18 <mappers> 19 <mapper resource="sqlmap/BlogMapper.xml" /> 20 </mappers> 21 </configuration>
(4). 创建 BlogMapper.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"> <mapper namespace="test"> <select id="queryBlogById" parameterType="Integer" resultType="com.richer.mybatis.pojo.Blog"> select * from blog where id=#{id} </select> </mapper>
(5). 引入日志配置文件
1 # Global logging configuration 2 log4j.rootLogger=DEBUG, stdout 3 # Console output... 4 log4j.appender.stdout=org.apache.log4j.ConsoleAppender 5 log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 6 log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
(6). 创建测试类
1 public class TestQuickStart { 2 @Test 3 public void testQueryBlogById() throws Exception { 4 // 1. 加载SqlMapConfig.xml配置文件 5 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 6 // 2. 创建SqlSessionFactoryBuilder对象 7 SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); 8 // 3. 创建SqlSessionFactory对象 9 SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); 10 // 4. 创建SqlSession对象 11 SqlSession sqlSession = sqlSessionFactory.openSession(); 12 // 5. 执行SqlSession对象执行查询,获取结果Blog 13 // 第一个参数是BlogMapper.xml的statement的id,第二个参数是执行sql需要的参数; 14 Blog blog = sqlSession.selectOne("queryBlogById", 1); 15 // 6. 打印结果 16 System.out.println(blog); 17 // 7. 释放资源 18 sqlSession.close(); 19 } 20 }
4.Mybatis 入门详解
(1).准备数据库
- 使用数据库 mybatis_learn
- 执行 sql 脚本
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). 需求1 : 根据用户 id 查询一个用户
i. 创建 pojo
1 public class User{ 2 ? 3 private int id; 4 private String username; 5 private String sex; 6 private Date birthday; 7 private String address; 8 ? 9 public int getId() { 10 return id; 11 } 12 public void setId(int id) { 13 this.id = id; 14 } 15 public String getUsername() { 16 return username; 17 } 18 public void setUsername(String username) { 19 this.username = username; 20 } 21 public String getSex() { 22 return sex; 23 } 24 public void setSex(String sex) { 25 this.sex = sex; 26 } 27 public Date getBirthday() { 28 return birthday; 29 } 30 public void setBirthday(Date birthday) { 31 this.birthday = birthday; 32 } 33 public String getAddress() { 34 return address; 35 } 36 public void setAddress(String address) { 37 this.address = address; 38 } 39 @Override 40 public String toString() { 41 return "User [id=" + id + ", username=" + username + ", sex=" + sex 42 + ", birthday=" + birthday + ", address=" + address + "]"; 43 } 44 }
ii.创建 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 <!-- 配置 --> 6 <configuration> 7 <!-- 8 environments 配置数据源环境 9 可以配置多个 environment 10 default : 默认使用哪个数据源,配置对应的 id 11 --> 12 <environments default="development"> 13 <!-- 开发用数据源 --> 14 <environment id="development"> 15 <!-- 16 transactionManager:事务管理器 17 type = "JDBC" : 使用 JDBC 事务管理机制 18 type = "MANAGERED" : mybatis 不管理事务,交给外部框架 19 --> 20 <transactionManager type="JDBC" /> 21 <!-- 22 POOLED : 使用连接池 23 UNPOOLED : 不使用连接池 24 --> 25 <dataSource type="POOLED"> 26 <property name="driver" value="com.mysql.jdbc.Driver" /> 27 <property name="url" 28 value="jdbc:mysql://localhost:3306/mybatis_learn?characterEncoding=utf-8" /> 29 <property name="username" value="root" /> 30 <property name="password" value="root" /> 31 </dataSource> 32 </environment> 33 <!-- 测试用数据源 --> 34 <environment id="test"> 35 <transactionManager type="JDBC" /> 36 <dataSource type="POOLED"> 37 <property name="driver" value="com.mysql.jdbc.Driver" /> 38 <property name="url" 39 value="jdbc:mysql://localhost:3306/mybatis_learn?characterEncoding=utf-8" /> 40 <property name="username" value="root" /> 41 <property name="password" value="root" /> 42 </dataSource> 43 </environment> 44 <!-- 生产用数据源 --> 45 <environment id="produce"> 46 <transactionManager type="JDBC" /> 47 <dataSource type="POOLED"> 48 <property name="driver" value="com.mysql.jdbc.Driver" /> 49 <property name="url" 50 value="jdbc:mysql://localhost:3306/mybatis_learn?characterEncoding=utf-8" /> 51 <property name="username" value="root" /> 52 <property name="password" value="root" /> 53 </dataSource> 54 </environment> 55 </environments> 56 <!-- 加载 sql 映射文件 --> 57 <mappers> 58 <!-- resource 加载 classpath 下指定的 sql 映射文件 --> 59 <mapper resource="sqlmap/UserMapper.xml" /> 60 </mappers> 61 </configuration>
iii. 创建 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 <!-- 6 映射,存放 sql 语句 7 namespace : 分类管理 sql 语句 8 唯一确定 sql 语句 : namespace + "." + id 9 --> 10 <mapper namespace="test"> 11 <!-- 12 查询使用 select 标签 13 id : sql 语句标识符,通常也称为 statement 的 id 14 parameterType : 参数类型(输入映射) 15 resultType : 结果类型(输出映射),配置为 pojo 那么 Mybatis 就把结果集中的每一条记录封装为指定的 pojo 16 --> 17 <select id="queryUserById" parameterType="Integer" 18 resultType="com.richer.mybatis.pojo.User"> 19 <!-- 20 #{} 固定取参语法 21 #{} 取参名称 : parameterType 为基础数据类型时,名称任意 22 parameterType 为 pojo 时,名称为属性名 23 #{} 预编译语句 防止 sql 注入 24 --> 25 select * from user where id=#{id} 26 </select> 27 </mapper>
iv.创建测试类
1 /** 2 * 测试用例 : 根据 id 查询用户 3 * @throws IOException 4 */ 5 @Test 6 public void testQueryUserByID() throws IOException{ 7 // 读取配置文件 8 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 9 // 获取会话工厂 10 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 11 // 获取会话 12 SqlSession sqlSession = sessionFactory.openSession(); 13 /** 14 * 调用 sqlSession 的 api 15 * param1 : sql 语句的唯一标识符 16 * param2 : 传入的参数 17 */ 18 User user = sqlSession.selectOne("test.queryUserById",16); 19 System.out.println(user); 20 sqlSession.close(); 21 }
(3).需求2 : 根据 username 模糊查询用户列表
i. #{ } 取参
UserMapper.xml
<!-- resultType : 返回多条记录时配置和单条一样,mybatis 会自动吧多条数据放到集合里 --> <select id="queryUserByUsername" parameterType="String" resultType="com.richer.mybatis.pojo.User"> select * from user where username like #{username} </select>
测试类
1 /** 2 * 测试用例 : 根据 username 模糊查询用户列表 3 * @throws IOException 4 */ 5 @Test 6 public void testQueryUserByUsername() throws IOException{ 7 // 读取配置文件 8 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 9 // 获取会话工厂 10 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 11 // 获取会话 12 SqlSession sqlSession = sessionFactory.openSession(); 13 /** 14 * 调用 sqlSession 的 api 15 * param1 : sql 语句的唯一标识符 16 * param2 : 传入的参数 17 */ 18 //注意 : 多条数据使用 selectOne 会报异常 TooManyResultsException 19 List<User> list = sqlSession.selectList("test.queryUserByUsername","%王%"); 20 if (list != null && list.size() > 0) { 21 for(User user : list){ 22 System.out.println(user); 23 } 24 } 25 sqlSession.close(); 26 }
ii.${ }取参
UserMapper.xml
1 <select id="queryUserByUsername2" parameterType="String" 2 resultType="com.richer.mybatis.pojo.User"> 3 <!-- 4 $()固定取参语法 5 $()取参名称 : parameterType 为基础数据类型时,名称必须为 value 6 parameterType 为 pojo 时,名称为属性名 7 $()不会添加单引号对 8 $()原理:简单的字符串拼接 9 --> 10 select * from user where username like ${value} 11 </select>
测试类
1 /** 2 * 测试用例 : 根据 username 模糊查询用户列表2 3 * @throws IOException 4 */ 5 @Test 6 public void testQueryUserByUsername2() throws IOException{ 7 // 读取配置文件 8 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 9 // 获取会话工厂 10 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 11 // 获取会话 12 SqlSession sqlSession = sessionFactory.openSession(); 13 /** 14 * 调用 sqlSession 的 api 15 * param1 : sql 语句的唯一标识符 16 * param2 : 传入的参数 17 */ 18 List<User> list = sqlSession.selectList("test.queryUserByUsername2","‘%王%‘"); 19 if (list != null && list.size() > 0) { 20 for(User user : list){ 21 System.out.println(user); 22 } 23 } 24 sqlSession.close(); 25 }
iii.#{ }与${ }取参原则
一般使用 #{ } ,当传入参数为数据库对象使用 ${ }(比如表名)
(4). 需求3 : 保存用户
i.UserMapper.xml
1 <insert id="saveUser" parameterType="com.richer.mybatis.pojo.User" > 2 insert into user(username,sex,birthday,address) 3 values(#{username},#{sex},#{birthday},#{address}) 4 </insert>
ii.测试类
1 /** 2 * 测试用例 : 保存用户 3 * @throws IOException 4 */ 5 @Test 6 public void testSaveUser() throws IOException{ 7 // 读取配置文件 8 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 9 // 获取会话工厂 10 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 11 // 获取会话 12 SqlSession sqlSession = sessionFactory.openSession(); 13 User user = new User(); 14 user.setUsername("妲己"); 15 user.setSex("2"); 16 user.setBirthday(new Date()); 17 user.setAddress("朝歌"); 18 sqlSession.insert("test.saveUser",user); 19 //增加删除修改需要提交事务,底层是 connection 的事务机制 20 sqlSession.commit(); 21 sqlSession.close(); 22 }
iii.返回 mysql 自增主键 id (解决输出 id=0 的问题)
UserMapper.xml
1 <insert id="saveUser" parameterType="com.richer.mybatis.pojo.User" > 2 <!-- 3 selectKey : 查询主键标签 4 order : 查询主键语句在 insert 之前还是之后 5 resultType : 查询出来的主键要回显的属性值 6 --> 7 <selectKey order="AFTER" resultType="Integer" keyProperty="id"> 8 获取最后一个自增 id 9 select last_insert_id() 10 </selectKey> 11 insert into user(username,sex,birthday,address) 12 values(#{username},#{sex},#{birthday},#{address}) 13 </insert>
(5). 需求4 : 更新和删除用户
i. 更新用户
UserMapper.xml
1 <update id="updateUserById" parameterType="com.richer.mybatis.pojo.User"> 2 update user set username = #{username} where id = #{id} 3 </update>
测试类
1 /** 2 * 测试用例 : 根据 id 修改用户 3 * @throws IOException 4 */ 5 @Test 6 public void testUpdateUserById() throws IOException{ 7 // 读取配置文件 8 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 9 // 获取会话工厂 10 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 11 // 获取会话 12 SqlSession sqlSession = sessionFactory.openSession(); 13 User user = new User(); 14 user.setUsername("比干"); 15 user.setId(31); 16 sqlSession.update("test.updateUserById",user); 17 //增加删除修改需要提交事务,底层是 connection 的事务机制 18 sqlSession.commit(); 19 System.out.println(user); 20 sqlSession.close(); 21 }
ii. 删除用户
UserMapper.xml
1 <delete id="deleteUserById" parameterType="Integer"> 2 delete from user where id = #{id} 3 </delete>
测试类
1 /** 2 * 测试用例 : 根据 id 删除用户 3 * @throws IOException 4 */ 5 @Test 6 public void testDeleteUserById() throws IOException{ 7 // 读取配置文件 8 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 9 // 获取会话工厂 10 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 11 // 获取会话 12 SqlSession sqlSession = sessionFactory.openSession(); 13 sqlSession.delete("test.deleteUserById",28); 14 //增加删除修改需要提交事务,底层是 connection 的事务机制 15 sqlSession.commit(); 16 sqlSession.close(); 17 }
(6). 小结
- 频繁创建、释放数据库连接造成系统资源浪费,影响系统性能。使用数据库连接池技术可以解决此问题。解决:在SqlMapConfig.xml中配置数据连接池,使用连接池管理数据库连接。
- Sql语句写在代码中造成代码不易维护,实际应用中Sql变化的可能较大,Sql变动需要改变java代码。
解决:将Sql语句配置在XXXXmapper.xml文件中与Java代码分离。
- 向Sql语句传参数麻烦,因为Sql语句的where条件不一定,可能多也可能少,占位符需要和参数一一对应(硬编码)。
解决:Mybatis自动将Java对象映射至Sql语句,通过statement中的parameterType定义输入参数的类型。
- 对结果集解析麻烦(查询列硬编码),Sql变化会导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成Pojo对象解析比较方便。
解决:Mybatis自动将Sql执行结果映射至Java对象,通过statement中的resultType定义输出结果的类型。
5. Mybatis 的 两种 Dao 开发方式
(1). 原始 Dao 开发方式
UserDao.java
1 public interface UserDao { 2 User queryUserById(int id); 3 }
UserDaoImpl.java
1 public class UserDaoImpl implements UserDao { 2 ? 3 private SqlSessionFactory sqlSessionFactory; 4 ? 5 public UserDaoImpl(SqlSessionFactory sqlSessionFactory) { 6 this.sqlSessionFactory = sqlSessionFactory; 7 } 8 ? 9 public User queryUserById(int id) { 10 SqlSession sqlSession = sqlSessionFactory.openSession(); 11 User user = sqlSession.selectOne("test.queryUserById",id); 12 sqlSession.close(); 13 return user; 14 } 15 }
测试类
1 public class Test_02 { 2 @Test 3 public void testQueryUserById() throws IOException{ 4 // 读取配置文件 5 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 6 // 获取会话工厂 7 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 8 UserDao userDao = new UserDaoImpl(sessionFactory); 9 User user = userDao.queryUserById(27); 10 System.out.println(user); 11 } 12 }
(2). mapper 动态代理开发方式
i. 开发规范
Mapper动态代理开发方式只需要程序员开发 Mapper 接口(相当于 Dao 接口),Mybatis 框架会根据接口定义创建接口的动态代理对象,代理对象的方法同 Dao 接口实现类中的方法。
Mapper 接口开发需要遵循以下4个规范:
- Mapper 映射文件中的 namespace 与 mapper 接口的类路径相同。
- Mapper 接口方法名和 Mapper 映射文件中定义的每个 Sql 的 id 相同
- Mapper 接口方法的输入参数类型和 Mapper 映射文件中定义的每个 Sql 的 ParameterType 的类型相同
- Mapper 接口方法的输出参数类型和 Mapper 映射文件中定义的每个 Sql 的 resultType 的类型相同
ii. UserMapper.xml 映射文件
1 <mapper namespace="com.richer.mybatis.mapper.UserMapper"> 2 <select id="queryUserById" parameterType="Integer" 3 resultType="com.richer.mybatis.pojo.User"> 4 select * from user where id=#{id} 5 </select> 6 </mapper>
iii. SqlMapConfig.xml 加载 UserMapper.xml
1 <mapper resource="mapper/UserMapper.xml" />
iv. UserMapper.java 接口文件
public interface UserMapper { User queryUserById(int id);}
v. 测试类
1 @Test 2 public void testQueryUserById() throws IOException { 3 // 读取配置文件 4 InputStream inputStream = Resources.getResourceAsStream("SqlMapConfig.xml"); 5 // 获取会话工厂 6 SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); 7 SqlSession sqlSession = sessionFactory.openSession(); 8 //使用 jdk 动态代理,面向接口,实现了 InvocationHandler 9 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); 10 User user = userMapper.queryUserById(27); 11 System.out.println(user); 12 }
6. 全局配置文件 SqlMapConfig.xml 说明
(1). 配置内容和顺序
SqlMapConfig.xml 中配置的内容和顺序如下:
- properties 属性配置信息
- settings 全局配置参数
- typeAliases 类型别名
- typeHandlers 类型处理器
- objectFactory 对象工厂
- plugins 插件
- environments 环境集合属性对象
- environment 环境子属性对象
- transactionManager 事务管理
- dataSource 数据源
- mappers 映射器
注意 : 标签是有顺序的 !
(2). properties 属性配置信息
使用的 properties 的好处:配置数据共享
配置 db.properties
1 jdbc.driver=com.mysql.jdbc.Driver 2 jdbc.url=jdbc:mysql://localhost:3306/mybatis_learn?characterEncoding=utf-8 3 jdbc.username=root 4 jdbc.password=root
修改 SqlMapConfig.xml
1 <!-- 用resource属性加载外部配置文件 --> 2 <properties resource="db.properties"> 3 <!-- 在properties内部用property定义属性 --> 4 <!-- 如果外部配置文件有该属性,则内部定义属性被外部属性覆盖 --> 5 <property name="jdbc.username" value="root111" /> 6 </properties> 7 ? 8 <environments default="development"> 9 <environment id="development"> 10 <transactionManager type="JDBC" /> 11 <dataSource type="POOLED"> 12 <property name="driver" value="${jdbc.driver}" /> 13 <property name="url" 14 value="${jdbc.url}" /> 15 <property name="username" value="${jdbc.username}" /> 16 <property name="password" value="${jdbc.password}" /> 17 </dataSource> 18 </environment> 19 </environments>
(3). typeAliases 类型别名
i. Mybatis默认支持的别名
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
map | Map |
ii. 自定义别名
SqlMapConfig.xml
1 <typeAliases> 2 <!-- 定义好的别名不区分大小写 --> 3 <!-- <typeAlias type="com.richer.mybatis.pojo.User" alias="abc"/> --> 4 <!-- 批量别名定义,扫描整个包下的类,别名为类名(大小写不敏感) --> 5 <package name="com.richer.mybatis.pojo"/> 6 </typeAliases>
(4). mappers 映射器
1 <mappers> 2 <!-- resource 加载 classpath 下指定的 sql 映射文件 --> 3 <!-- <mapper resource="sqlmap/UserMapper.xml" /> 4 <mapper resource="mapper/UserMapper.xml" /> --> 5 <!-- 6 mapper 动态代理方式使用 7 加载 sql 映射文件的时候可以指定到 mapper 接口类 8 但是需要满足 : 9 1.sql 映射文件必须和 mapper 借口放在同个目录下 10 2.名称必须一致 11 --> 12 <!-- <mapper class="com.richer.mybatis.mapper.UserMapper"/> --> 13 <!-- 14 与 class 方式前提条件相同, mapper 动态代理方式使用 15 --> 16 <package name="com.richer.mybatis.mapper"/> 17 </mappers>
原文地址:https://www.cnblogs.com/RicherQ/p/9106374.html