Mybatis文章笔记整合【狂神说】
Mybatis官方文档
GitHub
[toc]
一、搭建Mybatis项目 思路流程:搭建环境–>导入Mybatis—>编写代码—>测试
1.1 搭建环境 搭建实验数据库 1 2 3 4 5 6 7 8 9 10 11 12 13 14 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' );
idea创建项目
New Project ——Maven
删除src目录(这样就可以把它当成一个父工程)
导入依赖(GitHub上找) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 <?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 > org.example</groupId > <artifactId > mybatisstudy</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 > <build > <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 > </build > </project >
1.2 创建一个模块 1、右击父项目:New——Module
编写MyBatis核心配置文件(查看帮助文档) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <?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 ="admin" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="com/mc/dao/userMapper.xml" /> </mappers > </configuration >
连接mysql数据库
l连接成功后寻找已经建立的数据库
编写MyBatis工具类(查看帮助文档) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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(); } } public static SqlSession getSession () { return sqlSessionFactory.openSession(); } }
1.3 编写代码
实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 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/Mapper接口类
1 2 3 4 5 6 7 8 import com.mc.pojo.User;import java.util.List;public interface UserDao { List<User> getUserList () ; }
编写Dao/Mapper配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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.mc.dao.UserDao" > <select id ="getUserList" resultType ="com.mc.pojo.User" > select * from user </select > </mapper >
1.4 测试代码
编写junit测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.mc.dao;import com.mc.pojo.User;import com.mc.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 = MybatisUtils.getSession(); UserDao userDao = sqlSession.getMapper(UserDao.class); List<User> userList = userDao.getUserList(); for (User user:userList){ System.out.println(user); } sqlSession.close(); } }
1.5 总结
创建数据库
pom.xml中导入MyBatis相关jar包(org.mybatis、mysql、junit)(注意配置build中的resource)
resources/mybatis-config.xml中编写mybatis核心配置文件
在java/…/utils/MyBatisUtils编写mybatis工具类(三步固定流程)
在java/…/pojo/User中创建实体类
在java/…/dao/UserDao编写Dao/Mapper接口类
在java/…/dao/UserMapper.xml中编写配置文件(注意namespace、id连接第6步包名要一致)
编写测试类
二、CURD增删改查 UserMapper.xml配置文件详解
namespace 配置文件中namespace中的名称为对应Mapper接口或者Dao接口的完整包名,必须一致!
2.1 select
需求:根据id查询用户
1、在UserMapper中添加对应方法
1 2 3 4 5 6 public interface UserMapper { List<User> selectUser () ; User selectUserById (int id) ; }
2、在UserMapper.xml中添加Select语句
1 2 3 <select id ="selectUserById" resultType ="com.kuang.pojo.User" > select * from user where id = #{id}</select >
3、测试类中测试
1 2 3 4 5 6 7 @Test public void tsetSelectUserById () { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1 ); System.out.println(user); session.close();
2.2 insert 我们一般使用insert标签进行插入操作,它的配置和select标签差不多!
需求:给数据库增加一个用户
1、在UserMapper接口中添加对应的方法
1 2 int addUser (User user) ;
2、在UserMapper.xml中添加insert语句(#{}中的参数需要与实例中的参数一一对应)
1 2 3 <insert id ="addUser" parameterType ="com.kuang.pojo.User" > insert into user (id,name,pwd) values (#{id},#{name},#{pwd})</insert >
3、测试
1 2 3 4 5 6 7 8 9 10 @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(); }
注意点:增、删、改操作需要提交事务!
2.3 update 我们一般使用update标签进行更新操作,它的配置和select标签差不多!
需求:修改用户的信息
1、同理,编写接口方法
1 2 int updateUser (User user) ;
2、编写对应的配置文件SQL
1 2 3 <update id ="updateUser" parameterType ="com.kuang.pojo.User" > update user set name=#{name},pwd=#{pwd} where id = #{id}</update >
3、测试
1 2 3 4 5 6 7 8 9 10 11 @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(); }
2.4 delete 我们一般使用delete标签进行删除操作,它的配置和select标签差不多!
需求:根据id删除一个用户
1、同理,编写接口方法
1 2 int deleteUser (int id) ;
2、编写对应的配置文件SQL
1 2 3 <delete id ="deleteUser" parameterType ="int" > delete from user where id = #{id}</delete >
3、测试
1 2 3 4 5 6 7 8 9 @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(); }
另一种方式:使用map 1、在接口方法中,参数直接传递Map;
1 User selectUserByNP2(Map <String ,Object > map );
2、编写sql语句的时候,需要传递参数类型,参数类型为map
1 2 3 <select id ="selectUserByNP2" parameterType ="map" resultType ="com.kuang.pojo.User" > select * from user where name = #{username} and pwd = #{pwd}</select >
3、在使用方法的时候,Map的 key 为 sql中取的值即可,没有顺序要求!
1 2 3 4 Map<String, Object> map = new HashMap<String, Object>(); map.put("username" ,"小明" ); map.put("pwd" ,"123456" ); User user = mapper.selectUserByNP2(map);
总结:如果参数过多,我们可以考虑直接使用Map实现,如果参数比较少,直接传递参数即可
模糊查询like语句 第1种:在Java代码中添加sql通配符。
1 2 3 4 5 6 7 8 9 string wildcardname = “%smi%”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like #{value} </select>
第2种:在sql语句中拼接通配符,会引起sql注入
1 2 3 4 5 6 string wildcardname = “smi”; list<name> names = mapper.selectlike(wildcardname); <select id=”selectlike”> select * from foo where bar like "%"#{value}"%" </select>
2.5 小结:
所有的增删改操作都需要提交事务!
接口所有的普通参数,尽量都写上@Param参数,尤其是多个参数时,必须写上!
有时候根据业务的需求,可以考虑使用map传递参数!
为了规范操作,在SQL的配置文件中,我们尽量将Parameter参数和resultType都写上!
三、配置解析
mybatis-config.xml 系统核心配置文件
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。
能配置的内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处理器) objectFactory(对象工厂) plugins(插件) environments(环境配置) environment(环境变量) transactionManager(事务管理器) dataSource(数据源) databaseIdProvider(数据库厂商标识) mappers(映射器)
我们可以阅读 mybatis-config.xml 上面的dtd的头文件
3.1 environments元素 1 2 3 4 5 6 7 8 9 10 11 12 13 <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" > <property name ="..." value ="..." /> </transactionManager > <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 >
3.2 mappers元素 mappers
映射器 : 定义映射SQL语句文件
既然 MyBatis 的行为其他元素已经配置完了,我们现在就要定义 SQL 映射语句了。但是首先我们需要告诉 MyBatis 到哪里去找到这些语句。Java 在自动查找这方面没有提供一个很好的方法,所以最佳的方式是告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:///
的 URL),或类名和包名等。映射器是MyBatis中最核心的组件之一,在MyBatis 3之前,只支持xml映射器,即:所有的SQL语句都必须在xml文件中配置。而从MyBatis 3开始,还支持接口映射器,这种映射器方式允许以Java代码的方式注解定义SQL语句,非常简洁。
引入资源方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <mappers > <mapper resource ="org/mybatis/builder/PostMapper.xml" /> </mappers > <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文件
1 2 3 4 5 6 7 <?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的命名必须跟某个接口同名
接口中的方法与映射文件中sql语句id应该一一对应
namespace和子元素的id联合保证唯一 , 区别不同的mapper
绑定DAO接口
namespace命名规则 : 包名+类名
MyBatis 的真正强大在于它的映射语句,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 为聚焦于 SQL 而构建,以尽可能地为你减少麻烦。
3.3 Properties优化 数据库这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。具体的官方文档
方法一:直接在核心配置文件中引入
1 2 3 4 5 6 7 8 9 10 11 <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 ="admin" /> </dataSource > </environment > </environments >
方法二:我们来优化我们的配置文件
第一步 : 在资源目录下新建一个db.properties
1 2 3 4 driver=com.mysql.jdbc.Driver url=jdbc:mysql: username=root password=123456
第二步 : 将文件导入properties 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <configuration > <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 >
可以直接引入外部文件
可以在其中添加一些配置属性
如果两个文件中有同一字段,优先使用外部配置文件
3.4 typeAliases优化 类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
1 2 3 4 5 <typeAliases > <typeAlias type ="com.kuang.pojo.User" alias ="User" /> </typeAliases >
当这样配置时,User
可以用在任何使用com.kuang.pojo.User
的地方。
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
1 2 3 <typeAliases > <package name ="com.kuang.pojo" /> </typeAliases >
每一个在包 com.kuang.pojo
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。
但是可以使用注解diy名称
若有注解,则别名为其注解值。见下面的例子:
1 2 3 4 @Alias("user") public class User { ... }
3.5 其他配置浏览 设置
类型处理器 (typeHandlers)
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。【了解即可】
对象工厂 (objectFactory)
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。
默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过有参构造方法来实例化。
如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。【了解即可】
生命周期和作用域 作用域(Scope)和生命周期
理解我们目前已经讨论过的不同作用域和生命周期类是至关重要的,因为错误的使用会导致非常严重的并发问题 。
我们可以先画一个流程图,分析一下Mybatis的执行过程
作用域理解
SqlSessionFactoryBuilder 的作用在于创建 SqlSessionFactory,创建成功后,SqlSessionFactoryBuilder 就失去了作用,所以它只能存在于创建 SqlSessionFactory 的方法中,而不要让其长期存在。因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域 (也就是局部方法变量)。
SqlSessionFactory 可以被认为是一个数据库连接池,它的作用是创建 SqlSession 接口对象。因为 MyBatis 的本质就是 Java 对数据库的操作,所以 SqlSessionFactory 的生命周期存在于整个 MyBatis 的应用之中,所以一旦创建了 SqlSessionFactory,就要长期保存它,直至不再使用 MyBatis 应用,所以可以认为 SqlSessionFactory 的生命周期就等同于 MyBatis 的应用周期。
由于 SqlSessionFactory 是一个对数据库的连接池,所以它占据着数据库的连接资源。如果创建多个 SqlSessionFactory,那么就存在多个数据库连接池,这样不利于对数据库资源的控制,也会导致数据库连接资源被消耗光,出现系统宕机等情况,所以尽量避免发生这样的情况。
因此在一般的应用中我们往往希望 SqlSessionFactory 作为一个单例,让它在应用中被共享。所以说 SqlSessionFactory 的最佳作用域是应用作用域。
如果说 SqlSessionFactory 相当于数据库连接池,那么 SqlSession 就相当于一个数据库连接(Connection 对象),你可以在一个事务里面执行多条 SQL,然后通过它的 commit、rollback 等方法,提交或者回滚事务。所以它应该存活在一个业务请求中,处理完整个请求后,应该关闭这条连接,让它归还给 SqlSessionFactory,否则数据库资源就很快被耗费精光,系统就会瘫痪,所以用 try…catch…finally… 语句来保证其正确关闭。
所以 SqlSession 的最佳的作用域是请求或方法作用域。
四、ResultMap解决属性名和字段名不一致 4.1 查询为null问题 要解决的问题:属性名和字段名不一致
环境:新建一个项目,将之前的项目拷贝过来
1、查看之前的数据库的字段名
2、Java中的实体类设计
1 2 3 4 5 6 7 8 9 10 public class User { private int id; private String name; private String password; }
3、接口
1 2 User selectUserById (int id) ;
4、mapper映射文件
1 2 3 <select id ="selectUserById" resultType ="user" > select * from user where id = #{id}</select >
5、测试
1 2 3 4 5 6 7 8 @Test public void testSelectUserById () { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1 ); System.out.println(user); session.close(); }
结果:
User{id=1, name=’狂神’, password=’null’}
查询出来发现 password 为空 . 说明出现了问题!
分析:
select * from user where id = #{id} 可以看做
select id,name,pwd from user where id = #{id}
mybatis会根据这些查询的列名(会将列名转化为小写,数据库不区分大小写) , 去对应的实体类中查找相应列名的set方法设值 , 由于找不到setPwd() , 所以password返回null ; 【自动映射】
解决方案
方案一:为列名指定别名 , 别名和java实体类的属性名一致 .
1 2 3 <select id ="selectUserById" resultType ="User" > select id , name , pwd as password from user where id = #{id}</select >
方案二:使用结果集映射->ResultMap 【推荐】
1 2 3 4 5 6 7 8 9 10 11 <resultMap id ="UserMap" type ="User" > <id column ="id" property ="id" /> <result column ="name" property ="name" /> <result column ="pwd" property ="password" /> </resultMap > <select id ="selectUserById" resultMap ="UserMap" > select id , name , pwd from user where id = #{id}</select >
4.2 ResultMap 自动映射
resultMap
元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets
数据提取代码中解放出来。
实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap
能够代替实现同等功能的长达数千行的代码。
ResultMap 的设计思想是,对于简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述它们的关系就行了。
你已经见过简单映射语句的示例了,但并没有显式指定 resultMap
。比如:
1 2 3 4 5 <select id ="selectUserById" resultType ="map" > select id , name , pwd from user where id = #{id}</select >
上述语句只是简单地将所有的列映射到 HashMap
的键上,这由 resultType
属性指定。虽然在大部分情况下都够用,但是 HashMap 不是一个很好的模型。你的程序更可能会使用 JavaBean 或 POJO(Plain Old Java Objects,普通老式 Java 对象)作为模型。
ResultMap
最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。
手动映射
1、返回值类型为resultMap
1 2 3 <select id ="selectUserById" resultMap ="UserMap" > select id , name , pwd from user where id = #{id}</select >
2、编写resultMap,实现手动映射!
1 2 3 4 5 6 7 <resultMap id ="UserMap" type ="User" > <id column ="id" property ="id" /> <result column ="name" property ="name" /> <result column ="pwd" property ="password" /> </resultMap >
如果世界总是这么简单就好了。但是肯定不是的,数据库中,存在一对多,多对一的情况,我们之后会使用到一些高级的结果集映射,association,collection这些,我们将在之后讲解,今天你们需要把这些知识都消化掉才是最重要的!理解结果集映射的这个概念!
五、 日志 5.1 日志工厂 思考:我们在测试SQL的时候,要是能够在控制台输出 SQL 的话,是不是就能够有更快的排错效率?
如果一个 数据库相关的操作出现了问题,我们可以根据输出的SQL语句快速排查问题。
对于以往的开发过程,我们会经常使用到debug模式来调节,跟踪我们的代码执行过程。但是现在使用Mybatis是基于接口,配置文件的源代码执行过程。因此,我们必须选择日志工具来作为我们开发,调节程序的工具。
Mybatis内置的日志工厂提供日志功能,具体的日志实现有以下几种工具:
SLF4J
Apache Commons Logging
Log4j 2
Log4j
JDK logging
具体选择哪个日志实现工具由MyBatis的内置日志工厂确定。它会使用最先找到的(按上文列举的顺序查找)。如果一个都未找到,日志功能就会被禁用。
标准日志实现
指定 MyBatis 应该使用哪个日志记录实现。如果此设置不存在,则会自动发现日志记录实现。
1 2 3 <settings > <setting name ="logImpl" value ="STDOUT_LOGGING" /> </settings >
测试,可以看到控制台有大量的输出!我们可以通过这些输出来判断程序到底哪里出了Bug
5.2 Log4j 简介:
Log4j是Apache的一个开源项目
通过使用Log4j,我们可以控制日志信息输送的目的地:控制台,文本,GUI组件….
我们也可以控制每一条日志的输出格式;
通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。最令人感兴趣的就是,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
使用步骤:
1、导入log4j的包
1 2 3 4 5 <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency >
2、配置文件编写(resources / log4j.properties)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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
3、setting设置日志实现
1 2 3 <settings > <setting name ="logImpl" value ="LOG4J" /> </settings >
4、在程序中使用Log4j进行输出!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static Logger logger = Logger.getLogger(MyTest.class);@Test public void selectUser () { logger.info("info:进入selectUser方法" ); logger.debug("debug:进入selectUser方法" ); logger.error("error: 进入selectUser方法" ); SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.selectUser(); for (User user: users){ System.out.println(user); } session.close(); }
5、测试,看控制台输出!
使用Log4j 输出日志
可以看到还生成了一个日志的文件 【需要修改file的日志级别】
六、分页 6.1 limit实现分页 思考:为什么需要分页?
在学习mybatis等持久层框架的时候,会经常对数据进行增删改查操作,使用最多的是对数据库进行查询操作,如果查询大量数据的时候,我们往往使用分页进行查询,也就是每次处理小部分数据,这样对数据库压力就在可控范围内。
使用Limit实现分页
1 2 3 4 5 6 7 8 9 10 11 12 SELECT * FROM table LIMIT stratIndex,pageSize SELECT * FROM table LIMIT 5 ,10 ; SELECT * FROM table LIMIT 95 ,-1 ; SELECT * FROM table LIMIT 5 ;
步骤:
1、修改Mapper文件
1 2 3 <select id ="selectUser" parameterType ="map" resultType ="user" > select * from user limit #{startIndex},#{pageSize}</select >
2、Mapper接口,参数为map
1 2 List<User> selectUser (Map<String,Integer> map) ;
3、在测试类中传入参数测试
推断:起始位置 = (当前页面 - 1 ) * 页面大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void testSelectUser () { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); int currentPage = 1 ; int pageSize = 2 ; Map<String,Integer> map = new HashMap<String,Integer>(); map.put("startIndex" ,(currentPage-1 )*pageSize); map.put("pageSize" ,pageSize); List<User> users = mapper.selectUser(map); for (User user: users){ System.out.println(user); } session.close(); }
6.2 RowBounds分页 我们除了使用Limit在SQL层面实现分页,也可以使用RowBounds在Java代码层面实现分页,当然此种方式作为了解即可。我们来看下如何实现的!
步骤:
1、mapper接口
1 2 List<User> getUserByRowBounds () ;
2、mapper文件
1 2 3 <select id="getUserByRowBounds" resultType="user">select * from user </select >
3、测试类
在这里,我们需要使用RowBounds类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Testpublic void testUserByRowBounds() { SqlSession session = MybatisUtils.getSession(); int currentPage = 2 ; //第几页 int pageSize = 2 ; //每页显示几个 RowBounds rowBounds = new RowBounds((currentPage-1 )*pageSize,pageSize); //通过session .**方法进行传递rowBounds,[此种方式现在已经不推荐使用了] List<User > users = session .selectList("com.kuang.mapper.UserMapper.getUserByRowBounds", null , rowBounds); for (User user : users){ System .out .println(user ); } session .close (); }
6.3 PageHelper分页插件
了解即可,可以自己尝试使用
官方文档:https://pagehelper.github.io/
七、使用注解开发
注意: 利用注解开发就不需要mapper.xml映射文件了 .
7.1 注解使用流程 1、我们在我们的接口中添加注解
1 2 3 @Select("select id,name,pwd password from user") public List<User> getAllUser () ;
2、在mybatis的核心配置文件中注入
1 2 3 4 <mappers > <mapper class ="com.kuang.mapper.UserMapper" /> </mappers >
3、我们去进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void testGetAllUser () { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> users = mapper.getAllUser(); for (User user : users){ System.out.println(user); } session.close(); }
4、利用Debug查看本质
5、本质上利用了jvm的动态代理机制
6、Mybatis详细的执行流程
7.2 注解增删改 改造MybatisUtils工具类的getSession( ) 方法,重载实现。
1 2 3 4 5 6 7 8 public static SqlSession getSession () { return getSession(true ); } public static SqlSession getSession (boolean flag) {java return sqlSessionFactory.openSession(flag); }
【注意】确保实体类和数据库字段对应
查询:
1、编写接口方法注解
1 2 3 4 @Select("select * from user where id = #{id}") User selectUserById (@Param("id") int id) ;
2、测试
1 2 3 4 5 6 7 8 9 10 @Test public void testSelectUserById () { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = mapper.selectUserById(1 ); System.out.println(user); session.close(); }
新增:
1、编写接口方法注解
1 2 3 @Insert("insert into user (id,name,pwd) values (#{id},#{name},#{pwd})") int addUser (User user) ;
2、测试
1 2 3 4 5 6 7 8 9 10 @Test public void testAddUser () { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(6 , "秦疆" , "123456" ); mapper.addUser(user); session.close(); }
修改:
1、编写接口方法注解
1 2 3 @Update("update user set name=#{name},pwd=#{pwd} where id = #{id}") int updateUser (User user) ;
2、测试
1 2 3 4 5 6 7 8 9 10 @Test public void testUpdateUser () { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(6 , "秦疆" , "zxcvbn" ); mapper.updateUser(user); session.close(); }
删除:
1、编写接口方法注解
1 2 3 @Delete("delete from user where id = #{id}") int deleteUser (@Param("id") int id) ;
2、测试
1 2 3 4 5 6 7 8 9 @Test public void testDeleteUser () { SqlSession session = MybatisUtils.getSession(); UserMapper mapper = session.getMapper(UserMapper.class); mapper.deleteUser(6 ); session.close(); }
【注意点:增删改一定记得对事务的处理】
7.3 @param()注解 @Param注解用于给方法参数起一个名字。以下是总结的使用原则:
在方法只接受一个参数的情况下,可以不使用@Param。
在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
如果参数是 JavaBean , 则不能使用@Param。
不使用@Param注解时,参数只能有一个,并且是Javabean。
7.4 #与$的区别
使用注解和配置文件协同开发,才是MyBatis的最佳实践!
延申
${}方式无法防止sql注入
$一般用于传入数据库对象比如数据库表名
尽量使用#{}
八、多对一和一对多 8.1 多对一的处理 多对一的理解:
多个学生对应一个老师
如果对于学生这边,就是一个多对一的现象,即从学生这边关联一个老师!
8.1.1 数据库设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 USE `mybatis` ;CREATE TABLE `teacher` (`id` INT (10 ) NOT NULL ,`name` VARCHAR (30 ) DEFAULT NULL , PRIMARY KEY (`id` ) ) ENGINE =INNODB DEFAULT CHARSET =utf8INSERT INTO teacher(`id` , `name` ) VALUES (1 , '秦老师' );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 =utf8INSERT INTO `student` (`id` , `name` , `tid` ) VALUES ('1' , '小明' , '1' );INSERT INTO `student` (`id` , `name` , `tid` ) VALUES ('2' , '小红' , '1' );INSERT INTO `student` (`id` , `name` , `tid` ) VALUES ('3' , '小张' , '1' );INSERT INTO `student` (`id` , `name` , `tid` ) VALUES ('4' , '小李' , '1' );INSERT INTO `student` (`id` , `name` , `tid` ) VALUES ('5' , '小王' , '1' );
8.1.2 搭建测试环境 1、IDEA安装Lombok插件
2、引入Maven依赖
1 2 3 4 5 <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > </dependency >
3、在代码中增加注解
1 2 3 4 5 6 7 8 9 10 11 12 @Data public class Teacher { private int id; private String name; }@Data public class Student { private int id; private String name; private Teacher teacher; }
4、编写实体类对应的Mapper接口 【两个】
1 2 3 4 public interface StudentMapper { }public interface TeacherMapper { }
5、编写Mapper接口对应的 mapper.xml配置文件 【两个】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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.StudentMapper" > </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.TeacherMapper" > </mapper >
在idea中建立的文件如下:
测试
1 2 3 4 5 6 7 8 public static void main (String[] args) { SqlSession sqlSession = MybatisUtils.getSession(); TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher(1 ); System.out.println(teacher); sqlSession.close(); }
8.1.3 按照查询嵌套处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <?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.mc.dao.StudentMapper" > <select id ="getStudent" resultMap ="StudentTeacher" > select * from student </select > <resultMap id ="StudentTeacher" type ="com.mc.pojo.Student" > <result property ="id" column ="id" /> <result property ="name" column ="name" /> <association property ="teacher" column ="tid" javaType ="com.mc.pojo.Teacher" select ="getTeacher" /> </resultMap > <select id ="getTeacher" resultType ="com.mc.pojo.Teacher" > select * from teacher where id = #{id} </select > </mapper >
测试
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testStudent () { SqlSession sqlSession = MybatisUtils.getSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> student = mapper.getStudent(); for (Student student1 : student) { System.out.println(student1); } sqlSession.close(); }
8.1.4 按照结果嵌套处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <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.mc.pojo.Student" > <id property ="id" column ="sid" /> <result property ="name" column ="sname" /> <association property ="teacher" javaType ="com.mc.pojo.Teacher" > <result property ="name" column ="tname" /> </association > </resultMap >
测试
1 2 3 4 5 6 7 8 9 10 11 @Test public void testStudent2 () { SqlSession sqlSession = MybatisUtils.getSession(); StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); List<Student> student = mapper.getStudent2(); for (Student student1 : student) { System.out.println(student1); } sqlSession.close(); }
8.2 一对多的处理 一对多的理解:
一个老师拥有多个学生
如果对于老师这边,就是一个一对多的现象,即从一个老师下面拥有一群学生(集合)!
实体类编写
1 2 3 4 5 6 7 8 9 10 11 12 13 @Data public class Student { private int id; private String name; private int tid; }@Data public class Teacher { private int id; private String name; private List<Student> students; }
….. 和之前一样,搭建测试的环境!
8.2.1 按结果嵌套处理 1、TeacherMapper接口编写方法
1 2 public Teacher getTeacher (int id) ;
2、编写接口对应的Mapper配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 <mapper namespace ="com.mc.mapper.TeacherMapper" > <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.mc.pojo.Teacher" > <result property ="name" column ="tname" /> <collection property ="students" ofType ="com.mc.pojo.Student" > <result property ="id" column ="sid" /> <result property ="name" column ="sname" /> <result property ="tid" column ="tid" /> </collection > </resultMap > </mapper >
3、将Mapper文件注册到MyBatis-config文件中
1 2 3 <mappers > <mapper resource ="mapper/TeacherMapper.xml" /> </mappers >
4、测试
1 2 3 4 5 6 7 8 @Test public void testGetTeacher () { SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher(1 ); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); }
8.2.2 按查询嵌套处理 1、TeacherMapper接口编写方法
1 public Teacher getTeacher2 (int id) ;
2、编写接口对应的Mapper配置文件
1 2 3 4 5 6 7 8 9 10 <select id ="getTeacher2" resultMap ="TeacherStudent2" > select * from teacher where id = #{id}</select > <resultMap id ="TeacherStudent2" type ="com.mc.pojo.Teacher" > <collection property ="students" javaType ="ArrayList" ofType ="com.mc.pojo.Student" column ="id" select ="getStudentByTeacherId" /> </resultMap > <select id ="getStudentByTeacherId" resultType ="Student" > select * from student where tid = #{id}</select >
3、将Mapper文件注册到MyBatis-config文件中
4、测试
1 2 3 4 5 6 7 8 @Test public void testGetTeacher2 () { SqlSession session = MybatisUtils.getSession(); TeacherMapper mapper = session.getMapper(TeacherMapper.class); Teacher teacher = mapper.getTeacher2(1 ); System.out.println(teacher.getName()); System.out.println(teacher.getStudents()); }
8.3 小结 1、关联-association
2、集合-collection
3、所以association是用于一对一和多对一,而collection是用于一对多的关系
4、JavaType和ofType都是用来指定对象类型的
JavaType是用来指定pojo中属性的类型
ofType指定的是映射到list集合属性中pojo的类型。
注意说明:
1、保证SQL的可读性,尽量通俗易懂
2、根据实际要求,尽量编写性能更高的SQL语句
3、注意属性名和字段不一致的问题
4、注意一对多和多对一 中:字段和属性对应的问题
5、尽量使用Log4j,通过日志来查看自己的错误
面试高频:Mysql引擎、InnoDB底层原理、索引、索引优化
九、动态SQL 什么是动态SQL:动态SQL指的是根据不同的查询条件 , 生成不同的Sql语句.
1 2 3 4 5 6 7 8 9 10 11 官网描述: MyBatis 的强大特性之一便是它的动态 SQL 。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。 虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。 动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。 - if - choose (when , otherwise) - trim (where , set ) - foreach
我们之前写的 SQL 语句都比较简单,如果有比较复杂的业务,我们需要写复杂的 SQL 语句,往往需要拼接,而拼接 SQL ,稍微不注意,由于引号,空格等缺失可能都会导致错误。
那么怎么去解决这个问题呢?这就要使用 mybatis 动态SQL,通过 if, choose, when, otherwise, trim, where, set, foreach等标签,可组合成非常灵活的SQL语句,从而在提高 SQL 语句的准确性的同时,也大大提高了开发人员的效率。
9.1 搭建环境 新建一个数据库表:blog
字段:id,title,author,create_time,views
1 2 3 4 5 6 7 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
1、创建Mybatis基础工程
2、IDutil工具类
1 2 3 4 5 6 7 public class IDUtil { public static String genId () { return UUID.randomUUID().toString().replaceAll("-" ,"" ); } }
3、实体类编写 【注意set方法作用】
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.mc.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; }
4、编写Mapper接口及xml文件
1 2 3 4 5 6 7 8 9 public interface BlogMapper { } <?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.BlogMapper" > </mapper>
5、mybatis核心配置文件,下划线驼峰自动转换
1 2 3 4 5 6 7 8 <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> <setting name ="logImpl" value ="STDOUT_LOGGING" /> </settings > <mappers > <mapper resource ="mapper/BlogMapper.xml" /> </mappers >
6、插入初始数据
编写接口
1 2 int addBlog (Blog blog) ;
sql配置文件
1 2 3 4 <insert id ="addBlog" parameterType ="blog" > insert into blog (id, title, author, create_time, views) values (#{id},#{title},#{author},#{createTime},#{views});</insert >
初始化博客方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import com.mc.dao.BlogMapper;import com.mc.pojo.Blog;import com.mc.utils.IDutils;import com.mc.utils.MybatisUtils;import org.apache.ibatis.session.SqlSession;import org.junit.Test;import java.util.Date;public class Mytast { @Test public void addInitBlog () { SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); Blog blog = new Blog(); blog.setId(IDutils.getId()); blog.setTitle("Mybatis如此简单" ); blog.setAuthor("狂神说" ); blog.setCreateTime(new Date()); blog.setViews(9999 ); mapper.addBlog(blog); blog.setId(IDutils.getId()); blog.setTitle("Java如此简单" ); mapper.addBlog(blog); blog.setId(IDutils.getId()); blog.setTitle("Spring如此简单" ); mapper.addBlog(blog); blog.setId(IDutils.getId()); blog.setTitle("微服务如此简单" ); mapper.addBlog(blog); session.commit(); session.close(); } }
初始化数据完毕!
【解决Java驼峰命名和数据库字段名冲突】
【自动生成ID】 1 2 return UUID.randomUUID().toString().replaceAll("-" ,"" );
9.2 f 语句 需求:根据作者名字和博客名字来查询博客!如果作者名字为空,那么只根据博客名字查询,反之,则根据作者名来查询
1、编写接口类
1 2 List<Blog> queryBlogIf (Map map) ;
2、编写SQL语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <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 >
3、测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void testQueryBlogIf () { SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, String> map = new HashMap<String, String>(); map.put("title" ,"Mybatis如此简单" ); map.put("author" ,"狂神说" ); List<Blog> blogs = mapper.queryBlogIf(map); System.out.println(blogs); java session.close(); }
这样写我们可以看到,如果 author 等于 null,那么查询语句为 select * from user where title=#{title},但是如果title为空呢?那么查询语句为 select * from user where and author=#{author},这是错误的 SQL 语句,如何解决呢?请看下面的 where 语句!
9.3 Where 修改上面的SQL语句;
1 2 3 4 5 6 7 8 9 10 11 <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 开头的,则它会剔除掉。
9.3 Set 同理,上面的对于查询 SQL 语句包含 where 关键字,如果在进行更新操作的时候,含有 set 关键词,我们怎么处理呢?
1、编写接口方法
1 int updateBlog (Map map) ;
2、sql配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 <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 >
3、测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testUpdateBlog () { SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, String> map = new HashMap<String, String>(); map.put("title" ,"动态SQL" ); map.put("author" ,"秦疆" ); map.put("id" ,"9d6a763f5e1347cebda43e2a32687a77" ); mapper.updateBlog(map); session.close(); }
9.4 choose语句 有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
1、编写接口方法
1 List<Blog> queryBlogChoose (Map map) ;
2、sql配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <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 >
3、测试类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testQueryBlogChoose () { SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap<String, Object> map = new HashMap<String, Object>(); map.put("title" ,"Java如此简单" ); map.put("author" ,"狂神说" ); map.put("views" ,9999 ); List<Blog> blogs = mapper.queryBlogChoose(map); System.out.println(blogs); session.close(); }
9.5 SQL片段 有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
提取SQL片段:
1 2 3 4 5 6 7 8 <sql id ="if-title-author" > <if test ="title != null" > title = #{title} </if > <if test ="author != null" > and author = #{author} </if > </sql >
引用SQL片段:
1 2 3 4 5 6 7 8 <select id ="queryBlogIf" parameterType ="map" resultType ="blog" > select * from blog <where > <include refid ="if-title-author" > </include > </where > </select >
注意:
①、最好基于 单表来定义 sql 片段,提高片段的可重用性
②、在 sql 片段中不要包括 where
9.6 Foreach 将数据库中前三个数据的id修改为1,2,3;
需求:我们需要查询 blog 表中 id 分别为1,2,3的博客信息
1、编写接口
1 List<Blog> queryBlogForeach (Map map) ;
2、编写SQL语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="queryBlogForeach" parameterType ="map" resultType ="blog" > select * from blog <where > <foreach collection ="ids" item ="id" open ="and (" close =")" separator ="or" > id=#{id} </foreach > </where > </select >
3、测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void testQueryBlogForeach () { SqlSession session = MybatisUtils.getSession(); BlogMapper mapper = session.getMapper(BlogMapper.class); HashMap map = new HashMap(); List<Integer> ids = new ArrayList<Integer>(); ids.add(1 ); ids.add(2 ); ids.add(3 ); map.put("ids" ,ids); List<Blog> blogs = mapper.queryBlogForeach(map); System.out.println(blogs); session.close(); }
小结:其实动态 sql 语句的编写往往就是一个拼接的问题,为了保证拼接准确,我们最好首先要写原生的 sql 语句出来,然后在通过 mybatis 动态sql 对照着改,防止出错。多在实践中使用才是熟练掌握它的技巧。
十、缓存 10.1 简介 1、什么是缓存 [ Cache ]?
存在内存中的临时数据。
将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(关系型数据库数据文件)查询,从缓存中查询,从而提高查询效率,解决了高并发系统的性能问题。
2、为什么使用缓存?
减少和数据库的交互次数,减少系统开销,提高系统效率。
3、什么样的数据能使用缓存?
10.2 Mybatis缓存
10.3 一级缓存 一级缓存也叫本地缓存:
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;
测试
1、在mybatis中加入日志,方便测试结果
2、编写接口方法
1 2 User queryUserById (@Param("id") int id) ;
3、接口对应的Mapper文件
1 2 3 <select id ="queryUserById" resultType ="user" > select * from user where id = #{id}</select >
4、测试
1 2 3 4 5 6 7 8 9 10 11 12 13 @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不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @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相同,查询条件不同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @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相同,两次查询之间执行了增删改操作!
增加方法
1 2 int updateUser (Map map) ;
编写SQL
1 2 3 <update id ="updateUser" parameterType ="map" > update user set name = #{name} where id = #{id}</update >
测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @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相同,手动清除一级缓存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @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
10.4 二级缓存
使用步骤
1、开启全局缓存 【mybatis-config.xml】
1 <setting name ="cacheEnabled" value ="true" />
2、去每个mapper.xml中配置使用二级缓存,这个配置非常简单;【xxxMapper.xml】
1 2 3 4 5 6 7 8 9 <cache /> 官方示例=====>查看官方文档<cache eviction ="FIFO" flushInterval ="60000" size ="512" readOnly ="true" /> 这个更高级的配置创建了一个 FIFO 缓存,每隔 60 秒刷新,最多可以存储结果对象或列表的 512 个引用,而且返回的对象被认为是只读的,因此对它们进行修改可能会在不同线程中的调用者产生冲突。
3、代码测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @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(); }
10.5 结论
只要开启了二级缓存,我们在同一个Mapper中的查询,可以在二级缓存中拿到数据
查出的数据都会被默认先放在一级缓存中
只有会话提交或者关闭以后,一级缓存中的数据才会转到二级缓存中
缓存原理图
10.6 第三方缓存EhCache
第三方缓存实现–EhCache: 查看百度百科
Ehcache是一种广泛使用的java分布式缓存,用于通用缓存;
要在应用程序中使用Ehcache,需要引入依赖的jar包
1 2 3 4 5 6 <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.1.0</version > </dependency >
在mapper.xml中使用对应的缓存即可
1 2 3 <mapper namespace = “org.acme.FooMapper” > <cache type = “org.mybatis.caches.ehcache.EhcacheCache” /> </mapper >
编写ehcache.xml文件,如果在加载时未找到/ehcache.xml资源或出现问题,则将使用默认配置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 <?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 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" /> </ehcache >