对比JdbcTempalte,mybatis才能称得上是框架,JdbcTempalte顶多算是工具类,同时,对比Hibernate,Mybatis又有更多的灵活性,算是一种折中方案。html
特色:java
mybatis总体结构:mysql
主配置文件
web
依赖sql
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency>
主配置文件数据库
<?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"> <configuration> <!-- 环境:说明能够配置多个,default:指定生效的环境 --> <environments default="development"> <!-- id:环境的惟一标识 --> <environment id="development"> <!-- 事务管理器,type:类型 --> <transactionManager type="JDBC" /> <!-- 数据源:type-池类型的数据源 --> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <!-- 映射文件 --> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
映射文件:
apache
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace(命名空间):映射文件的惟一标识 --> <mapper namespace="UserMapper"> <!-- 查询的statement,id:在同一个命名空间下的惟一标识,resultType:sql语句的结果集封装类型,这里须要全名 --> <select id="queryUserById" resultType="cn.bilibili.mybatis.pojo.User"> select * from tb_user where id = #{id} </select> </mapper>
使用slf4j12记录日志:
数组
依赖缓存
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.6.4</version> </dependency>
配置文件session
log4j.rootLogger=DEBUG,A1 log4j.logger.org.apache=DEBUG log4j.appender.A1=org.apache.log4j.ConsoleAppender log4j.appender.A1.Target=System.err log4j.appender.A1.layout=org.apache.log4j.PatternLayout log4j.appender.A1.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n
测试方法:
SqlSession sqlSession = null; try { // 指定mybatis的全局配置文件 String resource = "mybatis-config.xml"; // 读取mybatis-config.xml配置文件 InputStream inputStream = Resources.getResourceAsStream(resource); // 构建sqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 获取sqlSession会话 sqlSession = sqlSessionFactory.openSession(); // 执行查询操做,获取结果集。参数:1-命名空间(namespace)+“.”+statementId,2-sql的占位符参数 User user = sqlSession.selectOne("UserMapper.queryUserById", 1L); System.out.println(user); } finally { // 关闭链接 if (sqlSession != null) { sqlSession.close(); } }
大体就是经过SqlSessionFactoryBuilder类得到sql会话工厂,经过sqlSession执行sql语句,而要执行的语句及其映射bean都已经配置在xml里面。须要注意的是mybatis默认开启事务,因此执行增删改时须要手动提交。
mybatis能够直接映射Dao接口,而没必要写其实现类(实际上是mybatis帮咱们实现了啦)
Dao接口:
public interface UserMapper { public User queryUserById(Long id); public List<User> queryUserList(); public void insertUser(User user); public void updateUser(User user); public void deleteUserById(Long id); }
映射配置文件
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 这里命名空间须要和映射的接口对应 --> <mapper namespace="UserMapper"> <!-- id和方法名对应,而且惟一,所以不能有重载方法;返回类型和resultType对应; --> <select id="queryUserById" resultType="cn.bilibili.mybatis.pojo.User"> select * from tb_user where id = #{id} </select> ... </mapper>
Tips:在IDEA中可使用Ctrl + Shift + T
快速建立测试用例
<settings> <!-- 开启驼峰匹配 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
配置文档的顶层结构以下:
具体能够参考官方文档
这里介绍几个经常使用的属性。
属性:properties,能够定义变量,而后使用${变量名}
来取得其值,如:
<properties resource = "jdbc.properties" />
jdbc.properties内容:
driverClass = com.mysql.jdbc.Driver url = jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8 username = root password = 1234
而后即可以经过${driverClass}
来配置驱动了。
包含<setting>
标签,有name
和value
属性
驼峰匹配(默认关闭)
<setting name="mapUnderscoreToCamelCase" value="true"/>
延迟加载(默认关闭)
<setting name="lazyLoadingEnabled" value="true"/>
二级缓存(默认开启)
<setting name="cacheEnabled" value="true"/>
自动映射规则:
name属性 | 描述 | value属性 | 默认value |
---|---|---|---|
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(不管是否嵌套)。 | NONE, PARTIAL, FULL | PARTIAL |
什么沙雕排版😅
类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减小类彻底限定名的冗余。
<typeAliases> <!-- 第一种 --> <typeAlias alias="User" type="com.bilibili.pojo.User"/> <!-- 第二种 --> <package name="com.bilibili.pojo"/> </typeAliases>
第一种是直接配置一个类的别名,第二种是配置扫描一个包下的全部类
或者注解方式配置别名:
@Alias("User") public class User { ... }
以及数据类型的别名(不区分大小写,基本数据类型特殊命名):
别名 | 映射的类型 |
---|---|
_byte | byte |
_long | long |
_short | short |
_int | int |
_integer | int |
_double | double |
_float | float |
_boolean | boolean |
string | String |
byte | Byte |
long | Long |
short | Short |
int | Integer |
integer | Integer |
double | Double |
float | Float |
boolean | Boolean |
date | Date |
decimal | BigDecimal |
bigdecimal | BigDecimal |
object | Object |
map | Map |
hashmap | HashMap |
list | List |
arraylist | ArrayList |
collection | Collection |
iterator | Iterator |
Mybatis能够配置多个环境,可是一个SQLSessionFactory只对应一个环境
<!-- 能够配置多个环境,并设置默认环境 --> <environments default="development"> <!-- 配置环境,id:环境的惟一标识 --> <environment id="development"> <!-- 事务管理器,type:使用jdbc的事务管理器 --> <transactionManager type="JDBC"> <property name="..." value="..."/> </transactionManager> <!-- 数据源,type:池类型的数据源 --> <dataSource type="POOLED"> <property name="driver" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments>
能够经过build方法的重载指定环境:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environmentID); //reader为主配置文件流,environment为environmentID,properties为读取的变量文件,三个参数的任一改变都能改变环境 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environmentID, properties);
若是忽略了环境参数,那么默认环境将会被加载,以下所示:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);
mapper的做用是告诉mybatis去哪里照执行的SQL语句,能够经过下面四种方式:
相对路径的xml文件引用(resources目录):
<!-- 使用相对于类路径的资源引用,相对的是resources目录 --> <mappers> <mapper resource="UserMapper.xml"/> </mappers>
绝对路径的xml文件引用(不推荐):
<!-- 使用彻底限定资源定位符(URL) --> <mappers> <mapper url="file:///D:/UserMapper.xml"/> </mappers>
经过接口路径:
<!-- 使用映射器接口实现类的彻底限定类名 --> <mappers> <mapper class="com.bilibili.mapper.UserMapper"/> </mappers>
此方式条件:
经过接口所在包的路径
<!-- 将包内的映射器接口实现所有注册为映射器 --> <mappers> <package name="com.bilibili.mapper"/> </mappers>
注意事项
使用动态代理的方式实现接口映射时,mapper.xml文件的命名空间必须是接口的全限定名,而且不能使用typeAliases
别名,由于别名是针对Java Bean的
前两种为直接指定xml文件位置,所以对xml路径没有什么要求。
后两种为指定接口而后寻找xml文件,所以xml须要在和接口相同的路径(相对resources),而且!idea中resources目录不能使用.
(点)来创建多级目录,须要使用/
或者\
来创建多级目录!!!
映射文件的结构:
示例:
<select id="queryUserById" resultType="User"> SELECT * FROM user WHERE ID = #{id} </select>
示例:
<insert id = "addUser" useGeneratedKeys = "true" keyColumn = "id" keyProperty = "id" parameterType = "User"> INSERT INTO user ( id, username, password ) VALUES ( NULL, #{userName}, #{password} ) </insert>
示例:
<update id = "updateUserById" parameterType = "User"> UPDATE user SET username = #{username}, password = #{password} WHERE id = #{id} </update> <delete id = "deleteUserById" parameterType = "long"> DELETE FROM user WHERE id = #{id} </delete>
CRUD都有个parameterType标签,用来指定接受的参数类型。
接收参数有两种方式:
#{ }
预编译,相似占位符${ }
非预编译,直接sql拼接,不能防止SQL注入,通常用来接收表名等属性参数类型有三种:
基本数据类型
当sql方法的参数是多个时:
例如queryUserByUserNameAndPassword(String username, String password)
这种两个参数时,能够这样接收参数:
<select id="queryUserByUserNameAndPassword" resultType="User"> SELECT * FROM user WHERE username = #{0}/#{arg0}/#{param1} AND password = #{1}/#{arg1}/#{param2} </select>
不能见名知意的变量不是好方法,因此咱们的解决方案是添加@Param
注解:
queryUserByUserNameAndPassword(@Param(username) String username, @Param(password)String password)
<select id="queryUserByUserNameAndPassword" resultType="User"> SELECT * FROM user WHERE username = #{username} AND password = #{password} </select>
这样就能够见名知意了。
HashMap参数
示例:
User loginMap(Map<String,String> map); /****************************************/ Map<String, String> map = new HashMap<>(); map.put("userName","zhangsan"); map.put("password","123456"); User user = userMapper.loginMap(map);
xml中和注解用法相似:
<select id="loginMap" resultType="User"> SELECT * FROM user WHERE username = #{userName} AND password = #{password} </select>
Pojo
mapper.xml用法不变,xml是经过Getter方法来获取值的。
${}
的用法
一个参数时,默认状况下使用${value}
接收数据。可是这样不能见名知意。
一样使用@Param()
注解。
${ }
和#{ }
#{ }
${ }
SQL拼接(不能防止SQL注入)
$
还有一个问题是,在主配置文件中,使用${driver}
获取资源文件的驱动或者url等数据,若是用户的password属性和资源文件中的password属性同名时,此时会读取资源文件中的password而不会使用传入的参数password,解决方法是在资源文件中的属性都加入前缀:
jdbc.driverClass = com.mysql.jdbc.Driver jdbc.url = jdbc:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=utf8 jdbc.username = root jdbc.password = 1234
固然,主配置文件里面的引用也要修改。
resultMap是mybatis中最强大的特性,能够很方便的解决下面两个问题:
简单映射示例:
在映射文件中配置自定义ResultMap:
<resultMap id="userResultMap" type="User" autoMapping="true"> <!--配置主键映射关系,配置主键能够增长查询效率--> <id column="id" property="id"></id> <!--配置普通字段的映射关系--> <result column="user_name" property="userName"></result> </resultMap>
autoMapping
属性:
在查询语句中使用自定义映射:
<!-- resultMap属性:引用自定义结果集做为数据的封装方式 --> <select id="queryUserById" resultMap="userResultMap"> select * from tb_user where id = #{id} </select>
一对一映射:
当订单(Order)对象内有用户(User)属性时,以前的状况咱们是不能一次查询出来的,可是有了映射即可以很方便的查询:
<!-- id:惟一标识,type:返回类型,autoMapping:自动映射 --> <resultMap id="orderResultMapper" type="Order" autoMapping="true"> <!-- 主键映射 --> <id column="id" property="id"/> <!-- 通常属性映射 --> <result column="order_number" property="orderNumber"/> <result column="user_id" property="userId"/> <!-- 一对一映射,property:属性,javaType:属性类型 --> <association property="user" javaType="User" autoMapping="true"> <!-- 主键映射,写法和resultMap同样 --> <id column="user_id" property="id"/> </association> </resultMap> <select id="queryOrderByOrderNumber" resultMap="orderResultMapper"> SELECT * FROM tb_order o, tb_user u WHERE o.user_id = u.id AND o.order_number = #{orderNumber} </select>
一对多映射
当一个订单内有多个信息时,即Order类中持有List<OrderDetail>
,即可以进行一对多映射。
这里的订单能够理解为多个商品一次下单,这一订单中有多个OrderDetail,每一个OrderDetail对应一个商品
<resultMap id="orderResultMapper2" type="Order" autoMapping="true"> <!-- 主键的字段使用SQL语句的中的别名 --> <id column="oid" property="id"/> <association property="user" javaType="User" autoMapping="true"> <id column="uid" property="id"/> </association> <!-- 一对多映射,property:类中的属性,javaType:该属性对应的Java类型,ofType:该属性存储的类型,也就是泛型 --> <collection property="orderDetailList" javaType="list" ofType="OrderDetail" autoMapping="true"> <id column="did" property="id"/> </collection> </resultMap> <select id="queryOrderAndUserAndOrderDetailsByOrderNumber" resultMap="orderResultMapper2"> <!-- 当表数量较多时,须要指定不一样表主键的别名来区分 --> SELECT *, o.id oid, detail.id did, u.id uid FROM tb_order o, tb_orderdetail detail, tb_user u where o.order_number = #{orderNumber} AND o.user_id = u.id AND detail.order_id = o.id; </select>
注意,当表数量较多时,须要指定不一样表主键的别名来区分。
多对多映射
每一个OrderDetail对应一个商品,这时即可以添加映射:
<resultMap id="orderResultMapper3" type="Order" autoMapping="true"> <id column="oid" property="id"/> <association property="user" javaType="User" autoMapping="true"> <id column="uid" property="id"/> </association> <collection property="orderDetailList" javaType="list" ofType="OrderDetail" autoMapping="true"> <id column="did" property="id"/> <!-- 添加一对一映射 --> <association property="item" javaType="Item" autoMapping="true"> <id column="iid" property="id"/> </association> </collection> </resultMap> <select id="queryOrderAndUserAndOrderDetailAndItemByOrderNumber" resultMap="orderResultMapper3"> SELECT *, o.id oid, detail.id did, u.id uid, item.id iid FROM tb_order o, tb_user u, tb_orderdetail detail, tb_item item where o.order_number = #{orderNumber} and o.user_id = u.id and detail.order_id = o.id and detail.item_id = item.id </select>
继承
从上面的代码能够看出这样映射虽然很方便,可是代码存在冗余的状况:
图中的代码咱们已经在其余映射中配置过了,当后面的映射须要这一段时,咱们即可以使用继承。
修改后的第三个映射的resultMap为:
<!-- 添加extends属性,即可以映射重用 --> <resultMap id="orderResultMapper3" type="Order" autoMapping="true" extends="orderResultMapper"> <collection property="orderDetailList" javaType="list" ofType="OrderDetail" autoMapping="true"> <id column="did" property="id"/> <association property="item" javaType="Item" autoMapping="true"> <id column="iid" property="id"/> </association> </collection> </resultMap>
延迟加载
延迟加载是指当咱们须要哪部分数据时,而后再去查。
上面的几个映射,每次查询时会一股脑将数据所有查询出来,即便不须要的数据也会查询(由于只有一条语句)。当咱们查询订单时,须要用户信息的时候再去查,所以SQL语句须要拆成两条。
主配置文件中开启延迟加载:
<settings> <!-- 开启延迟加载(默认关闭) --> <setting name="lazyLoadingEnabled" value="true" /> <!-- 关闭使用任意属性,就加载延迟对象的功能。(在3.4.1及以前的版本默认值为 true,3.4.1以后的版本不须要配置此项) --> <setting name="aggressiveLazyLoading" value="false"/> </settings>
延迟加载示例:
<resultMap id="orderLazyUser" type="Order" autoMapping="true"> <id column="id" property="id"/> <!-- property:属性,select:延迟加载对象依赖的SQL语句,column:传进去的参数 --> <association property="user" select="queryUserById" column="user_id" autoMapping="true"> <id column="id" property="id"/> </association> </resultMap> <select id="queryOrderLazyUser" resultMap="orderLazyUser"> SELECT * FROM tb_order where order_number = #{orderNumber} </select> <select id="queryUserById" resultType="User"> SELECT * FROM tb_user WHERE id = #{id} </select>
若是报错须要添加cglib依赖(3.3以上不须要添加此依赖):
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency>
对于使用很频繁的SQL语句,能够单独抽离出来进行复用。
在同一个mapper.xml中:
<sql id = "testSql"> id, username, password, ... </sql> <select id = "queryAllUser" resultType = "User"> SELECT <include refid = "testSql"></include> FROM user </select>
可是这样只能在一个mapper文件中使用,所以咱们能够把常用的写入在一个mapper文件中:
CommonSQL.xml文件:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="CommonSQL"> <sql id="testSql"> id, user_name, password, ... </sql> </mapper>
在主配置中引入:
<mappers> <mapper resource = "CommonSQL" /> </mappers>
而后就能够在mapper中使用:
<select id = "queryAllUser" resultType = "User"> SELECT <include refid = "CommonSQL.testSql"></include> FROM user </select>
即refid = "namespace.sqlID"
变一下便可。
感受这个好麻烦啊,由于这些字符在SQL中出现的频率过高了
符号 | 转义 |
---|---|
< | < |
> | > |
& | & |
' | ' |
" | " |
或者使用CDATA:<![CDATA[]]>
真是反人类。。。
mybatis中也能够动态拼接SQL语句(可是在xml中写SQL太难受了),支持ognl表达式(Struts不就是由于这东西才被黑客利用爆出漏洞死掉的么,还用。。。)
if
示例:
<select id="queryMaleUserByUserNameOrId" resultType="User" > select * from tb_user where sex = 1 <!-- if标签,条件判断,test属性编写ognl表达式 --> <if test="userName != null and userName.trim() != ''" > and user_name like #{userName} </if> <if test="id != null and id.trim() != '' " > and id like #{id} </if> </select>
上面的SQL能够经过用户名或者ID来查询。
choose, when, otherwise
至关于switch, case, default,只不过自动break,一旦有一个when成立,后续的when都再也不执行。
示例:
<select id="queryMaleUserByIdOrUserName" resultType="User"> select * from tb_user where sex = 1 <choose> <when test="id != null and id.trim()!='' " > and id like #{id} </when> <when test="userName != null and userName.trim()!=''" > and user_name like #{userName} </when> <otherwise> and active = 1 </otherwise> </choose> </select>
上面的SQL意思是若是提供了id就用id查询,没提供id就用用户名查询,都没有的话则查询全部的激活用户。
where, set, trim
第一个查询若是把性别也改成动态的:
<select id="queryUserByUserNameOrId" resultType="User" > select * from tb_user where <if test = "sex != null and id.trim() != '' " > sex = #{sex} </if> <if test = "userName != null and userName.trim() != '' " > and user_name like #{userName} </if> <if test = "id != null and id.trim() != '' " > and id like #{id} </if> </select>
若是sex参数为空的话这条SQL语句就变成了这样:
select * from tb_user where and user_name like #{userName} and id like #{id}
有点逗比是否是,此时<where>
标签的做用就体现出来了:
<select id="queryUserByUserNameOrId" resultType="User" > select * from tb_user <where> <if test = "sex != null and id.trim() != '' " > sex = #{sex} </if> <if test = "userName != null and userName.trim() != '' "> and user_name like #{userName} </if> <if test = "id != null and id.trim() != '' " > and id like #{id} </if> </where> </select>
where 元素只会在至少有一个子元素的条件返回 SQL 子句的状况下才去插入“WHERE”子句。并且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
若是 where 元素没有按正常套路出牌,咱们能够经过自定义 trim 元素来定制 where 元素的功能。好比,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND | OR "> ... </trim>
<set>
标签和where相似:
<update id = "changePasswordOrUserName"> UPDATE user <set> <if test = "password != null"> password = #{password}, </if> <if test = "username != null"> username = #{username} </if> </set> WHERE id = #{id} </update>
set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,由于用了条件语句以后极可能就会在生成的 SQL 语句的后面留下这些逗号。
上面的句子就至关于:
<trim prefix = "SET" suffixOverrides = ","> ... </trim>
foreach
动态 SQL 的另一个经常使用的操做需求是对一个集合进行遍历,一般是在构建 IN 条件语句的时候。好比:
<select id="selectUserIn" resultType="User"> SELECT * FROM user WHERE id in <foreach item="item" index="index" collection="list" open="(" separator="," close=")"> #{item} </foreach> </select>
能够迭代Collection、数组、Map,当参数为Map时,index为键,item为值。
一级缓存:
缓存是sqlSession级别,默认开启(没法关闭?),可使用session.clearCache()
方法清除缓存。对于同一条数据再次查询会查询缓存里的数据。须要注意的是增、删、改语句都会清除缓存,即便是不一样数据。
二级缓存:
二级缓存须要Pojo对象实现Serializable接口,能够实现不一样sqlSession间公用缓存。当第一个sqlSession查询一条数据后调用sqlSession.close()
方法会将数据添加到二级缓存,第二个sqlSession再次查询同一数据时会使用缓存。(数据增删改一样会状况二级缓存)
开启二级缓存:
<settings> <!-- 开启二级缓存,默认是开启的 --> <setting name="cacheEnabled" value="true"/> </settings>
注解须要在主配置文件中添加包扫描。
给参数添加别名(估计是由于反射不能获取接口中声明的局部变量名称)
示例:
User queryUserById(@Param("id") Integer id);
@Select("select * from tb_user where id = #{id}") User queryUserById(@Param("id") Integer id); @Delete("DELETE FROM tb_user WHERE id = #{id}") int deleteUserById(@Param("id") Integer id); @Update("UPDATE tb_user SET name = #{name} WHERE id = #{id}") int updateNameById(@Param("name") String name, @Param("id") Integer id); /* @Options:参数配置 useGeneratedKeys:主键回写(默认false) keyColumn:主键字段名 keyProperty:主键属性(默认id) */ @Insert("insert into tb_user(user_name,password,name) values (#{userName}, #{password}, #{name}) ") @Options(useGeneratedKeys = true,keyColumn = "id") int addUser(User user);
@Select("select id uid,user_name,password pwd from tb_user where id=#{id}") /* Results:定义结果集,内部是一个Result注解的数组 Result:结果集映射关系 column:列名 property:属性名 */ @Results({ @Result(column = "uid",property = "id"), @Result(column = "user_name",property = "userName"), @Result(column = "pwd",property = "password") }) public User findUserByIdResultMap(@Param("id") Long id);
@Select("select * from tb_order where order_number = #{orderNumber}") @Results({ /* 一对一映射调用其余接口的方法 column:传入的参数 property:返回对应的属性 one表明一对一,值为@One注解 @One:一对一注解 select:引用的查询方法 */ @Result(column = "user_id",property = "user", one = @One(select = "com.bilibili.mybatis.mapper.UserMapper.queryUserById")) }) Order queryOrderAndUserByOrderNumber(@Param("orderNumber") String orderNumber);
UserMapper接口:
//被调用的方法 public interface UserMapper { @Select("select * from tb_user where id = #{id}") User queryUserById(@Param("id") Integer id); }
@Select("select * from tb_order where order_number = #{orderNumber}") @Results({ @Result(column = "id", property = "id"), //一对一映射 @Result(column = "user_id",property = "user",one = @One(select = "com.bilibili.mybatis.mapper.UserMapper.queryUserById")), /* 一对多映射 many表明一对多,值为@Many注解 @Many:一对多注解 select:引用的方法 */ @Result(column = "id", property = "orderDetailList", many = @Many(select = "com.bilibili.mybatis.mapper.OrderDetailMapper.queryOrderDetailsByOrderId" )) }) Order queryOrderAndUserAndOrderDetailsByOrderNumber(@Param("orderNumber") String orderNumber);
OrderDetailMapper接口:
//引用的方法 public interface OrderDetailMapper { @Select("select * from tb_orderdetail where order_id = #{oid}") @Results({ @Result(column = "id",property = "id"), }) List<OrderDetail> queryOrderDetailsByOrderId(@Param("oid") Integer oid); }
多对多只须要在被引用的一对多方法里添加一对一便可:
public interface OrderDetailMapper { @Select("select * from tb_orderdetail where id = #{id}") OrderDetail queryOrderDetailById(@Param("id") Integer id); @Select("select * from tb_orderdetail where order_id = #{oid}") @Results({ @Result(column = "id",property = "id"), //添加一对一 @Result(column = "item_id", property = "item", one = @One(select = "com.bilibili.mybatis.mapper.ItemMapper.queryItemById")) }) List<OrderDetail> queryOrderDetailsByOrderId(@Param("oid") Integer oid); }
在@One注解里添加fetchType
属性:
@Select("select * from tb_order where order_number = #{orderNumber}") @Results({@Result(column = "user_id", property = "user", one = @One( select = "com.bilibili.mybatis.mapper.UserMapper.queryUserById", //添加延迟加载属性 fetchType = FetchType.LAZY )) }) Order queryOrderAndUserByOrderNumber(@Param("orderNumber") String orderNumber);
fetchType属性会覆盖全局属性,可选值有:
注解方式使用动态SQL的话须要一个类来专门构建SQL语句:
//用来构建SQL语句的类 public class UserSqlBuilder { //想要在匿名内部类中访问须要将变量声明为为final public String queryUserByConditions(final User user) { //第一种方式,直接SQL拼接 StringBuilder sqlSb = new StringBuilder("SELECT * FROM tb_user WHERE 1=1 "); if (user.getUserName() != null && user.getUserName().trim().length() > 0) { sqlSb.append(" AND user_name like #{userName} "); } if (user.getSex() != null) { sqlSb.append(" AND sex = #{sex} "); } return sqlSb.toString(); //第二种方式,使用mybatis提供的类 //注意下面的SELECT、FROM等都是方法,而且是写在构造代码块里的(猛地一看还真没看明白) String sql = new SQL() {{ SELECT("*"); FROM("tb_user"); if (user.getUserName() != null && user.getUserName().trim().length() > 0){ WHERE("user_name like #{userName}"); } if (user.getSex() != null){ WHERE("sex = #{sex} "); } }}.toString(); return sql; } }
接口中的方法为:
public interface UserMapper { //只需将@Select()注解替换为@SelectProvider()便可 //type:提供SQL语句的类,method:提供SQL的方法 @SelectProvider(type = UserSqlBuilder.class,method = "queryUserByConditions") List<User> queryUserByConditions(final User user); }
除了SelectProvider还有:
按需添加便可。
若是@SelectProvider描述的抽象方法没有使用@Param给变量添加别名,而且声明了多个变量,那么提供SQL的类的方法参数须要和接口中的方法同样,也就是声明出全部的变量:
public interface UserMapper { @SelectProvider(type = UserSqlBuilder.class,method = "queryUserByConditions") List<User> queryUserByConditions(final User user, final String name); }
提供SQL的类的方法也须要声明为接口一样的参数:
public class UserSqlBuilder { //提供SQL的方法也须要声明两个变量 public String queryUserByConditions(final User user, final String name){ ... } }
若是接口中的抽象方法使用了@Param参数,那么类中提供SQL的方法即可以用哪一个参数声明哪一个参数:
//接口 public interface UserMapper { @SelectProvider(type = UserSqlBuilder.class,method = "queryUserByConditions") List<User> queryUserByConditions(@Param("user") final User user, @Param("user") final String name); }
提供SQL的类中的方法即可以这样写:
public class UserSqlBuilder { //只须要声明本身须要的类即可以了 public String queryUserByConditions(@Param("user") final User user){ ... } }
若是Mybatis的版本在3.5.1之后,能够将这样简化:
映射的接口:
public interface UserMapper { @SelectProvider(UserSqlProvider.class) List<User> queryUserByConditions(final User user); }
提供SQL的类:
//提供类继承一个超类:ProviderMethodResolver class UserSqlProvider implements ProviderMethodResolver { //这里的方法名须要和接口中的方法名同样 public static String queryUserByConditions(final User user){ ... } }