mybatis知识点(四)

来源:互联网 发布:三菱plc编程软件work2 编辑:程序博客网 时间:2024/06/05 16:28
3.6 动态SQL  dynamic-sql
有时候,静态的SQL语句并不能满足应用程序的需求。我们可以根据一些条件,来动态地构建 SQL语句。
例如,在Web应用程序中,有可能有一些搜索界面,需要输入一个或多个选项,然后根据这些已选择的条件去执行检索操作。在实现这种类型的搜索功能,我们可能需要根据这些条件来构建动态的SQL语句。如果用户提供了任何输入条件,我们需要将那个条件添加到SQL语句的WHERE子句中。MyBatis通过使用<if>,<choose>,<where>,<foreach>,<trim>元素提供了对构造动态SQL语句的高级别支持。


DynamicSql
3.6.1 If 条件
<if>元素被用来有条件地嵌入SQL片段,如果测试条件被赋值为true,则相应地SQL片段将会被添加到SQL语句中。假定我们有一个课程搜索界面,设置了讲师(Tutor)下拉列表框,课程名称(CourseName)文本输入框,开始时间(StartDate)输入框,结束时间(EndDate)输入框,作为搜索条件。假定课讲师下拉列表是必须选的,其他的都是可选的。
当用户点击搜索按钮时,我们需要显示符合以下条件的成列表:
特定讲师的课程 
课程名
包含输入的课程名称关键字的课程;如果课程名称输入为空,则取所有课程
在开始时间和结束时间段内的课程
我们可以对应的映射语句,如下所示:


<resultMap type="Course" id="CourseResult"> 
 <id 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" /> 
</resultMap> 


<select id="searchCourses" parameterType="map" resultMap="CourseResult">
SELECT * FROM COURSES 
WHERE TUTOR_ID= #{tutorId} 
<if test="courseName != null"> 
AND NAME LIKE #{courseName} 
</if> 
<if test="startDate != null"> 
AND START_DATE >= #{startDate} 
</if> 
<if test="endDate != null"> 
AND END_DATE <![CDATA[ <= ]]> #{endDate} 
</if> 
</select> 


public interface DynamicSqlMapper{ 
List<Course> searchCourses(Map<String, Object> map); 

public void searchCourses(){ 
Map<String,Object> map = new HashMap<String,Object>(); 
map.put("tutorId", 1); 
map.put("courseName", "%Java%"); 
map.put("startDate", new Date()); 
DynamicSqlMapper mapper = sqlSession.getMapper(DynamicSqlMapper.class); 
List<Course> courses = mapper.searchCourses(map);
for (Course course : courses){ 
System.out.println(course); 


此处将生成查询语句SELECT * FROM COURSES WHERE TUTOR_ID= ? AND NAME like ? AND START_DATE >= ?。



3.6.2 choose,when 和 otherwise 条件
有时候,查询功能是以查询类别为基础的。首先,用户需要先选择是否希望通过选择讲师,课程名称,开始时间,或结束时间作为查询条件类别来进行查询,然后根据选择的查询类别,输入相应的参数。在这样的情景中,我们【需要只使用其中一种】查询类别。
MyBatis提供了<choose>元素支持此类型的SQL预处理。 
如果没有选择查询类别,则查询开始时间在今天之后的课程,代码如下:
注意:mysql中now()是当前时间 oracle需要使用sysdate
<select id="searchCourses" parameterType="map" resultMap="CourseResult"> 
SELECT * FROM COURSES 
<choose> 
<when test="searchBy == 'Tutor'"> 
WHERE TUTOR_ID= #{tutorId} 
</when> 
<when test="searchBy == 'CourseName'"> 
WHERE name like #{courseName} 
</when> 
<otherwise> 
WHERE start_date >= sysdate 
</otherwise> 
</choose> 
</select>
MyBatis计算<choose>测试条件的值,且使用第一个值为TRUE的子句。如果没有条件为 true,则使用<otherwise>内的子句。
相当于我们常用的java代码中的这个例子:
if(){
..
}
else if(){
..
}
else{
..
}
 测试例子:public void test_when(){ 
try {
SqlSession sqlSession=
MyBatisSqlSessionFactory.openSession();
Map<String,Object> map = new HashMap<String,Object>(); 
map.put("tutorId", 1); 
map.put("courseName", "%Java%"); 
//输入根据什么类型来查询
map.put("searchBy", "Tutor");
//map.put("searchBy", "CourseName");
//map.put("startDate", new Date()); 
DynamicSqlMapper mapper = 
sqlSession.getMapper(DynamicSqlMapper.class); 
List<Course> courses = 
mapper.searchCourses(map);
for (Course course : courses){ 
System.out.println(course); 
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();




3.6.3 Where 条件
有时候,所有的查询条件应该是可选的。在需要使用至少一种查询条件的情况下,我们应该使用WHERE子句。并且如果有多个条件,我们需要在条件中添加AND或OR。
MyBatis提供了<where>元素支持这种类型的动态SQL语句。 
在我们查询课程界面,我们假设所有的查询条件是可选的。然而,当需要提供一个或多个查询条件时,应该改使用WHERE子句。
<select id="searchCourses" parameterType="map" resultMap="CourseResult"> 
SELECT * FROM COURSES 
<where>  
<if test="tutorId != null "> 
TUTOR_ID= #{tutorId} 
</if> 
<if test="courseName != null"> 
AND name like #{courseName} 
</if> 
<if test="startDate != null"> 
AND start_date >= #{startDate} 
</if> 
<if test="endDate != null"> 
AND end_date <![CDATA[ <= ]]> #{endDate} 
</if> 
</where> 
</select> 


<where>元素只有在其内部标签有返回内容时才会在动态语句上插入WHERE条件语句。并且,如果WHERE子句以AND或者OR打头,则打头的AND或OR将会被移除。例如:如果参数tutorId的值为null,并且courseName参数值不为null,则<where>标签会将AND name like #{courseName}中的AND移除掉,生成的SQL WHERE子句为:
where name like #{courseName}




3.6.4 <trim>条件
<trim>元素和<where>元素类似,但是<trim>提供了在添加前缀/后缀或者移除前缀/后缀方面提供更大的灵活性。
<select id="searchCourses" parameterType="map" resultMap="CourseResult"> 
SELECT * FROM COURSES 
<trim prefix="WHERE" suffixOverrides="or | and"> 
<if test=" tutorId != null ">
TUTOR_ID= #{tutorId} and
</if> 
<if test="courseName != null"> 
name like #{courseName} and
</if> 
</trim> 
</select>

prefix表示有一个if成立则插入where语句
suffix表示后缀,和prefix相反


suffixOverrides="and"表示如果最后生成的sql语句多一个and,则自动去掉.
prefixOverrides的意思是处理前缀,和suffixOverrides相反


这里如果任意一个<if>条件为true,<trim>元素会插入WHERE,并且移除紧跟WHERE后面的AND 


3.6.5 foreach 循环
另外一个强大的动态SQL语句构造标签即是<foreach>。它可以迭代遍历一个数组或者列表,构造AND/OR条件或一个IN子句。
假设我们想找到tutor_id为 1,3,6的讲师所教授的课程,我们可以传递一个tutor_id组成的列表给映射语句,然后通过<foreach>遍历此列表构造动态SQL。
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult"> 
SELECT * FROM COURSES 
<if test="tutorIds != null"> 
<where> 
<foreach item="tutorId" collection="tutorIds"> 
OR tutor_id=#{tutorId} 
</foreach> 
</where>  
</if>  
</select> 

代码:
public interface DynamicSqlMapper{ 
List<Course> searchCoursesByTutors(Map<String,Object> map); 

public void searchCoursesByTutors(){ 
Map<String,Object> map = new HashMap<String,Object>(); 
List<Integer> tutorIds = new ArrayList<Integer>(); 
tutorIds.add(1); 
tutorIds.add(3); 
tutorIds.add(6); 
map.put("tutorIds", tutorIds); 
DynamicSqlMapper mapper = 
sqlSession.getMapper(DynamicSqlMapper.class); 
List<Course> courses = mapper.searchCoursesByTutors(map); 
for (Course course : courses){ 
System.out.println(course); 




怎样使用<foreach>生成IN子句:
<select id="searchCoursesByTutors" parameterType="map" resultMap="CourseResult"> 
SELECT * FROM COURSES 
<if test="tutorIds != null"> 
<where> 
tutor_id IN 
<foreach item="tempValue" collection="tutorIds" open="(" separator="," close=")"> 
#{tempValue} 
</foreach> 
</where> 
</if> 
</select> 






3.6.6 set 条件
<set>元素和<where>元素类似,如果其内部条件判断有任何内容返回时,他会插入SET SQL 片段。
 
<update id="updateCourse" parameterType="Course"> 
update courses  
<set> 
<if test="name != null">name=#{name},</if> 
<if test="description != null">description=#{description},</if> 
<if test="startDate != null">start_date=#{startDate},</if> 
<if test="endDate != null">end_date=#{endDate},</if> 
</set> 
where course_id=#{courseId} 
</update> 


这里,如果<if>条件返回了任何文本内容,<set>将会插入set关键字和其文本内容,并且会剔除将末尾的“,”。在上述的例子中,如果phone!=null,<set>将会让会移除phone=#{phone}后的逗号“,”,生成set phone=#{phone} 






四 mybatis的一些特殊功能 special


除了简化数据库编程外,MyBatis还提供了各种功能,这些对实现一些常用任务非常有用,比如按页加载表数据,存取CLOB/BLOB类型的数据,处理枚举类型值,等等。


4.1 处理枚举类型
MyBatis支持持久化enum类型属性。假设t_user表中有一列gender(性别)类型为 varchar2(10),存储 MALE 或者 FEMALE 两种值。并且,Student对象有一个enum类型的gender 属性,如下所示:


public enum Gender { 
FEMALE,MALE 



默认情况下MyBatis使用EnumTypeHandler来处理enum类型的Java属性,并且将其存储为 enum值的名称。你不需要为此做任何额外的配置。你可以可以向使用基本数据类型属性一样使用enum类型属性,如下:
drop table t_user;
create table t_user(
 id number primary key,
 name varchar2(50),
 gender varchar2(10)
);


public class User{ 
private Integer id; 
private String name; 
private Gender gender; 


//setters and getters 



 
<insert id="insertUser" parameterType="User"> 
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select my_seq.nextval from dual
</selectKey>
insert into t_user(id,name,gender) 
values(#{id},#{name},#{gender}) 
</insert>


当你执行insertStudent语句的时候MyBatis会取Gender枚举(FEMALE/MALE)的名称,然后将其存储到GENDER列中。如果你希望存储原enum的顺序位置(0/1),而不是enum名,你需要明确地配置它
如果你想存储FEMALE为0,MALE为1到gender列中,你需要在mybatis-config.xml文件中配置EnumOrdinalTypeHandler: 
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="com.briup.special.Gender"/> 


注意:使用顺序位置为值存储到数据库时要当心。顺序值是根据enum中的声明顺序赋值的。如果你改变了Gender里面对象的声明顺序,则数据库存储的数据和此顺序值就不匹配了。练习:查询出来看看枚举是什么样的?

4.2 处理CLOB/BLOB类型数据
BLOB和CLOB都是大字段类型,BLOB是按二进制来存储的,而CLOB是可以直接存储文字的。通常像图片、文件、音乐等信息就用BLOB字段来存储,先将文件转为二进制再存储进去。而像文章或者是较长的文字,就用CLOB存储.


BLOB和CLOB在不同的数据库中对应的类型也不一样:
MySQL 中:clob对应text/longtext,blob对应blob  
Oracle中:clob对应clob,blob对应blob  

MyBatis提供了内建的对CLOB/BLOB类型列的映射处理支持。

drop table user_pics;
create table user_pics( 
id number primary key, 
name varchar2(50) , 
pic blob, 
bio clob
); 


这里,照片可以是PNG,JPG或其他格式的。简介信息可以是学生或者讲师的漫长的人生经历。默认情况下,MyBatis将CLOB类型的列映射到java.lang.String类型上、而把BLOB列映射到byte[]类型上。
 
public class UserPic{ 
private int id; 
private String name; 
private byte[] pic; 
private String bio; 
//setters & getters 

 
<insert id="insertUserPic" parameterType="UserPic"> 
<selectKey keyProperty="id" resultType="int" order="BEFORE">
select my_seq.nextval from dual
</selectKey>
insert into user_pics(id,name, pic,bio) 
values(#{id},#{name},#{pic},#{bio}) 
</insert> 
<select id="getUserPicById" parameterType="int" resultType="UserPic"> 
select * from user_pics where id=#{id} 
</select> 


java代码:
@Test
public void test_insertUserPic(){ 
byte[] pic = null; 
try {
//读取用户头像图片
File file = new File("src/com/briup/special/test.png"); 
InputStream is = new FileInputStream(file); 
pic = new byte[is.available()]; 
is.read(pic); 
is.close(); 
} catch (Exception e){ 
e.printStackTrace(); 

String name = "tom"; 
String bio = "可以是很长的字符串";
//准备好要插入到数据库中的数据并封装成对象
UserPic userPic = new UserPic(name, pic , bio); 


SqlSession session = null; 
try{ 
session = MyBatisSqlSessionFactory.openSession();
SpecialMapper mapper = session.getMapper(SpecialMapper.class);
mapper.insertUserPic(userPic);
session.commit(); 
}catch (Exception e) {
e.printStackTrace();
session.rollback();
}finally {
if(session!=null)session.close();
}



下面的getUserPic()方法展示了怎样将CLOB类型数据读取到String类型,BLOB类型数据读取成byte[]属性:


@Test
public void test_getUserPicById(){

SqlSession session = null;
try {
session = MyBatisSqlSessionFactory.openSession();

SpecialMapper mapper = session.getMapper(SpecialMapper.class);

UserPic userPic = mapper.getUserPicById(24);

System.out.println(userPic.getId());
System.out.println(userPic.getName());
System.out.println(userPic.getBio());
System.out.println(userPic.getPic().length);

} catch (Exception e) {
e.printStackTrace();
}finally {
if(session!=null)session.close();
}
}




4.3 传入多个输入参数
MyBatis中的映射语句有一个parameterType属性来制定输入参数的类型。如果我们想给映射语句传入多个参数的话,我们可以将所有的输入参数放到HashMap中,将HashMap传递给映射语句。同时MyBatis还提供了另外一种传递多个输入参数给映射语句的方法。假设我们想通过给定的name和email信息查找学生信息,定义查询接口如下:
对于映射器中的方法,MyBatis默认从左到右给方法的参数命名为param1、param2…,依次类推。
 
public interface StudentMapper{ 
List<Student> findAllStudentsByNameEmail(String name, String email); 

MyBatis支持将多个输入参数传递给映射语句,并以#{param}的语法形式引用它们:
<select id="findAllStudentsByNameEmail" resultMap="StudentResult"> 
select stud_id, name,email, phone from Students 
where name=#{param1} and email=#{param2} 
</select> 
这里#{param1}引用第一个参数name,而#{param2}引用了第二个参数email。


代码中调用:
Student Mapper student Mapper = sql Session.get Mapper(StudentMapper.class); 
student Mapper.findAllStudentsByNameEmail(name, email); 




4.4 多行结果集映射成Map
可以使用之前我们介绍到的接口的方式来实现(默认把列名作为key,列中的值作为value)。
如果有一些特殊的情况,比如需要使用id值作为key,把一行数据封装成的对象作为value放到map中的话,需要使用下面的方式:

<select id="findAllUsers" resultType="User"> 
select id,name,gender from t_user 
</select> 


Map<Integer, User> map = session.selectMap("com.briup.mappers.SpecialMapper.findAllUsers","id");
for(Integer key:map.keySet()){
System.out.println(key+" : "+map.get(key));

注意:需要注意gender列的值都是数字还是都是字符串(需要一致)


这里map将会将id作为key值,而每行数据封装成的User对象作为value值。


4.5 使用RowBounds对结果集进行分页
有时候,我们会需要跟海量的数据打交道,比如一个有数百万条数据级别的表。由于计算机内存的现实我们不可能一次性加载这么多数据,我们可以获取到数据的一部分。特别是在Web应用程序中,分页机制被用来以一页一页的形式展示海量的数据。
MyBatis可以使用RowBounds逐页加载表数据。RowBounds对象可以使用offset和limit参数来构建。参数offset表示开始位置,而limit表示要取的记录的数目

<select id="findAllUsers" resultType="User"> 
select id,name,gender from t_user 
</select> 

public List<User> findAllUsers(RowBounds rowBounds);


然后,你可以加载第一页数据(前5条):
int offset = 0;
int limit = 5; 
RowBounds rowBounds = new RowBounds(offset, limit); 
List<Student> = studentMapper.getStudents(rowBounds); 
若要展示第二页,使用offset=5,limit=5

但是其实Mybatis的分页是基于内存的分页(查出所有记录再按偏移量和limit取结果),在大数据量的情况下这样的分页效率会很低。

oracle使用rownum也可以完成分页:
rownum 只能等于1
rownum 大于0
rownum 可以小于任何数
例如:把sql语句查询结果当做一张表再查询
select *
from (
select rownum as rowno, t.*
from t_user t
where rownum <= 10
) temp
where temp.rowno >= 5;






4.6 使用ResultHandler自定义结果集ResultSet处理
MyBatis在将查询结果集映射到java对象方面提供了很大的选择性。但是,有时候我们会遇到由于特定的目的,需要我们自己处理SQL查询结果的情况。MyBatis提供了ResultHandler接口,可以让我们以任何自己喜欢的方式处理结果集ResultSet。
例如:我们要把t_user表中所有数据的id和name查询出来,并且把id值作为key,把name值作为value封装到Map集合中


注意:sqlSession.selectMap()则可以返回以给定列为key,记录对象为value的map。但是不能将其配置成使用其中一个属性作为key,而另外的属性作为 value。但是mybatis在之后的版本中可能会完成这个功能



对于sqlSession.select()方法,我们可以传递给它一个ResultHandler接口的实现,它会被调用来处理ResultSet的每一条记录,而且完成我们上面的需求:


@Test
public void test_ResultHandler(){
final Map<Integer,String> map = new HashMap<Integer, String>(); 
SqlSession session = null;
try {
session = MyBatisSqlSessionFactory.openSession();

session.select("com.briup.mappers.SpecialMapper.findAllUsers", new ResultHandler<User>() {


@Override
public void handleResult(ResultContext<? extends User> resultContext) {
User user = resultContext.getResultObject(); 
map.put(user.getId(), user.getName()); 
}
});

for(Integer key:map.keySet()){
System.out.println(key+" : "+map.get(key));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(session!=null)session.close();
}
}


在上述的代码中,我们提供了匿名内部类对ResultHandler接口的实现,在handleResult()方法中,我们使用context.getResultObject()获取当前的result对象,即User对象,并对查询返回的每一行都会调用handleResult()方法,从而我们从User对象中取出id和name的值,将其放到map中。




4.7 缓存
将从数据库中加载的数据缓存到内存中,是很多应用程序为了提高性能而采取的一贯做法。默认情况下,mybatis会启用一级缓存;即,如果你使用同一个SqlSession接口对象调用了相同的SELECT语句,则直接会从缓存中返回结果,而不是再查询一次数据库。
注意:session调用commit或close方法后,一级缓存就会被清空 


例如: 根据日志输出可以看出,下面代码只会发出一条sql查询语句
@Test
public void test_cache1(){
SqlSession session = null;
try {
session = MyBatisSqlSessionFactory.openSession();

SpecialMapper mapper = session.getMapper(SpecialMapper.class);

User user1 = mapper.findUserById(21);
System.out.println(user1);

// session.commit();

User user2 = mapper.findUserById(21);
System.out.println(user2);

} catch (Exception e) {
e.printStackTrace();
}finally {
if(session!=null)session.close();
}
}


二级缓存: 在不同的session对象之间可以共享缓存数据
1.mybatis-config.xml文件中保证<setting name="cacheEnabled" value="true"/>设置中是缓存功能是开启的,默认就是开启的true
2.在需要二级缓存的xml映射文件中,手动开启缓存功能,在根元素中加入一个标签即可:<cache/>
3.一个session查询完数据之后,需要调用commit或者close方法后,这个数据才会进入到缓存中,然后其他session就可以共享到这个缓存数据了

注意:默认情况下,被二级缓存保存的对象需要实现序列化接口,可以通过cache标签的readOnly属性进行设置


例如:
mybatis-config.xml:
<settings>
<setting name="cacheEnabled" value="true"/>
</settings>


xml映射文件:
<mapper namespace="com.briup.mappers.SpecialMapper">
<cache/>
<select> ..</select>
<select> ..</select>
<select> ..</select>
</mapper>


测试代码:
@Test
public void test_cache2(){
SqlSession session1 = null;
SqlSession session2 = null;
try {
session1 = MyBatisSqlSessionFactory.openSession();
session2 = MyBatisSqlSessionFactory.openSession();

SpecialMapper mapper1 = session1.getMapper(SpecialMapper.class);
SpecialMapper mapper2 = session2.getMapper(SpecialMapper.class);

User user1 = mapper1.findUserById(21);
System.out.println(user1);
session1.commit();

User user2 = mapper2.findUserById(21);
System.out.println(user2);
session2.commit();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(session1!=null)session1.close();
if(session2!=null)session2.close();
}
}


二级缓存补充说明
  1. 映射语句文件中的所有select语句将会被缓存
  2. 映射语句文件中的所有insert,update和delete语句会刷新缓存
  3. 缓存会使用Least Recently Used(LRU,最近最少使用的)算法来收回。
  4. 缓存会根据指定的时间间隔来刷新。
  5. 缓存会存储1024个对象


cache标签常用属性:
<cache 
eviction="FIFO"  <!--回收策略为先进先出-->
flushInterval="60000" <!--自动刷新时间60s-->
size="512" <!--最多缓存512个引用对象-->
readOnly="true"/> <!--true表示对象不能被写出去,只能读数据,即不可以被序列化,false表示可以写出去,即可以被序列化,默认值是false-->




五 MyBatis中的注解
之前我们都是在映射器MapperXML配置文件中配置映射语句的。除此之外MyBatis也支持使用注解来配置映射语句。当我们使用基于注解的映射器接口时,我们不再需要在XML配置文件中配置了。如果你愿意,你也可以同时使用基于XML和基于注解的映射语句。


5.1 在映射器Mapper接口上使用注解
MyBatis对于大部分的基于XML的映射器元素(包括<select>,<update>)提供了对应的基于注解的配置项。然而在某些情况下,基于注解配置还不能支持基于XML的一些元素。


5.2 映射语句
MyBatis提供了多种注解来支持不同类型的语句如:SELECT,INSERT,UPDATE,DELETE。

5.2.1 @Insert 
我们可以使用 @Insert注解来定义一个INSERT映射语句:
 
package com.briup.mappers; 
public interface StudentMapper{ 


@Insert("INSERT INTO STUDENTS(STUD_ID,NAME,EMAIL,ADDR_ID, PHONE) 
VALUES(#{studId},#{name},#{email},#{address.addr Id},#{phone})") 
int insertStudent(Student student); 





自动生成主键


可以使用 @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); 


这里STUD_ID列值将会通过MySQL数据库自动生成。并且生成的值将会被设置到student对象的studId属性中。
StudentMapper mapper = sqlSession.get Mapper(Student Mapper.class); 
mapper.insertStudent(student); 
int studentId = student.getStudId(); 

有一些数据库如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 STUD_ID_SEQ.NEXTVAL FROM DUAL",  
keyProperty="studId", resultType=int.class, before=true) 
int insertStudent(Student student); 


这里我们使用了 @Select Key来生成主键值,并且存储到了student对象的studId属性上。由于我们设置了before=true,该语句将会在执行INSERT语句之前执行。如果你使用序列作为触发器来设置主键值,我们可以在INSERT语句执行后,从sequence_name.currval获取数据库产生的主键值。


@Insert("INSERT INTO STUDENTS(NAME,EMAIL,ADDR_ID, PHONE)  
VALUES(#{name},#{email},#{address.addrId},#{phone})") 
@SelectKey(statement="SELECT STUD_ID_SEQ.CURRVAL FROM DUAL",  
keyProperty="studId", resultType=int.class, before=false) 
int insertStudent(Student student); 






5.2.2 @Update 
我们可以使用 @Update注解来定义一个UPDATE映射语句,如下所示:
@Update("UPDATE STUDENTS SET NAME=#{name}, EMAIL=#{email},  
PHONE=#{phone} WHERE STUD_ID=#{studId}") 
int updateStudent(Student student); 




StudentMapper mapper = sqlSession.getMapper(StudentMapper.class); 
int noOfRowsUpdated = mapper.updateStudent(student); 






5.2.3 @Delete 
我们可以使用 @Delete注解来定义一个DELETE映射语句,如下所示:
@Delete("DELETE FROM STUDENTS WHERE STUD_ID=#{studId}") 
int deleteStudent(int studId);




5.2.4 @Select
我们可以使用 @Select注解来定义一个SELECT映射语句。
 
package com.briup.mappers; 
public interface StudentMapper{ 
@Select("SELECT STUD_ID AS STUDID, NAME, EMAIL, PHONE FROM 
STUDENTS WHERE STUD_ID=#{studId}") 
Student findStudentById(Integer studId); 

为了将列名和Student bean属性名匹配,我们为stud_id起了一个studId的别名。如果返回了多行结果,将抛出TooManyResultsException异常。




5.3 结果映射
我们可以将查询结果通过别名或者是 @Results注解与Java Bean属性映射起来。
 
package com.briup.mappers; 
public interface StudentMapper{ 
@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注解和映射器XML配置文件元素<resultMap> 对应。然而,My Batis3.2.2不能为 @Results注解赋予一个ID。所以,不像<resultMap>元素,我们不应在不同的映射语句中重用 @Results声明。这意味着即使 @Results注解完全相同,我们也需要(在不同的映射接口中)重复 @Results声明。



例如,看下面的 findStudentById()和 findAllStudents()方法:


@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配置完全相同,但是必须得重复它。这里有一个解决方法。我们可以创建一个映射器Mapper配置文件,然后配置<resultMap>元素,然后使用 @ResultMap注解引用此<resultMap>。 在StudentMapper.xml中定义一个ID为StudentResult的<resultMap>。

xml配置:
 
<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中,使用 @Result Map引用名为StudentResult的resultMap:
 
public interface StudentMapper{ 
@Select("SELECT * FROM STUDENTS WHERE STUD_ID=#{studId}") 
@ResultMap("com.briup.mappers.StudentMapper.StudentResult") 
Student findStudentById(int stud Id); 


@Select("SELECT * FROM STUDENTS") 
@ResultMap("com.briup.mappers.StudentMapper.StudentResult") 
List<Student> findAllStudents(); 





5.4 一对一映射
MyBatis提供了 @One注解来使用嵌套select语句(Nested-Select)加载一对一关联查询数据。让我们看看怎样使用 @One注解获取学生及其地址信息
 
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的值将会作为输入参数传递给find Address By Id()方法。如果 @One SELECT查询返回了多行结果,则会抛出TooManyResultsException异常。


int studId = 1; 
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); 
Student student = studentMapper.selectStudentWithAddress(studId); 
System.out.println("Student :"+student); 
System.out.println("Address :"+student.getAddress()); 


我们可以通过基于XML的映射器配置,使用嵌套结果ResultMap来加载一对一关联的查询。而MyBatis3.2.2 版本,并没有对应的注解支持。但是我们可以在映射器Mapper配置文件中配置<resultMap>并且使用 @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); 





5.5 一对多映射
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.Tutor Mapper.findAddressById")), 
@Result(property = "courses", column = "tutor_id", 
many = @Many(select = "com.briup.mappers.Tutor Mapper.findCoursesByTutorId")) 
}) 
Tutor findTutorById(int tutorId); 



这里我们使用了 @Many注解的select属性来指向一个完全限定名称的方法,该方法将返回一个 List<Course>对象。使用column=”tutor_id”,TUTORS表中的tutor_id列值将会作为输入参数传递给find Courses By Tutor Id()方法。 
之前我们使用过嵌套结果ResultMap来加载一对多关联的查询。而MyBatis3.2.2版本,并没有对应的注解支持。但是我们可以在映射器Mapper配置文件中配置<result Map>并且使用 @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); 
原创粉丝点击