Mybatis第二天
动态sql
if
满足if的判断条件,if中的语句会被拼接入sql中。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
choose、when、otherwise
类似Java中的switch,传入了那个参数,就使用哪个参数作为查询条件。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
trim、where、set
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
这个情况下,如果if都判断为false,那么sql语句就会多出一个where,可以使用< where >标签代替where
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
where 元素只会在子元素返回任何内容的情况下才插入 WHERE 子句。而且,若子句的开头为 AND 或 OR , where 元素也会将它们去除。如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。
用于动态更新语句的类似解决方案叫做 set 。 set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
这个例子中, set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
foreach
动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。
比如:
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT *
FROM POST P
WHERE ID in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符。
提示 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach 。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
Mybatis的特殊功能
处理枚举类型
默认情况下MyBatis使用EnumTypeHandler来处理enum类型的Java属性,并且将其存储为enum值的名称。我们不需要为此做任何额外的配置。可以像使用基本数据类型属性一样使用enum类型属性。
当执行insertStudent语句的时候MyBatis会取Gender枚举(FEMALE/MALE)的名称,存储到GENDER列中。如果你想存储FEMALE为0,MALE为1到gender列中,需要在mybatis-config.xml文件中配置专门的类型处理器,并指定它处理的枚举类型是哪个。
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.briup.special.Gender"/>
注意:枚举类型的【顺序值】是根据enum中的声明顺序赋值的。如果改变了Gender里面对象的声明顺序,则数据库存储的数据和此顺序值就不匹配了。
sql语句需要传入多个参数
MyBatis中的映射语句有一个parameterType属性来制定输入参数的类型。如果我们想给映射语句传入多个参数的话,我们可以将所有的输入参数放到HashMap中,将HashMap传递给映射语句。其实就是把多个参数存到Map中,把Map当做一个参数再传给sql语句,mybaits收到后再解析Map集合拿到每一个参数。同时MyBatis还提供了另外一种传递多个输入参数给映射语句的方法。
例如我们想通过给定的name和email信息查找学生信息,定义查询接口如下:
对于映射器中的方法,MyBatis默认从左到右给方法的参数命名为param1、param2…,依次类推。
<update id="updateUserPicNameById1" parameterType="map">
update user_pics set name = #{name} where id = #{id}
</update>
<update id="updateUserPicNameById2">
update user_pics set name = #{param2} where id = #{param1}
</update>
void updateUserPicNameById1(Map<String,String> map);
void updateUserPicNameById2(Integer id,String name);
使用RowBounds对结果集进行分页
MyBatis可以使用RowBounds逐页加载表数据。RowBounds对象可以使用offset和limit参数来构建。参数offset表示开始位置,而limit表示要取的记录的数目。
但是其实Mybatis的分页是基于内存的分页(查出所有记录再按偏移量offset和limit取结果),在大数据量的情况下这样的分页效率会很低,后面我们会使用mybaits的分页辅助工具来完成分页。
oracle使用rownum也可以完成分页: rownum(伪列)是oracle中独有的,是在查询过程中动态生成的一个列,该列不会在数据库中真实添加,表示查询出的每行数据的行号,第一行的行号是1,以此类推。
rownum的操作要求:
rownum 可以等于1
rownum 可以大于0
rownum 可以小于任何数
但是可以给rownum其一个别名,并把sql语句查询结果当做一张表再查询,这样就可以对这个行号进行任意操作了。
缓存
默认情况下,mybatis会启用一级缓存。如果使用同一个session对象调用了相同的SELECT语句,则直接会从缓存中返回结果,而不是再查询一次数据库。
注意:session调用commit或close方法后,这个session中的一级缓存就会被清空
二级缓存: 在不同的session对象之间可以共享缓存的数据
- mybatis-config.xml 文件中保证 < setting name=“cacheEnabled” value=“true”/> 设置中是缓存功能是开启的,默认就是开启的true
- 在需要二级缓存的xml映射文件中,手动开启缓存功能,在根元素中加入一个标签即可: < cache/>
- 一个session查询完数据之后,需要调用commit或者close方法后,这个数据才会进入到二级缓存中,然后其他session就可以共享到这个缓存数据了。
注意:默认情况下,被二级缓存保存的对象需要实现序列化接口。
二级缓存补充说明
- 映射语句文件中的所有select语句将会被缓存
- 映射语句文件中的所有insert,update和delete语句会刷新缓存
- 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
- 缓存会根据指定的时间间隔来刷新。
- 缓存会存储1024个对象
cache标签常用属性:
eviction=“FIFO” flushInterval=“60000” size=“512” readOnly=“true”
Mybatis注解
@Insert
插入注解,标记这个接口对应的方法用于插入数据库。
自动生成主键可以使用 @Options注解的userGeneratedKeys和keyProperty属性让数据库产生 auto_increment(自增长)列的值,然后将生成的值设置到输入参数对象的属性中。
@Insert("insert into students(name,email,addr_id, phone)
values(#{name},#{email},#{address.addr Id},#{phone})")
@Options(useGeneratedKeys = true, keyProperty = "studId")
int insertStudent(Student student);
有一些数据库如Oracle,并不支持AUTO_INCREMENT列属性,它使用序列(SEQUENCE)来产生主键的值。我们可以使用@SelectKey注解来为任意SQL语句来指定主键值,作为主键列的值。假设我们有一个名为STUD_ID_SEQ的序列来生成STUD_ID主键值。
@Insert("insert into students(stud_id,name,email,addr_id,phone) values(#{studId},#
{name},#{email},#{address.addrId},#{phone})")
@SelectKey(statement="select my_seq.nextval from dual",
keyProperty="studId", resultType=int.class, before=true)
int insertStudent(Student student);
@Update
定义一个update映射语句
@Delete
定义一个DELETE映射语句
@Select
定义一个Select映射语句,
结果集映射
可以将查询结果通过别名或者是@Results注解与实体类的属性映射起来。
@Select("select * from students")
@Results(
{
@Result(id = true, column = "stud_id", property = "studId"),
@Result(column = "name", property = "name"),
@Result(column = "email", property = "email"),
@Result(column = "addr_id", property = "address.addrId")
})
List<Student> findAllStudents();
也可以在xml中定义结果集,然后在注解中引用。例如:
@Select("select * from students where stud_id=#{studId}")
@Results(
{
@Result(id = true, column = "stud_id", property = "studId"),
@Result(column = "name", property = "name"),
@Result(column = "email", property = "email"),
@Result(column = "addr_id", property = "address.addrId")
})
Student findStudentById(int studId);
@Select("select * from students")
@Results(
{
@Result(id = true, column = "stud_id", property = "studId"),
@Result(column = "name", property = "name"),
@Result(column = "email", property = "email"),
@Result(column = "addr_id", property = "address.addrId")
})
List<Student> findAllStudents();
这里两个语句的 @Results配置完全相同,可以创建一个映射文件,文件中配置< resultMap>元素,然后使用@ResultMap注解引用此< resultMap>。 在StudentMapper.xml中定义一个ID为StudentResult的< resultMap>。
<mapper namespace="com.briup.mappers.StudentMapper">
<resultMap type="Student" id="StudentResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<result property="phone" column="phone" />
</resultMap>
</mapper>
在StudentMapper.java中,使用 @ResultMap引用名为StudentResult的resultMap:
public interface StudentMapper{
@Select("select * from students where stud_id=#{studId}")
@ResultMap("com.briup.mappers.StudentMapper.StudentResult")
Student findStudentById(int studId);
@Select("select * from students")
@ResultMap("com.briup.mappers.StudentMapper.StudentResult")
List<Student> findAllStudents();
}
一对一映射
MyBatis提供了 @One注解来使用嵌套select语句(Nested-Select)加载一对一关联查询数据。
例如,使用@One注解获取学生及其地址信息 @Result注解中,有one这个属性,也有many这个属性。
public interface StudentMapper{
@Select("select addr_id as addrId, street, city, state, zip, country from
addresses where addr_id=#{id}")
Address findAddressById(int id);
@Select("select * from students where stud_id=#{studId} ")
@Results(
{
@Result(id = true, column = "stud_id", property = "studId"),
@Result(column = "name", property = "name"),
@Result(column = "email", property = "email"),
@Result(property = "address", column = "addr_id",
one = @One(select = "com.briup.mappers.Student Mapper.findAddressById"))
})
Student selectStudentWithAddress(int studId);
}
这里使用了@One注解的select属性来指定一个方法,该方法会返回一个Address对象。使用column=“addr_id”,则 STUEDNTS表中列addr_id的值将会作为输入参数传递给findAddressById()方法。如果 @One SELECT查询返回了多行结果,则会抛出TooManyResultsException异常。之前可以通过基于XML的映射配置,使用嵌套结果ResultMap来加载一对一关联的查询。而MyBatis当前版本中,并没有对应的注解支持。但是可以在映射文件中把1对1的嵌套结果形式的映射信息配置好,然后在@ResultMap注解中去引用它。在StudentMapper.xml中配置< resultMap>,如下所示:
<mapper namespace="com.briup.mappers.StudentMapper">
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<resultMap type="Student" id="StudentWithAddressResult">
<id property="studId" column="stud_id" />
<result property="name" column="name" />
<result property="email" column="email" />
<association property="address" resultMap="AddressResult" />
</resultMap>
</mapper>
public interface StudentMapper{
@Select("select stud_id, name, email, a.addr_id, street, city, state, zip,
country" + " FROM students s left outer join addresses a on s.addr_id=a.addr_id" + "
where stud_id=#{studId} ")
@ResultMap("com.briup.mappers.StudentMapper.StudentWithAddressResult")
Student selectStudentWithAddress(int id);
}
一对多映射
MyBatis提供了@Many注解,用来使用嵌套Select语句加载一对多关联查询。
例如,使用@Many注解获取一个讲师及其教授课程列表信息:
public interface TutorMapper{
@Select("select addr_id as addrId, street, city, state, zip, country from addresses where addr_id=#{id}")
Address findAddressById(int id);
@Select("select * from courses where tutor_id=#{tutorId}") @Results({
@Result(id = true, column = "course_id", property = "courseId"),
@Result(column = "name", property = "name"),
@Result(column = "description", property = "description"),
@Result(column = "start_date" property = "startDate"),
@Result(column = "end_date" property = "endDate")
})
List<Course> findCoursesByTutorId(int tutorId);
@Select("SELECT tutor_id, name as tutor_name, email, addr_id FROM tutors where tutor_id=#{tutorId}")
@Results({
@Result(id = true, column = "tutor_id", property = "tutorId"),
@Result(column = "tutor_name", property = "name"),
@Result(column = "email", property = "email"),
@Result(property = "address", column = "addr_id" one = @One(select = "com.briup.mappers.TutorMapper.findAddressById")),
@Result(property = "courses", column = "tutor_id", many = @Many(select = "com.briup.mappers.TutorMapper.findCoursesByTutorId"))
})
Tutor findTutorById(int tutorId);
}
这里使用了@Many注解的select属性来指向一个方法,该方法将返回一个List< Course >对象。
使用column=”tutor_id”,TUTORS表中的tutor_id列值将会作为输入参数传递给findCoursesByTutorId()方法。
之前可以通过基于XML的映射配置,使用嵌套结果ResultMap来加载一对N关联的查询。而MyBatis当前版本,并没有对应的注解支持。
但是可以在映射文件中把1对N的嵌套结果形式的映射信息配置好,然后在@ResultMap注解中去引用它。
例如,在TutorMapper.xml中配置< resultMap >
<mapper namespace="com.briup.mappers.Tutor Mapper">
<resultMap type="Address" id="AddressResult">
<id property="addrId" column="addr_id" />
<result property="street" column="street" />
<result property="city" column="city" />
<result property="state" column="state" />
<result property="zip" column="zip" />
<result property="country" column="country" />
</resultMap>
<resultMap type="Course" id="CourseResult">
<id column="course_id" property="course Id" />
<result column="name" property="name" />
<result column="description" property="description" />
<result column="start_date" property="startDate" />
<result column="end_date" property="endDate" />
</resultMap>
<resultMap type="Tutor" id="TutorResult">
<id column="tutor_id" property="tutorId" />
<result column="tutor_name" property="name" />
<result column="email" property="email" />
<association property="address" result Map="AddressResult" />
<collection property="courses" result Map="CourseResult" />
</resultMap>
</mapper>
public interface TutorMapper{
@Select("select t.tutor_id, t.name as tutor_name,email,a.addr_id, street, city, state, zip, country, course_id, c.name,description, start_date, end_date from tutors t left outer join addresses a on t.addr_id=a.addr_id left outer join courses c on t.tutor_id=c.tutor_id where t.tutor_id=#{tutorId}")
@Result Map("com.briup.mappers.TutorMapper.TutorResult")
Tutor selectTutorById(int tutorId);
}