支持的注解有以下:
MyBatis可以利用SQL映射文件来配置,也可以利用Annotation来设置。MyBatis提供的一些基本注解如下表所示。
注解 |
目标 |
相对应的 XML |
描述 |
@CacheNamespace |
类 |
|
为给定的命名空间 (比如类) 配置缓存。属性:implemetation,eviction, flushInterval,size,readWrite,blocking 和 properties。 |
@Property |
N/A |
|
Specifies the property value or placeholder(can replace by configuration properties that defined at themybatis-config.xml). Attributes: name, value. (Available on MyBatis 3.4.2+) |
@CacheNamespaceRef |
类 |
|
参照另外一个命名空间的缓存来使用。属性:value, name。 If you use this annotation, you should be specified either value or name attribute. For the value attribute specify a java type indicating the namespace(the namespace name become a FQCN of specified java type), and for the name attribute(this attribute is available since 3.4.2) specify a name indicating the namespace. |
@ConstructorArgs |
方法 |
|
收集一组结果传递给一个劫夺对象的构造方法。属性:value,是形式参数的数组。 |
@Arg |
N/A |
· · |
单独的构造方法参数 , 是 ConstructorArgs 集合的一部分。属性: id,column,javaType,typeHandler。 id 属性是布尔值, 来标识用于比较的属性,和 |
@TypeDiscriminator |
方法 |
|
一组实例值被用来决定结果映射的表现。属性: column, javaType, jdbcType, typeHandler,cases。cases 属性就是实例的数组。 |
@Case |
N/A |
|
单独实例的值和它对应的映射。属性: value,type,results。Results 属性是结果数组,因此这个注解和实际的 ResultMap 很相似,由下面的 Results 注解指定。 |
@Results |
方法 |
|
结果映射的列表, 包含了一个特别结果列如何被映射到属性或字段的详情。属性:value, id。value 属性是 Result 注解的数组。这个id的属性是结果映射的名称。 |
@Result |
N/A |
· · |
在列和属性或字段之间的单独结果映射。属性:id,column, property, javaType ,jdbcType ,type Handler, one,many。id 属性是一个布尔值,表示了应该被用于比较(和在 XML 映射中的 |
@One |
N/A |
|
复杂类型的单独属性值映射。属性: select,已映射语句(也就是映射器方法)的完全限定名,它可以加载合适类型的实例。注意:联合映射在注解 API 中是不支持的。这是因为 Java 注解的限制,不允许循环引用。 fetchType会覆盖全局的配置参数lazyLoadingEnabled。 |
@Many |
N/A |
|
映射到复杂类型的集合属性。属性:select,已映射语句(也就是映射器方法)的全限定名,它可以加载合适类型的实例的集合,fetchType会覆盖全局的配置参数lazyLoadingEnabled。 注意联合映射在注解 API中是不支持的。这是因为 Java 注解的限制,不允许循环引用 |
@MapKey |
方法 |
复杂类型的集合属性映射。属性 : select,是映射语句(也就是映射器方法)的完全限定名,它可以加载合适类型的一组实例。注意:联合映射在 Java 注解中是不支持的。这是因为 Java 注解的限制,不允许循环引用。 |
|
@Options |
方法 |
映射语句的属性 |
这个注解提供访问交换和配置选项的宽广范围, 它们通常在映射语句上作为属性出现。而不是将每条语句注解变复杂,Options 注解提供连贯清晰的方式来访问它们。属性:useCache=true , flushCache=FlushCachePolicy.DEFAULT , resultSetType=FORWARD_ONLY , statementType=PREPARED , fetchSize=-1 , , timeout=-1 useGeneratedKeys=false , keyProperty=”id” , keyColumn=”” , resultSets=””。理解 Java 注解是很重要的,因为没有办法来指定“null” 作为值。因此,一旦你使用了 Options 注解,语句就受所有默认值的支配。要注意什么样的默认值来避免不期望的行为。 |
· @Insert · @Update · @Delete · @Select |
方法 |
· · · · |
这些注解中的每一个代表了执行的真实 SQL。它们每一个都使用字符串数组 (或单独的字符串)。如果传递的是字符串数组, 它们由每个分隔它们的单独空间串联起来。这就当用 Java 代码构建 SQL 时避免了“丢失空间”的问题。然而,如果你喜欢,也欢迎你串联单独的字符串。属性:value,这是字符串数组用来组成单独的 SQL 语句。 |
· @InsertProvider · @UpdateProvider · @DeleteProvider · @SelectProvider |
方法 |
· · · · |
这些可选的 SQL 注解允许你指定一个类名和一个方法在执行时来返回运行允许创建动态的 SQL。基于执行的映射语句, MyBatis 会实例化这个类,然后执行由 provider 指定的方法. You can pass objects that passed to arguments of a mapper method, "Mapper interface type" and "Mapper method" via theProviderContext(available since MyBatis 3.4.5 or later) as method argument. (In MyBatis 3.4 or later, it's allow multiple parameters) 属性: type,method。type 属性是类。method 属性是方法名。注意: 这节之后是对类的讨论,它可以帮助你以干净,容于阅读的方式来构建动态 SQL。 |
@Param |
Parameter |
N/A |
如果你的映射器的方法需要多个参数, 这个注解可以被应用于映射器的方法参数来给每个参数一个名字。否则,多参数将会以它们的顺序位置来被命名 (不包括任何 RowBounds 参数) 比如。 #{param1} , #{param2} 等 , 这是默认的。使用 @Param(“person”),参数应该被命名为 #{person}。 |
@SelectKey |
方法 |
|
该注解复制了 |
@ResultMap |
方法 |
N/A |
这个注解给@Select或者@SelectProvider提供在XML映射中的 |
@ResultType |
Method |
N/A |
当使用结果处理器时启用此注解。这种情况下,返回类型为void,所以Mybatis必须有一种方式决定对象的类型,用于构造每行数据。如果有XML的结果映射,使用@ResultMap注解。如果结果类型在XML的节点中指定了,就不需要其他的注解了。其他情况下则使用此注解。比如,如果@Select注解在一个方法上将使用结果处理器,返回类型必须是void并且这个注解(或者@ResultMap)是必须的。这个注解将被忽略除非返回类型是void。 |
@Flush |
方法 |
N/A |
如果这个注解使用了,它将调用定义在Mapper接口中的SqlSession#flushStatements()方法。(Mybatis 3.3或者以上) |
MyBatis是基于“数据库结构不可控”的思想建立的,也就是我们希望数据库遵循第三范式或BCNF,但实际事与愿违,那么结果集映射就是MyBatis为我们提供这种理想与现实间转换的手段了,而resultMap就是结果集映射的配置标签了。
1.从SQL查询结果到领域模型实体
在深入ResultMap标签前,我们需要了解从SQL查询结果集到JavaBean或POJO实体的过程。
1. 通过JDBC查询得到ResultSet对象
2. 遍历ResultSet对象并将每行数据暂存到HashMap实例中,以结果集的字段名或字段别名为键,以字段值为值
3. 根据ResultMap标签的type属性通过反射实例化领域模型
4. 根据ResultMap标签的type属性和id、result等标签信息将HashMap中的键值对,填充到领域模型实例中并返回
2.使用场景
在项目的实际开发中,有可能会遇到这样两种情况。
1. 实体类中的属性名与列名不相同,不能改但。导致不能自动装配值
2. 多表查询的返回值中可能需要其他对象,或者数组(一对一和一对多)
1.@Results注解
代替的是标签
该注解中可以使用单个@Result注解,也可以使用@Result集合
@Results({@Result(),@Result()})或@Results(@Result())
注意:使用注解是若报出org.apache.ibatis.binding.BindingException:Invalid bound statement (not found):接口全类名.方法名
可能是使用@Results注解时忘记使用@Select注解
2.@Resutl注解
代替了
@Result 中 属性介绍:
column 数据库的列名
Property需要装配的属性名
one 需要使用的@One注解(@Result(one=@One)()))
many 需要使用的@Many注解(@Result(many=@many)()))
3.@One注解(一对一)
代替了
@One注解属性介绍:
select 指定用来多表查询的sqlmapper
fetchType会覆盖全局的配置参数lazyLoadingEnabled。。
使用格式:
@Result(column=" ",property="",one=@One(select=""))
4.@Many注解(多对一)
代替了<Collection>标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。
注意:聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList)但是注解中可以不定义;
使用格式:
@Result(property="",column="",many=@Many(select=""))
1.@Results与 @Result的使用
@Result是最简单的映射,指定@Resul将基本数据库表字段,装配到实体类属性的映射。
column是数据库表的列名
property实体的字段
(在注解中返回,是根据方法的返回类型来定的。)
实体字段 表的列名 sid stuid sname stuname gid gid grade grade |
对应的接口映射 |
@Results({@Result(column="stuid",property="sid"),@Result(column="stuname",property="sname")}) @Select("select * from student where gid=#{0}") public List |
(如:该方法中表中数据的返回类型就是根据list中的Student来决定的)
id、result语句属性配置细节:
属性 |
描述 |
|
property |
需要映射到JavaBean 的属性名称。 |
|
column |
数据表的列名或者标签别名。 |
|
javaType |
一个完整的类名,或者是一个类型别名。如果你匹配的是一个JavaBean,那MyBatis 通常会自行检测到。然后,如果你是要映射到一个HashMap,那你需要指定javaType 要达到的目的。 |
|
jdbcType |
数据表支持的类型列表。这个属性只在insert,update 或delete 的时候针对允许空的列有用。JDBC 需要这项,但MyBatis 不需要。如果你是直接针对JDBC 编码,且有允许空的列,而你要指定这项。 |
|
typeHandler |
使用这个属性可以覆写类型处理器。这项值可以是一个完整的类名,也可以是一个类型别名。 |
|
支持的JDBC类型
为了将来的引用,MyBatis支持下列JDBC 类型,通过JdbcType 枚举:
BIT,FLOAT,CHAR,TIMESTAMP,OTHER,UNDEFINED,TINYINT,REAL,VARCHAR,BINARY,BLOB,NVARCHAR,SMALLINT,DOUBLE,LONGVARCHAR,VARBINARY,CLOB,NCHAR,INTEGER,NUMERIC,DATE,LONGVARBINARY,BOOLEAN,NCLOB,BIGINT,DECIMAL,TIME,NULL,CURSOR
2.@One(association联合)
联合元素用来处理“一对一”的关系。
两个属性:
1. select: 执行一个其它映射的SQL 语句返回一个Java实体类型。较灵活;
2. fetchType会覆盖全局的配置参数lazyLoadingEnabled。。
注意:select=指定查询语句,如果查询语句在其他的namespace中,则需要写全namespace.方法id
例如:在获取某个学生的信息的同时又想获取所属班级的所有信息
学生实体中的字段 |
package cn.et.fuqiang.resultMap.entity;
public class Student { private Integer sid;//学生id private String sname; //学生姓名 private Integer gid; //班级id
private Grade grade; //所属班级的所有信息 }
|
班级实体的字段 |
package cn.et.fuqiang.resultMap.entity;
import java.util.List;
public class Grade { private Integer id;//班级id private String gname;//班级名称 public Grade() { super(); // TODO Auto-generated constructor stub }
}
|
在Student的字段中,Grade班级实体,需要查询班级表才能获取,学生表中没有该列(所以需要使用多表查询中,一对一关系,的 @Result中的one属性 和@One注解)
@Results({ @Result(column="stuid",property="sid"), @Result(column="stuname",property="sname"), @Result(column="gid",property="grade",one=@One(select="cn.et.fuqiang.resultMap.annotation.GradeAnnotationInterface.gradeInStudent"))}) @Select("select * from student") public List |
解析: 因为表中的字段与实体中的不同,所以使用了这两个注解,将表的列与实体的属 @Result(column="stuid",property="sid"), @Result(column="stuname",property="sname"), 因为实体中还有一个班级实体属性,但表中没有该字段,所以就要用多表查询 @Result(column="gid",property="grade",one=@One(select="cn.et.fuqiang.resultMap.annotation.GradeAnnotationInterface.gradeInStudent")) (这里的property是实体中的字段,而column=gid就相当于关联的条件了 而select就是该关联条件对应的查询方法)
|
在namespace="cn.et.fuqiang.resultMap.annotation.GradeAnnotationInterface下的方法 |
@Results(@Result(property="id",column="gid")) @Select("select * from grade where gid=#{0}") public Grade gradeInStudent(); |
因为注解中已经指定了多表查询所以当调用指定了该注解的方法时多表查询的值会自动装配到对应的要返回实体属性中
以上代码写为转换为普通的sql语句
select s.*,g.*from student s inner join grade g ons.gid=g.gid
两个属性:
1. select: 执行一个其它映射的SQL 语句返回一个Java实体类型。较灵活;
2. fetchType会覆盖全局的配置参数lazyLoadingEnabled。。
例如,一个班级有多个学生。
学生实体中的字段 |
package cn.et.fuqiang.resultMap.entity;
public class Student { private Integer sid;//学生id private String sname; //学生姓名 private Integer gid; //班级id }
|
班级实体的字段 |
package cn.et.fuqiang.resultMap.entity;
import java.util.List;
public class Grade { private Integer id;//班级id private String gname;//班级名称 private List public Grade() { super(); // TODO Auto-generated constructor stub }
}
|
|
在Grade实体中有个List
@Results({ @Result(property="id",column="gid"), @Result(property="students",column="gid",many=@Many(select="cn.et.fuqiang.resultMap.annotation.StudentAnnotationInterface.queryStudentByGid"))}) @Select("select * from grade") public List |
解析: 因为表中的字段与实体中的不同,所以使用了这个注解,将表的列与实体的属性对应 @Result(property="id",column="gid") 因为实体中还有一个学生List实体属性,但表中没有该字段,所以就要用多表查询来填充其中的值 @Result(property="students",column="gid",many=@Many(select="cn.et.fuqiang.resultMap.annotation.StudentAnnotationInterface.queryStudentByGid"))
|
在namespace="cn.et.fuqiang.resultMap.annotation.StudentAnnotationInterface"下的查询语句和类型 |
|
@Results({@Result(column="stuid",property="sid"),@Result(column="stuname",property="sname")}) @Select("select * from student where gid=#{0}") public List |
(这里的property是实体中的字段,而column=gid就相当于关联的条件了 而select就是该关联条件对应的查询方法)
|
(这里的property是实体中的字段,而column=gid就相当于关联的条件了
而select就是该关联条件对应的查询方法)
所有代码实现
Jdbc连接四要素配置
url=jdbc:oracle:thin:@localhost:1521:orcl
user=mybatis
password=mybatis
driverClass=oracle.jdbc.OracleDriver
mybatis配置
xml version="1.0" encoding="UTF-8"?>
两个接口映射代码
StudentAnnotationInterface.java
package cn.et.fuqiang.resultMap.annotation;
import java.util.List;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import cn.et.fuqiang.resultMap.entity.Grade;
import cn.et.fuqiang.resultMap.entity.Student;
/**
*使用注解应用resultMap
*@author Administrator
*
*/
public interface StudentAnnotationInterface {
/**
* 简单的使用resultMap
* @Results注解相当于 mapper.xml文件中 resultMap标签
* 其中可以包括多个@Result注解
* 如果只有一个的话可直接写
* 多个的话必须用{@Result(),@Result()} 中间用","号隔开
* 注意:@Select注解不能忘记 如果忘记会报出
* org.apache.ibatis.binding.BindingException: Invalid bound statement (notfound):接口全类名.方法名
*
* @param id
* @return
*/
@Results({@Result(column="stuid",property="sid"),@Result(column="stuname",property="sname")})
@Select("select * from student wherestuid=#{0}")
public Student queryStudentById(Integer sid);
/**
* 使用resultMap实现多表查询
* 在@Result注解中可以使用多表查询
*
* 有两个 属性 many(一对多)和 one(一对一)
*
* 一对一的使用
*@Results({@Result(column="stuid",property="sid"),@Result(column="stuname",property="sname"),@Result(column="gid",property="grade",one=@One(select="cn.et.fuqiang.resultMap.annotation.GradeAnnotationInterface.gradeInStudent"))})
property=实体类中的属性名 ,
column=是通过那个列关联
one=@one(select=namespace.方法名 指定查询的方法 ,如果在其他接口中必须指定 全类名.方法名)
对应查询的的方法
如果表名与字段名不符必须先声明如果相同可以不设置
@Results(@Result(property="id",column="gid"))
查询语句相当于关联查询 ,传入的值是指定的关联的列传入的 column 所以方法中不用传
@Select("select * from grade where gid=#{0}")
public Grade gradeInStudent();
* @return 一对多返回指定的对象类型
*/
@Results({@Result(column="stuid",property="sid"),@Result(column="stuname",property="sname"),@Result(column="gid",property="grade",one=@One(select="cn.et.fuqiang.resultMap.annotation.GradeAnnotationInterface.gradeInStudent"))})
@Select("select * from student")
public List<Student> queryAllStudent();
@Results({@Result(column="stuid",property="sid"),@Result(column="stuname",property="sname")})
@Select("select * from student wheregid=#{0}")
public List<Student> queryStudentByGid();
}
GradeAnnotationInterface.java
package cn.et.fuqiang.resultMap.annotation;
import java.util.List;
import org.apache.ibatis.annotations.Many;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.SelectProvider;
import cn.et.fuqiang.resultMap.entity.Grade;
import cn.et.fuqiang.resultMap.entity.Student;
public interface GradeAnnotationInterface {
/**
* 简单的使用resultMap
* @Results注解相当于 mapper.xml文件中 resultMap标签
* 其中可以包括多个@Result注解
* 如果只有一个的话可直接写
* 多个的话必须用{@Result(),@Result()} 中间用","号隔开
* 注意:@Select注解不能忘记 如果忘记会报出
* org.apache.ibatis.binding.BindingException: Invalid bound statement (notfound):接口全类名.方法名
*
* @param id
* @return
*/
@Results(@Result(property="id",column="gid"))
@Select("select * from grade wheregid=#{0}")
public Grade queryGradeById(Integer id);
@Results(@Result(property="id",column="gid"))
@Select("select * from grade wheregid=#{0}")
public Grade gradeInStudent();
/**
* 使用resultMap实现多表查询
* 在@Result注解中可以使用多表查询
*
* 有两个 属性 many(一对多)和 one(一对一)
*
* 一对多的使用
*@Result(property="students",column="gid",many=@Many(select="cn.et.fuqiang.resultMap.annotation.StudentAnnotationInterface.queryStudentByGid"))
property=实体类中的属性名 ,
column=是通过那个列关联
many=@Many(select=namespace.方法名 指定查询的方法 ,如果在其他接口中必须指定 全类名.方法名)
对应查询的的方法
如果表名与字段名不符必须先声明如果相同可以不设置
@Results({@Result(column="stuid",property="sid"),@Result(column="stuname",property="sname")})
查询语句相当于关联查询 ,传入的值是指定的关联的列传入的 column 所以方法中不用传
@Select("select * from student where gid=#{0}")
public List
* @return 一对多返回的集合类型
*/
@Results({@Result(property="id",column="gid"),@Result(property="students",column="gid",many=@Many(select="cn.et.fuqiang.resultMap.annotation.StudentAnnotationInterface.queryStudentByGid"))})
@Select("select * from grade")
public List<Grade> queryAllGrade();
}
两个实体类
Grade
package cn.et.fuqiang.resultMap.entity;
import java.util.List;
public class Grade {
private Integer id;//班级
private String gname;//班级名称
private List<Student> students;//该班级下所有的学生
public Grade() {
super();
// TODOAuto-generated constructor stub
}
public Grade(Integer id, String gname) {
super();
this.id = id;
this.gname = gname;
}
public String getGname() {
return gname;
}
public void setGname(String gname) {
this.gname = gname;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<Student> getStudents() {
return students;
}
public void setStudents(List<Student> students) {
this.students = students;
}
}
Student
package cn.et.fuqiang.resultMap.entity;
public class Student {
private Integer sid;//学生id
private String sname;//学生姓名
private Integer gid;//班级id
private Grade grade;//所属班级的所有信息
public Student() {
super();
// TODOAuto-generated constructor stub
}
public Student(Integer sid, String sname, Integer gid, Grade grade) {
super();
this.sid = sid;
this.sname = sname;
this.gid = gid;
this.grade = grade;
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Integer getGid() {
return gid;
}
public void setGid(Integer gid) {
this.gid = gid;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
}
Test实体类
package cn.et.fuqiang.resultMap.annotation;
import java.io.InputStream;
import java.util.List;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import cn.et.fuqiang.resultMap.entity.Grade;
import cn.et.fuqiang.resultMap.entity.Student;
public class ResultMapAnnotationTest {
private static SqlSession session;
private static GradeAnnotationInterface gradeInter=null;
private static StudentAnnotationInterfacestudentInter=null;
static{
//mybatis的配置文件
String resource = "mybatis.xml";
//使用类加载器加载mybatis的配置文件(它也加载关联的映射文件)
InputStream is =ResultMapAnnotationTest.class.getResourceAsStream(resource);
//构建sqlSession的工厂
SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);
//使用MyBatis提供的Resources类加载mybatis的配置文件(它也加载关联的映射文件)
//Readerreader = Resources.getResourceAsReader(resource);
//构建sqlSession的工厂
//SqlSessionFactorysessionFactory = new SqlSessionFactoryBuilder().build(reader);
//创建能执行映射文件中sql的sqlSession
session = sessionFactory.openSession();
gradeInter=session.getMapper(GradeAnnotationInterface.class);
studentInter=session.getMapper(StudentAnnotationInterface.class);
}
//@Test
public void queryGradeTest(){
Grade grade=gradeInter.queryGradeById(1);
System.out.println(grade.getId()+"------"+grade.getGname());
}
//@Test
public void queryStudentTest(){
Student student=studentInter.queryStudentById(1);
System.out.println(student.getSid()+"------"+student.getSname());
}
@Test
public void queryGradeInStudent(){
List<Student> students=studentInter.queryAllStudent();
for (Student stu : students) {
System.out.println(stu.getSid()+"------"+stu.getSname()+"-------"+stu.getGrade().getGname());
}
}
//@Test
public void queryStudentInGrade(){
List<Grade> students=gradeInter.queryAllGrade();
for (Grade grade : students) {
System.out.println(grade.getGname()+"班"+grade.getStudents().size()+"人");
}
}
}