Table of Contents
Table of Contents
前言:
之前将映射器Mapper时,提到实现的方法有 XML映射+接口 和 注解+接口,MyBatis学习之映射器Mapper(接口映射器+xml映射文件)这篇文章就是基于 XML配置+接口。
虽然注解很方便,但注解不好管理,而且因为是写在java代码上,仅仅修改sql语句意味着要重新编译,还有 不好管理,xml映射文件放一起,可以很好管理SQL语句,寻找起来也快。
官方也推荐用XMl映射文件方式,因为复杂的映射,还是XML好理解,也好实现。
1、java 注解简介
MyBatis3构建在全面而且强大的Java 注解(Java annotation)之上。MyBatis注解提供了一种便捷的方式来实现简单映射语句,而不会引入大量的xml配置开销。
注解(annotation),即元数据,就是源代码的元数据。注解在代码中添加信息,可以在后续中更方便的使用这些数据。
java的注解没有行为,只能有数据,主要实现替代配置文件功能。
java注解,按照运行机制则分为三类:
(1)源码注解。只在源码中出现,编译成class文件就不存在了
(2)编译时注解。注解在源码和编译中都存在 例如:Override,Deprected,SuppressWarnings
(3)运行时注解。运行阶段起作用,甚至会影响运行逻辑的注解 例如spring框架中的@Autowired
2、注解与xml取舍
在没有注解之前,xml被广泛的应用于描述元数据,xml的维护越来越糟糕。在需要紧耦合的地方,比xml该容易维护,阅读更方便。在需要比较多参数设置时,使用xml更方便,而在将某个方法声明为服务时这种紧耦合的情况下,比较适合注解。
xml是松耦合的,注解是紧耦合的,对于xml和注解的使用,要具体问题具体分析。
java的注解没有行为,只能有数据,实际上就是一组键值对而已。通过解析类(Parse Class)就能把一个注解设置的键值对都找出来。
3、mybatis 注解
因为最初设计时,MyBatis 是一个 XML 驱动的框架。配置信息是基于 XML 的,而且映射语句也是定义在 XML 中的。而到了 MyBatis 3,就有新选择了。MyBatis 3 构建在全面且强大的基于 Java 语言的配置 API 之上。这个配置 API 是基于 XML 的 MyBatis 配置的基础,也是新的基于注解配置的基础。注解提供了一种简单的方式来实现简单映射语句,而不会引入大量的开销。
MyBatis基于注解的用法,正在变得越来越流行,但是需要注意的是:注解的方式还没有百分百覆盖所有XML标签,所以还是有一点点不足!
注解列表:
注解 | 目标 | 相对应的XML | 描述 |
---|---|---|---|
@CacheNamespace | 类 | <cache> | 为给定的命名空间(比如类)配置缓存。属性:implemetation,eviction,flushInterval,size,readWrite,blocking和properties。 |
@Property | N/A | <property> | 指定属性值或占位符(可以由mybatis-config.xml中定义的配置属性替换)。属性:name,value。(在MyBatis3.4.2+上可用) |
@CacheNamespaceRef | 类 | <cacheRef> | 参照另外一个命名空间的缓存来使用。属性:value,name。如果使用此注释,则应指定value或name属性。value属性指定指定命名空间的java类型(命名空间名称成为指定的java类型的FQCN),对于name属性(此属性自3.4.2起可用)指定命名空间的名称。 |
@ConstructorArgs | 方法 | <constructor> | 收集一组结果传递给一个劫夺对象的构造方法。属性:value,是形式参数的数组。 |
@Arg | N/A |
| 单独的构造方法参数,是ConstructorArgs集合的一部分。属性:id,column,javaType,typeHandler。id属性是布尔值,来标识用于比较的属性,和<idArg>XML元素相似。 |
@TypeDiscriminator | 方法 | <discriminator> | 一组实例值被用来决定结果映射的表现。属性:column,javaType,jdbcType,typeHandler,cases。cases属性就是实例的数组。 |
@Case | N/A | <case> | 单独实例的值和它对应的映射。属性:value,type,results。Results属性是结果数组,因此这个注解和实际的ResultMap很相似,由下面的Results注解指定。 |
@Results | 方法 | <resultMap> | 结果映射的列表,包含了一个特别结果列如何被映射到属性或字段的详情。属性:value,id。value属性是Result注解的数组。这个id的属性是结果映射的名称。 |
@Result | N/A |
| 在列和属性或字段之间的单独结果映射。属性:id,column,property,javaType,jdbcType,typeHandler,one,many。id属性是一个布尔值,表示了应该被用于比较(和在XML映射中的<id>相似)的属性。one属性是单独的联系,和<association>相似,而many属性是对集合而言的,和<collection>相似。它们这样命名是为了避免名称冲突。 |
@One | N/A | <association> | 复杂类型的单独属性值映射。属性:select,已映射语句(也就是映射器方法)的完全限定名,它可以加载合适类型的实例。注意:联合映射在注解API中是不支持的。这是因为Java注解的限制,不允许循环引用。fetchType会覆盖全局的配置参数lazyLoadingEnabled。 |
@Many | N/A | <collection> | 映射到复杂类型的集合属性。属性:select,已映射语句(也就是映射器方法)的全限定名,它可以加载合适类型的实例的集合,fetchType会覆盖全局的配置参数lazyLoadingEnabled。注意联合映射在注解API中是不支持的。这是因为Java注解的限制,不允许循环引用 |
@MapKey | 方法 | 复杂类型的集合属性映射。属性:select,是映射语句(也就是映射器方法)的完全限定名,它可以加载合适类型的一组实例。注意:联合映射在Java注解中是不支持的。这是因为Java注解的限制,不允许循环引用。 | |
@Options | 方法 | 映射语句的属性 | 这个注解提供访问交换和配置选项的宽广范围,它们通常在映射语句上作为属性出现。而不是将每条语句注解变复杂,Options注解提供连贯清晰的方式来访问它们。属性:useCache=true,flushCache=FlushCachePolicy.DEFAULT,resultSetType=FORWARD_ONLY,statementType=PREPARED,fetchSize=-1,timeout=-1useGeneratedKeys=false,keyProperty=”id”,keyColumn=””,resultSets=””。理解Java注解是很重要的,因为没有办法来指定“null”作为值。因此,一旦你使用了Options注解,语句就受所有默认值的支配。要注意什么样的默认值来避免不期望的行为。 |
| 方法 |
| 这些注解中的每一个代表了执行的真实SQL。它们每一个都使用字符串数组(或单独的字符串)。如果传递的是字符串数组,它们由每个分隔它们的单独空间串联起来。这就当用Java代码构建SQL时避免了“丢失空间”的问题。然而,如果你喜欢,也欢迎你串联单独的字符串。属性:value,这是字符串数组用来组成单独的SQL语句。 |
| 方法 |
| 这些可选的SQL注解允许你指定一个类名和一个方法在执行时来返回运行允许创建动态的SQL。基于执行的映射语句,MyBatis会实例化这个类,然后执行由provider指定的方法.该方法可以有选择地接受参数对象.(InMyBatis3.4orlater,it'sallowmultipleparameters)属性:type,method。type属性是类。method属性是方法名。注意:这节之后是对类的讨论,它可以帮助你以干净,容于阅读的方式来构建动态SQL。 |
@Param | Parameter | N/A | 如果你的映射器的方法需要多个参数,这个注解可以被应用于映射器的方法参数来给每个参数一个名字。否则,多参数将会以它们的顺序位置来被命名(不包括任何RowBounds参数)比如。#{param1},#{param2}等,这是默认的。使用@Param(“person”),参数应该被命名为#{person}。 |
@SelectKey | 方法 | <selectKey> | 该注解复制了<selectKey>的功能,用在注解了@Insert,@InsertProvider,@Updateor@UpdateProvider的方法上。在其他方法上将被忽略。如果你指定了一个@SelectKey注解,然后Mybatis将忽略任何生成的key属性通过设置@Options,或者配置属性。属性:statement是要执行的sql语句的字符串数组,keyProperty是需要更新为新值的参数对象属性,before可以是true或者false分别代表sql语句应该在执行insert之前或者之后,resultType是keyProperty的Java类型,statementType是语句的类型,取Statement,PreparedStatement和CallableStatement对应的STATEMENT,PREPARED或者CALLABLE其中一个,默认是PREPARED。 |
@ResultMap | 方法 | N/A | 这个注解给@Select或者@SelectProvider提供在XML映射中的<resultMap>的id。这使得注解的select可以复用那些定义在XML中的ResultMap。如果同一select注解中还存在@Results或者@ConstructorArgs,那么这两个注解将被此注解覆盖。 |
@ResultType | Method | N/A | 当使用结果处理器时启用此注解。这种情况下,返回类型为void,所以Mybatis必须有一种方式决定对象的类型,用于构造每行数据。如果有XML的结果映射,使用@ResultMap注解。如果结果类型在XML的<select>节点中指定了,就不需要其他的注解了。其他情况下则使用此注解。比如,如果@Select注解在一个方法上将使用结果处理器,返回类型必须是void并且这个注解(或者@ResultMap)是必须的。这个注解将被忽略除非返回类型是void。 |
@Flush | 方法 | N/A | 如果这个注解使用了,它将调用定义在Mapper接口中的SqlSession#flushStatements方法。(Mybatis3.3或者以上) |
总体而言,这些常用注解分为三大类:SQL语句映射,结果集映射和关系映射。
3.1、SQL语句映射
- @Insert注解:实现插入功能
@Insert("insert into user(name,sex,age) values(#{name},#{sex},#{age}")
int saveUser(User user);
- @Select注解:实现查询功能 (id = true表示为主键)
@Select("Select * from user")
@Results({
@Result(id = true, column = "id", property = "id"),
@Result(column = "name", property = "name"),
@Result(column = "sex", property = "sex"),
@Result(column = "age", property = "age")
})
List<User> queryAllUser();
- @SelectKey注解:插入后,获取id的值
以mysql为例,mysql在插入一条数据后,如何能获得到这个自增id的值呢?使用select last_insert_id() 可以取到最后生成的主键。
@Insert("insert into user(id,name) values(#{id},#{name})")
@Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
@SelectKey(statement = "select last_insert_id()" ,keyProperty = "id",keyColumn = "id",resultType = int.class,before = false)
public int insert(User user);
备注:before属性,默认是true,在执行插入语句之前,执行select last_insert_id()。如果设置为flase,则在插入这个语句之后,执行select last_insert_id()
- @Update注解:实现更新功能
@Update("update user set name= #{name},sex = #{sex},age =#{age} where id = #{id}")
void updateUserById(User user);
- @Delete注解:实现删除功能
@Delete("delete from user where id =#{id}")
void deleteById(Integer id);
3.2、结果集映射
@Result,@Results,@ResultMap是结果集映射的三大注解。
首先说明一下@Results各个属性的含义,
id为当前结果集声明唯一标识,value值为结果集映射关系,
@Result代表一个字段的映射关系,column指定数据库字段的名称,property指定实体类属性的名称,jdbcType数据库字段类型,
@Result里的id值为true表明主键,默认false;
使用@ResultMap来引用xml中的映射结果集<resultMap id="">,其中value可省略。
- 声明结果集映射关系代码:
@Select({"select id, name, class_id from student"})
@Results(id="studentMap", value={
@Result(column="id", property="id", jdbcType=JdbcType.INTEGER, id=true),
@Result(column="name", property="name", jdbcType=JdbcType.VARCHAR),
@Result(column="class_id ", property="classId", jdbcType=JdbcType.INTEGER)
})
List<Student> selectAll();
- 引用结果集代码:
@Select({"select id, name, class_id from student where id = #{id}"})
@ResultMap(value="studentMap")
Student selectById(integer id);
3.3、关系映射
-
@one注解:用于一对一关系映射
@Select("select * from student")
@Results({
@Result(id=true,property="id",column="id"),
@Result(property="name",column="name"),
@Result(property="age",column="age"),
@Result(property="address",column="address_id",one=@One(select="cn.mybatis.mydemo.mappers.AddressMapper.getAddress"))
})
public List<Student> getAllStudents();
-
@many注解:用于一对多关系映射
@Select("select * from t_class where id=#{id}")
@Results({
@Result(id=true,column="id",property="id"),
@Result(column="class_name",property="className"),
@Result(property="students", column="id", many=@Many(select="cn.mybatis.mydemo.mappers.StudentMapper.getStudentsByClassId"))
})
public Class getClass(int id);
4、最后在mybatis配置文件配置映射器:
- 使用xml映射文件映射SQL语句时:
<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
注意:此种⽅法要求mapper接⼝名称和mapper映射⽂件名称相同,且放在同⼀个⽬录中
- 使用注解映射SQL时:
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
具体地一个接口一个<mapper class>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
自动扫描此包下的所有接口,而不用一个接口一个<mapper class>