1.MyBatis映射文件深入
1.1.动态sql语句之<if>
我们根据实体类的不同取值,使用不同的SQL语句来进行查询。比如在id如果不为空时可以根据id查询,如果username不为空时还要加入用户名作为条件。这种情况在我们的多条件组合查询中经常会碰到。
使用<if>做判别条件处理可以动态地调整sql的长度,使之应用更加广泛的条件场景。
使用<if>经常和<where>标签结合使用组成where动态条件查询。适用场景如下:
Mapper接口层:
public interface UserMapper { public List<User> findByCondition(User user); }
UserMapper.xml配置:
<mapper namespace="com.fengye.mapper.UserMapper"> <select id="findByCondition" parameterType="com.fengye.domain.User" resultType="com.fengye.domain.User"> select * from user <where> <if test="id!=0"> and id = #{id} </if> <if test="username!=null"> and username = #{username} </if> <if test="password!=null"> and password = #{password} </if> </where> </select> </mapper>
1.2.动态sql语句之<foreach>
<foreach>标签用于需要遍历某个传入参数是数组或集合等情况,通常sql中使用in后接数组进行遍历。
适用场景如下:
Mapper接口层:
public interface UserMapper { public List<User> findByIdList(List<Integer> idList); public List<User> findByIdArrays(int[] arr); }
UserMapper.xml:
<select id="findByIdList" parameterType="list" resultType="com.fengye.domain.User"> select * from user <where> <!--集合List查询collection使用list,parameterType使用java.util.List(或者简写list)--> <foreach collection="list" open="id in (" close=")" separator="," item="id"> #{id} </foreach> </where> </select> <select id="findByIdArrays" parameterType="java.util.Arrays" resultType="com.fengye.domain.User"> select * from user <where> <!--数组array查询collection使用array,parameterType使用java.util.Arrays --> <foreach collection="array" open="id in (" close=")" separator="," item="id"> #{id} </foreach> </where> </select>
1.3.动态sql语句之<include>
Mapper.xml中的sql语句后期维护可能需要变更某些常用的一致的语句,为了后期便于维护sql,我们可以使用<include>标签对相同的sql进行抽取。
使用场景:
<!--公共相同的sql语句--> <sql id="selectUser"> select * from user </sql> <select id="findByCondition" parameterType="com.fengye.domain.User" resultType="com.fengye.domain.User"> <include refid="selectUser"></include> <where> <if test="id!=0"> and id = #{id} </if> <if test="username!=null"> and username = #{username} </if> <if test="password!=null"> and password = #{password} </if> </where> </select> <select id="findByIdList" parameterType="list" resultType="com.fengye.domain.User"> <include refid="selectUser"></include> <where> <!--集合List查询collection使用list,parameterType使用java.util.List(或者简写list)--> <foreach collection="list" open="id in (" close=")" separator="," item="id"> #{id} </foreach> </where> </select>
2.MyBatis核心配置文件深入
2.1.typeHandlers标签
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。下表描述了一些默认的类型处理器(截取部分)。
你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。具体做法为:实现 org.apache.ibatis.type.TypeHandler 接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler, 然后可以选择性地将它映射到一个JDBC类型。
例如需求:一个Java中的Date数据类型,我想将之存到数据库的时候存成一个1970年至今的毫秒数,取出来时转换成java的Date,即java的Date与数据库的varchar毫秒值之间转换。
开发步骤:
① 定义转换类继承类BaseTypeHandler<T>
② 覆盖4个未实现的方法,其中setNonNullParameter为java程序设置数据到数据库的回调方法,getNullableResult为查询时 mysql的字符串类型转换成 java的Type类型的方法
③ 在MyBatis核心配置文件中进行注册
④ 测试转换是否正确
实际操作过程中数据库表设计为:
User类为:
public class User { private int id; private String username; private String password; private Date birthday; }
直接执行普通new Date()插入时会报无法转化数据列错误,即无法将Date类型转换成long类型:
Cause: java.sql.SQLException: Data truncated for column 'birthday' at row 1
此时就需要我们实现自定义数据类型转换,告诉mybatis在转换时将Date类型转换成长整型插入数据库。
以下为主要实现步骤:
①自定义类型转换器DateTypeHandler继承BaseTypeHandler,并实现接口方法:
public class DateTypeHandler extends BaseTypeHandler<Date> { //将<T>对应的类型Date转换成数据库需要的类型long public void setNonNullParameter(PreparedStatement preparedStatement, int index, Date date, JdbcType jdbcType) throws SQLException { long dateTime = date.getTime(); preparedStatement.setLong(index, dateTime); } //用于将数据库中查询出的数据类型转换成<T>对应的类型Date public Date getNullableResult(ResultSet resultSet, String column) throws SQLException { long aLong = resultSet.getLong(column); return new Date(aLong); } //用于将数据库中查询出的数据类型转换成<T>对应的类型Date public Date getNullableResult(ResultSet resultSet, int i) throws SQLException { long aLong = resultSet.getLong(i); return new Date(aLong); } //用于将数据库中查询出的数据类型转换成<T>对应的类型Date public Date getNullableResult(CallableStatement callableStatement, int i) throws SQLException { long aLong = callableStatement.getLong(i); return new Date(aLong); } }
②将自定义的类型转换器注册在mybatis-config.xml中:
<?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"> <!--引入自定义的类型转换器DateTypeHandler--> <typeHandlers> <typeHandler handler="com.fengye.handler.DateTypeHandler"></typeHandler> </typeHandlers> </configuration>
测试插入数据库可以看到数据类型Date已映射到数据库中为Long长整型:
2.2.plugins标签实现分页操作
MyBatis可以使用第三方的插件来对功能进行扩展,分页助手PageHelper插件是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
① 导入通用PageHelper的坐标
② 在mybatis核心配置文件中配置PageHelper插件
③ 测试分页数据获取
导入通用PageHelper坐标:
<!-- 分页助手 --> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5</version> </dependency> <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>0.9.1</version> </dependency>
在mybatis-config.xml中使用<plugin>插件导入外部分页插件:
<configuration> <!-- 注意:分页助手的插件,配置在通用mapper之前 --> <plugins> <plugin interceptor="com.github.pagehelper.PageHelper"> <!--指定数据库方言--> <property name="dialect" value="mysql"></property> </plugin> </plugins> </configuration>
编写测试类,可以看到PageHelper对象的相关参数获取及API使用:
/** * 测试分页代码实现 * @throws IOException */ @Test public void test02() throws IOException { //设置分页参数 PageHelper.startPage(2, 3); InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> userList = userMapper.findAll(); for (User user : userList) { System.out.println(user); } //获取分页相关其它参数:需要传入查询到的List数据 PageInfo<User> pageInfo = new PageInfo<User>(userList); System.out.println("总条数:"+pageInfo.getTotal()); System.out.println("总页数:"+pageInfo.getPages()); System.out.println("当前页:"+pageInfo.getPageNum()); System.out.println("每页显示长度:"+pageInfo.getPageSize()); System.out.println("是否第一页:"+pageInfo.isIsFirstPage()); System.out.println("是否最后一页:"+pageInfo.isIsLastPage()); }
打印输出语句:
本节代码示例均已上传至github地址:
https://github.com/devyf/MyBatisReview/tree/master/fengye_mybatis_config