学习目标
- 动态代理Mapper实现类
- mybatis-config.xml详解
- Mapper XML文件详解
- 动态sql
- 缓存
- 高级查询
- 延迟加载
- 如果sql语句中出现’<’的解决方案
7.动态代理Mapper实现类
7.1.思考CRUD中的问题
- 1、接口->实现类->mapper.xml
- 2、实现类中,使用mybatis的方式非常类似
- 3、xml中的sql statement 硬编码到java代码中。
思考:能否只写接口,不写实现类。只编写接口和Mapper.xml即可?
因为在dao(mapper)的实现类中对sqlsession的使用方式很类似。因此mybatis提供了接口的动态代理。
7.2.使用动态代理改造CRUD
1、创建UserMapper接口(对应原UserDao)
package com.zpc.mybatis.dao;
import com.zpc.mybatis.pojo.User;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface UserMapper {
/**
* 登录(直接使用注解指定传入参数名称)
* @param userName
* @param password
* @return
*/
public User login(@Param("userName") String userName, @Param("password") String password);
/**
* 根据表名查询用户信息(直接使用注解指定传入参数名称)
* @param tableName
* @return
*/
public List<User> queryUserByTableName(@Param("tableName") String tableName);
/**
* 根据Id查询用户信息
* @param id
* @return
*/
public User queryUserById(String id);
/**
* 查询所有用户信息
* @return
*/
public List<User> queryUserAll();
/**
* 新增用户信息
* @param user
*/
public void insertUser(User user);
/**
* 根据id更新用户信息
* @param user
*/
public void updateUser(User user);
/**
* 根据id删除用户信息
* @param id
*/
public void deleteUserById(String id);
}
2、创建UserMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper:根标签,namespace:命名空间,随便写,一般保证命名空间唯一 ,为了使用接口动态代理,这里必须是接口的全路径名-->
<mapper namespace="com.zpc.mybatis.dao.UserMapper">
<!--
1.#{},预编译的方式preparedstatement,使用占位符替换,防止sql注入,一个参数的时候,任意参数名可以接收
2.${},普通的Statement,字符串直接拼接,不可以防止sql注入,一个参数的时候,必须使用${value}接收参数
-->
<select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
select * from ${tableName}
</select>
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{userName} and password = #{password}
</select>
<!-- statement,内容:sql语句。
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
resultType:sql语句查询结果集的封装类型,使用动态代理之后和方法的返回类型一致;resultMap:二选一
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
-->
<select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where id = #{id}
</select>
<select id="queryUserAll" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user
</select>
<!-- 新增的Statement
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
useGeneratedKeys:开启主键回写
keyColumn:指定数据库的主键
keyProperty:主键对应的pojo属性名
-->
<insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"
parameterType="com.zpc.mybatis.pojo.User">
INSERT INTO tb_user (
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
)
VALUES
(
#{id},
#{userName},
#{password},
#{name},
#{age},
#{sex},
#{birthday},
NOW(),
NOW()
);
</insert>
<!--
更新的statement
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
-->
<update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
UPDATE tb_user
<trim prefix="set" suffixOverrides=",">
<if test="userName!=null">user_name = #{userName},</if>
<if test="password!=null">password = #{password},</if>
<if test="name!=null">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
<if test="birthday!=null">birthday = #{birthday},</if>
updated = now(),
</trim>
WHERE
(id = #{id});
</update>
<!--
删除的statement
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
-->
<delete id="deleteUserById" parameterType="java.lang.String">
delete from tb_user where id=#{id}
</delete>
</mapper>
3、全局配置文件mybatis-config.xml引入UserMapper.xml
<mappers>
<mapper resource="mappers/MyMapper.xml"/>
<mapper resource="mappers/UserDaoMapper.xml"/>
<mapper resource="mappers/UserMapper.xml"/>
</mappers>
4、创建UserMapper测试用例
package com.zpc.mybatis.test;
import com.zpc.mybatis.dao.UserMapper;
import com.zpc.mybatis.pojo.User;
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 org.junit.Before;
import org.junit.Test;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
public class UserMapperTest {
public UserMapper userMapper;
@Before
public void setUp() throws Exception {
// 指定配置文件
String resource = "mybatis-config.xml";
// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession(true);
// 1. 映射文件的命名空间(namespace)必须是mapper接口的全路径
// 2. 映射文件的statement的id必须和mapper接口的方法名保持一致
// 3. Statement的resultType必须和mapper接口方法的返回类型一致
// 4. statement的parameterType必须和mapper接口方法的参数类型一致(不一定)
this.userMapper = sqlSession.getMapper(UserMapper.class);
}
@Test
public void testQueryUserByTableName() {
List<User> userList = this.userMapper.queryUserByTableName("tb_user");
for (User user : userList) {
System.out.println(user);
}
}
@Test
public void testLogin() {
System.out.println(this.userMapper.login("hj", "123456"));
}
@Test
public void testQueryUserById() {
System.out.println(this.userMapper.queryUserById("1"));
}
@Test
public void testQueryUserAll() {
List<User> userList = this.userMapper.queryUserAll();
for (User user : userList) {
System.out.println(user);
}
}
@Test
public void testInsertUser() {
User user = new User();
user.setId("11");
user.setAge(20);
user.setBirthday(new Date());
user.setName("大神");
user.setPassword("123456");
user.setSex(2);
user.setUserName("bigGod222");
this.userMapper.insertUser(user);
System.out.println(user.getId());
}
@Test
public void testUpdateUser() {
User user = new User();
user.setBirthday(new Date());
user.setName("静静");
user.setPassword("123456");
user.setSex(0);
user.setUserName("Jinjin");
user.setId("1");
this.userMapper.updateUser(user);
}
@Test
public void testDeleteUserById() {
this.userMapper.deleteUserById("1");
}
}
7.3.动态代理总结
8.mybatis-config.xml详解
mybatis-config.xml讲究严格的顺序,具体顺序遵循文档的顺序
8.1.properties属性读取外部资源
properties配置的属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
然后其中的属性就可以在整个配置文件中被用来替换需要动态配置的属性值。比如:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
这个例子中的 username 和 password 将会由 properties 元素中设置的相应值来替换。 driver 和 url 属性将会由 config.properties 文件中对应的值来替换。这样就为配置提供了诸多灵活选择。
属性也可以被传递到 SqlSessionFactoryBuilder.build()方法中。例如:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
// ... or ...
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
- 1)在 properties 元素体内指定的属性首先被读取。
- 2)然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
- 3)最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。
8.2.settings设置
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
测试:
没有开启驼峰匹配:
2018-07-01 13:57:56,486 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ?
2018-07-01 13:57:56,524 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 13:57:56,568 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='null', password='123456', name='大神', age=20, sex=2, birthday='2018-07-01', created='2018-07-01 13:36:09.0', updated='2018-07-01 13:36:09.0'}
开启驼峰匹配:
2018-07-01 13:58:40,599 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ?
2018-07-01 13:58:40,642 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 13:58:40,661 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='bigGod222', password='123456', name='大神', age=20, sex=2, birthday='2018-07-01', created='2018-07-01 13:36:09.0', updated='2018-07-01 13:36:09.0'}
8.3.typeAliases
类型别名是为 Java 类型命名的一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
<typeAliases>
<typeAlias type="com.zpc.mybatis.pojo.User" alias="User"/>
</typeAliases>
缺点:每个pojo类都要去配置。
解决方案:使用扫描包,扫描指定包下的所有类,扫描之后的别名就是类名(不区分大小写),建议使用的时候和类名一致。
<typeAliases>
<!--type:实体类的全路径。alias:别名,通常首字母大写-->
<!--<typeAlias type="com.zpc.mybatis.pojo.User" alias="User"/>-->
<package name="com.zpc.mybatis.pojo"/>
</typeAliases>
Mybatis已经为普通的 Java 类型内建了许多相应的类型别名。它们都是大小写不敏感的.
8.4.typeHandlers(类型处理器)
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。
8.5.plugins(插件)拦截器
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
现在一些MyBatis 插件比如PageHelper都是基于这个原理,有时为了监控sql执行效率,也可以使用插件机制
原理:
自定义拦截器:
// ExamplePlugin.java
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
配置:
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<property name="someProperty" value="100"/>
</plugin>
</plugins>
上面的插件将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象。
8.6.environments(环境)
MyBatis 可以配置成适应多种环境,例如,开发、测试和生产环境需要有不同的配置;
尽管可以配置多个环境,每个 SqlSessionFactory 实例只能选择其一。
虽然,这种方式也可以做到很方便的分离多个环境,但是实际使用场景下,我们更多的是选择使用spring来管理数据源,来做到环境的分离。
8.7.mappers
需要告诉 MyBatis 到哪里去找到 SQL 映射语句。即告诉 MyBatis 到哪里去找映射文件。你可以使用相对于类路径的资源引用, 或完全限定资源定位符(包括 file:/// 的 URL),或类名和包名等。例如:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
这里所谓的mapper接口路径。实际上就是dao的接口路径。在mybatis中,通常把dao的包叫做mapper。类名,也叫做mapper
- 1、定义一个接口。
- 2、在接口所在的包中定义mapper.xml,并且要求xml文件和interface的名称要相同。
- 3、在mybatis-config.xml 中通过class路径,引入mapper(注解方式)。要求mapper.xml 中的名称空间是类的接口的全路径。
注解方式:
<mappers>
<mapper resource="mappers/MyMapper.xml"/>
<mapper resource="mappers/UserDaoMapper.xml"/>
<!--注解方式可以使用如下配置方式-->
<mapper class="com.zpc.mybatis.dao.UserMapper"/>
</mappers>
问题:
- 1、mapper.xml 和 java文件没有分离。 之后的教程讲述和spring整合之后解决。
- 2、需要一个一个的去加载mapper。
当然也可以使用包扫描(必须使用注解方式,即在接口方法上使用注解,如@Select("select * from tb_user ")):
缺点:
- 1、如果包的路径有很多?
- 2、mapper.xml和mapper.java没有分离。
spring整合的时候解决。
9.Mapper XML文件详解
9.1.CRUD标签
9.1.1.select
select – 书写查询sql语句
select中的几个属性说明:
id属性:当前名称空间下的statement的唯一标识。必须。要求id和mapper接口中的方法的名字一致。
resultType:将结果集映射为java的对象类型。必须(和 resultMap 二选一)
parameterType:传入参数类型。可以省略
9.1.2.insert
insert 的几个属性说明:
id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
useGeneratedKeys:开启主键回写
keyColumn:指定数据库的主键
keyProperty:主键对应的pojo属性名
标签内部:具体的sql语句。
9.1.3.update
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。
9.1.4.delete
delete 的几个属性说明:
id属性:当前名称空间下的statement的唯一标识(必须属性);
parameterType:传入的参数类型,可以省略。
标签内部:具体的sql语句。
9.2.#{}和${}
场景:数据库有两个一模一样的表。历史表,当前表
查询表中的信息,有时候从历史表中去查询数据,有时候需要去新的表去查询数据。
希望使用1个方法来完成操作。
<select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
select * from #{tableName}
</select>
/**
* 根据表名查询用户信息(直接使用注解指定传入参数名称)
*
* @param tableName
* @return
*/
public List<User> queryUserByTableName(String tableName);
有问题,报语法错误:相当于执行了这样一条sql:
select * from “tb_user”;
显然表名多了引号。
改正:
<select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
select * from ${tableName}
</select>
注意:
#{}
只是替换?,相当于PreparedStatement使用占位符去替换参数,可以防止sql注入。
${}
是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql;$可以是sql中的任一部分传入到Statement中,不能防止sql注入。
使用${}
去取出参数值信息,需要使用${value}
#{}
只是表示占位,与参数的名字无关,如果只有一个参数,会自动对应。
推荐:
/**
* 根据表名查询用户信息(直接使用注解指定传入参数名称)
*
* @param tableName
* @return
*/
public List<User> queryUserByTableName(@Param("tableName") String tableName);
<select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
select * from ${tableName}
</select>
#{}
多个参数时:
/**
* 登录(直接使用注解指定传入参数名称)
*
* @param userName
* @param password
* @return
*/
public User login( String userName, String password);
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{userName} and password = #{password}
</select>
报错:
org.apache.ibatis.exceptions.PersistenceException:
Error querying database. Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
解决方案一:
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{0} and password = #{1}
</select>
解决方案二:
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{param1} and password = #{param2}
</select>
最终解决方案:
/**
* 登录(直接使用注解指定传入参数名称)
*
* @param userName
* @param password
* @return
*/
public User login(@Param("userName") String userName, @Param("password") String password);
<select id="login" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where user_name = #{userName} and password = #{password}
</select>
通常在方法的参数列表上加上一个注释@Param(“xxxx”) 显式指定参数的名字,然后通过KaTeX parse error: Expected 'EOF', got '#' at position 10: {“xxxx”}或#̲{“xxxx”} sql语句动…{};
sql语句中某个参数进行占位的时候#{}
9.3.面试题(#、$区别)
/**
* #号
* @param username1
* @return
*/
User queryUserListByName1(@Param("username1") String username1);
/**
* $号
* @param username2
* @return
*/
User queryUserListByName2(@Param("username2") String username2);
<select id="queryUserListByName1" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE user_name=#{username1}
</select>
<select id="queryUserListByName2" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE user_name='${username2}'//手动加了引号
</select>
9.4.resultMap
使用:
9.5.sql片段
<sql id=””></sql>
<include refId=”” />
例如在UserMapper.xml中定义如下片段:
<sql id="commonSql">
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
</sql>
则可以在UserMapper.xml中使用它:
<select id="queryUserById" resultMap="userResultMap">
select <include refid="commonSql"></include> from tb_user where id = #{id}
</select>
<select id="queryUsersLikeUserName" resultType="User">
select <include refid="commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
</select>
Sql片段也可以定义在单独的.xml文件中如:
定义CommonSQL.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="CommonSQL">
<sql id="commonSql">
id,
user_name,
password,
name,
age,
sex,
birthday,
created,
updated
</sql>
</mapper>
使用:
<select id="queryUserById" resultMap="userResultMap">
select <include refid="CommonSQL.commonSql"></include> from tb_user where id = #{id}
</select>
<select id="queryUsersLikeUserName" resultType="User">
select <include refid="CommonSQL.commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
</select>
当然要完成这个功能还需要在全局配置文件mybatis-config.xml中引入该外部配置文件:
<mappers>
<mapper resource="CommonSQL.xml"/>
<!-- 开启mapper接口的包扫描,基于class的配置方式 -->
<package name="com.zpc.mybatis.mapper"/>
</mappers>
10.动态sql
场景:查询男性用户,如果输入了姓名,按姓名模糊查询
10.1.if
场景:查询男性用户,如果输入了姓名,则按姓名查询
定义接口:
/**
* 查询男性用户,如果输入了姓名,则按姓名查询
* @param name
* @return
*/
List<User> queryUserList(@Param("name") String name);
编写mapper
<select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE sex=1
<if test="name!=null and name.trim()!=''">
and name like concat('%',#{name},'%')
</if>
</select>
测试
@Test
public void testqueryUserList() {
List<User> users = this.userMapper.queryUserList(null);
for (User user : users) {
System.out.println(user);
}
}
10.2.choose when otherwise
场景:查询男性用户,如果输入了姓名则按照姓名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找姓名为“鹏程”的用户。
定义接口:
/**
* 查询男性用户,如果输入了姓名则按照姓名模糊查找,否则如果输入了年龄则按照年龄查找,否则查找姓名为“鹏程”的用户。
* @param name
* @param age
* @return
*/
List<User> queryUserListByNameOrAge(@Param("name") String name,@Param("age") Integer age);
编写mapper配置:
<select id="queryUserListByNameOrAge" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user WHERE sex=1
<!--
1.一旦有条件成立的when,后续的when则不会执行
2.当所有的when都不执行时,才会执行otherwise
-->
<choose>
<when test="name!=null and name.trim()!=''">
and name like concat('%',#{name},'%')
</when>
<when test="age!=null">
and age = #{age}
</when>
<otherwise>
and name='鹏程'
</otherwise>
</choose>
</select>
测试:
@Test
public void queryUserListByNameOrAge() throws Exception {
List<User> users = this.userMapper.queryUserListByNameOrAge(null, 16);
for (User user : users) {
System.out.println(user);
}
}
10.3.where 和set
场景一:查询所有用户,如果输入了姓名按照姓名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立。
接口:
/**
* 查询所有用户,如果输入了姓名按照姓名进行模糊查询,如果输入年龄,按照年龄进行查询,如果两者都输入,两个条件都要成立
* @param name
* @param age
* @return
*/
List<User> queryUserListByNameAndAge(@Param("name") String name,@Param("age") Integer age);
配置:
<select id="queryUserListByNameAndAge" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user
<!--如果多出一个and,会自动去除,如果缺少and或者多出多个and则会报错-->
<where>
<if test="name!=null and name.trim()!=''">
and name like '%${name}%'
</if>
<if test="age!=null">
and age = #{age}
</if>
</where>
</select>
测试:
@Test
public void queryUserListByNameAndAge() throws Exception {
List<User> users = this.userMapper.queryUserListByNameAndAge("鹏程", 20);
for (User user : users) {
System.out.println(user);
}
}
场景二:修改用户信息,如果参数user中的某个属性为null,则不修改。
接口:
/**
* 根据id更新用户信息
*
* @param user
*/
public void updateUser(User user);
配置:
<update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
UPDATE tb_user
<trim prefix="set" suffixOverrides=",">
<if test="userName!=null">user_name = #{userName},</if>
<if test="password!=null">password = #{password},</if>
<if test="name!=null">name = #{name},</if>
<if test="age!=null">age = #{age},</if>
<if test="sex!=null">sex = #{sex},</if>
<if test="birthday!=null">birthday = #{birthday},</if>
updated = now(),
</trim>
WHERE
(id = #{id});
</update>
测试:
@Test
public void testUpdateUser() {
User user = new User();
user.setBirthday(new Date());
user.setName("静静");
user.setPassword("123456");
user.setSex(0);
user.setUserName("Jinjin");
user.setId("1");
this.userMapper.updateUser(user);
}
10.4.foreach
场景:按照多个id查询用户信息
接口:
/**
* 按多个Id查询
* @param ids
* @return
*/
List<User> queryUserListByIds(@Param("ids") String[] ids);
配置:
<select id="queryUserListByIds" resultType="com.zpc.mybatis.pojo.User">
select * from tb_user where id in
<foreach collection="ids" item="id" open="(" close=")" separator=",">
#{id}
</foreach>
</select>
测试:
@Test
public void queryUserListByIds() throws Exception {
List<User> users = this.userMapper.queryUserListByIds(new String[]{"1","2"});
for (User user : users) {
System.out.println(user);
}
}
If:testognl表达式或者简单java代码
Choose when otherwise—>相当于if else if else
When test参考if
Where set 都有一定的纠错功能
Trim:prefix suffix prefixOverrides suffixOverrides
Foreach:collection item saparator open close
11.缓存
11.1.一级缓存
在mybatis中,一级缓存默认是开启的,并且一直无法关闭
一级缓存满足条件:
- 1、同一个session中
- 2、相同的SQL和参数
测试:
@Test
public void testQueryUserById() {
System.out.println(this.userMapper.queryUserById("1"));
System.out.println(this.userMapper.queryUserById("1"));
}
2018-07-01 17:08:50,156 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2018-07-01 17:08:50,421 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
2018-07-01 17:08:50,423 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ?
2018-07-01 17:08:50,476 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:08:50,509 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
使用:sqlSession.clearCache();可以强制清除缓存
测试:
@Test
public void testQueryUserById() {
System.out.println(this.userMapper.queryUserById("1"));
sqlSession.clearCache();
System.out.println(this.userMapper.queryUserById("1"));
}
日志:
2018-07-01 17:10:51,065 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2018-07-01 17:10:51,359 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
2018-07-01 17:10:51,360 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ?
2018-07-01 17:10:51,408 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:10:51,437 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ?
2018-07-01 17:10:51,438 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:10:51,440 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
执行update、insert、delete的时候,会清空缓存
测试:
@Test
public void testQueryUserById() {
System.out.println(this.userMapper.queryUserById("1"));
//sqlSession.clearCache();
User user=new User();
user.setName("美女");
user.setId("1");
userMapper.updateUser(user);
System.out.println(this.userMapper.queryUserById("1"));
}
日志:
2018-07-01 17:18:15,128 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2018-07-01 17:18:15,399 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 242355057.
2018-07-01 17:18:15,401 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ?
2018-07-01 17:18:15,466 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:18:15,492 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='bigGod222', password='123456', name='鹏程', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 13:35:40.0'}
2018-07-01 17:18:15,527 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==> Preparing: UPDATE tb_user set name = ?, updated = now() WHERE (id = ?);
2018-07-01 17:18:15,529 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] ==> Parameters: 美女(String), 1(String)
2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.updateUser]-[DEBUG] <== Updates: 1
2018-07-01 17:18:15,532 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ?
2018-07-01 17:18:15,533 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:18:15,538 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
11.2.二级缓存
mybatis 的二级缓存的作用域是一个mapper的namespace ,同一个namespace中查询sql可以从缓存中命中。
开启二级缓存:
<mapper namespace="com.zpc.mybatis.dao.UserMapper">
<cache/>
</mapper>
测试:
@Test
public void testCache() {
System.out.println(this.userMapper.queryUserById("1"));
sqlSession.close();
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
System.out.println(mapper.queryUserById("1"));
}
开启二级缓存,必须序列化:
public class User implements Serializable{
private static final long serialVersionUID = -3330851033429007657L;
日志:
2018-07-01 17:23:39,335 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Opening JDBC Connection
2018-07-01 17:23:39,664 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Created connection 2092769598.
2018-07-01 17:23:39,665 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Preparing: select * from tb_user where id = ?
2018-07-01 17:23:39,712 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] ==> Parameters: 1(String)
2018-07-01 17:23:39,734 [main] [com.zpc.mybatis.dao.UserMapper.queryUserById]-[DEBUG] <== Total: 1
User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
2018-07-01 17:23:39,743 [main] [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-[DEBUG] Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@7cbd213e]
2018-07-01 17:23:39,744 [main] [org.apache.ibatis.datasource.pooled.PooledDataSource]-[DEBUG] Returned connection 2092769598 to pool.
2018-07-01 17:23:39,746 [main] [com.zpc.mybatis.dao.UserMapper]-[DEBUG] Cache Hit Ratio [com.zpc.mybatis.dao.UserMapper]: 0.5
User{id='1', userName='bigGod222', password='123456', name='美女', age=20, sex=1, birthday='2018-07-01', created='2018-07-01 13:35:40.0', updated='2018-07-01 17:18:15.0'}
关闭二级缓存:
不开启,或者在全局的mybatis-config.xml 中去关闭二级缓存
<settings>
<!--开启驼峰匹配-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--开启二级缓存,全局总开关,这里关闭,mapper中开启了也没用-->
<setting name="cacheEnabled" value="false"/>
</settings>