高级查询
ResultMap
解决实体类属性名与数据库表不一致的问题
用法:
<resultMap id="userResultMap" type="User"><id property="id" column="id"></id><result property="username" column="username"></result><result property="birthday" column="birthday"></result><result property="sex" column="sex"></result><result property="address" column="address"></result></resultMap><select id="findAllResultMap" resultMap="userResultMap">select * from user</select>
property是java的实体的属性名 column是数据库表的列
id用来配置主键,其他列的对应关系用result来完成。
最后把sql语句上的resultType换成resultMap即可
多条件查询
方式一 用arg0 arg1 …/ param1 param2 占位
<select id="findByIdAndUserName1" resultMap="userResultMap">select * from user where id = #{arg0} and username = #{arg1}</select>
不用写parameterType
方式二 加param注解
List<User> findByIdAndUserName2(@Param("id") int id, @Param("username") String name);
<select id="findByIdAndUserName2" resultMap="userResultMap">select * from user where id = #{id} and username = #{username}</select>
方式三 封装一个实体对象来查询
和以前一样,不多说了。
模糊查询
<select id="findByUserName" resultMap="userResultMap" parameterType="string">select * from user where username like #{username}</select><select id="findByUserName2" resultMap="userResultMap" parameterType="string">-- 必须写valueselect * from user where username like '${value}'</select>
其实就是用$还是#的区别。用#可以自动进行java类型到jdbc类型的转换,可以有效防止sql注入。而$就是原样拼接了,没有进行任何转换。
注意在写java代码的时候要加%
InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(stream);SqlSession sqlSession = fac.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = mapper.findByUserName2("%l%");System.out.println(users);
映射配置深入
返回主键
有时我们需要在插入后马上返回主键的值。
方式一
<!-- useGeneratedKeys 返回主键值--><!-- keyProperty 把返回的值封在哪个属性上--><insert id="saveUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address})</insert>
@Testpublic void test8() throws IOException {InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(stream);SqlSession sqlSession = fac.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setUsername("盖伦");user.setBirthday(new Date());user.setSex("男");user.setAddress("召唤师峡谷");System.out.println(user);mapper.saveUser(user);System.out.println(user);sqlSession.commit();sqlSession.close();}
但是他有局限性,只适用于支持主键自增的数据库,比如mysql和sqlserver。oracle不行。
方式二
<insert id="saveUser2" parameterType="User"><selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="int">select last_insert_id();</selectKey>insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address})</insert>
这种方式适用范围广。如果是oracle要把order改成before。order就是这句selectkey在插入前执行还是插入后执行。<br />last_insert_id();是MySQL的函数。
动态sql
当我们要根据不同的条件,来执行不同的 sql 语句的时候,需要用到动态sql
if
简化我们以前用jdbc的时候 先拼接where1=1再一顿if判断的手法。
<select id="findByIdAndUserNameIf" parameterType="User" resultType="User">select * from user-- 相当于where 1=1,没条件就不拼接<where><if test="id != null">and id = #{id}</if><if test="username != null">and username = #{username}</if></where></select>
set
<update id="updateIf" parameterType="User">update user-- set可以去掉最后一个逗号,所以每一行if的sql后面都要加逗号<set><if test="username != null">username = #{username},</if><if test="birthday != null">birthday = #{birthday},</if><if test="sex != null">sex = #{sex},</if><if test="address != null">address = #{address},</if></set>where id = #{id}</update>
foreach
主要是用在 where id in (1,2,3)这类场景之下
注意,如果list是空的,出现的错误会非常奇怪。整个foreach语句都会消失。然后报SQL ERROR。
List<User> findByList(List<Integer> list);List<User> findByArray(Integer[] arr);
<select id="findByList" parameterType="list" resultType="User">select * from user-- foreach里面的collection代表集合元素。如果泛型是基本类型或String,可以直接写list/collection-- open 语句开始部分-- close 语句结束部分-- item 每个元素的变量名-- seprator 分隔符<where><foreach collection="list" open="id in (" close=")" separator="," item="id">#{id}</foreach></where></select>
<select id="findByArray" parameterType="int" resultType="User">select * from user<where><foreach collection="array" open="id in (" close=")" separator="," item="id">#{id}</foreach></where></select>
sql
抽取公共部分用的
<sql id="selectUser">select * from user</sql>使用的时候只需要<select id="findByArray" parameterType="int" resultType="User"><include refid="selectUser"></include><where><foreach collection="array" open="id in (" close=")" separator="," item="id">#{id}</foreach></where></select>
核心配置深入
plugins
MyBatis 可以使用第三方的插件来对功能进行扩展
分页助手PageHelper 是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据
开发步骤:
①导入通用PageHelper 的坐标
②在mybatis 核心配置文件中配置PageHelper 插件
③测试分页数据获取
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>3.7.5</version></dependency><dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>0.9.1</version></dependency>
<!-- 注意:分页助手的插件 配置在通用馆mapper 之前 --><plugin interceptor="com.github.pagehelper.PageHelper"><!-- 指定方言 --><property name="dialect" value="mysql"/></plugin>
@Testpublic void testPageHelper(){//设置分页参数PageHelper.startPage(1,2);List<User> select = userMapper2.select(null);for(User user : select){System.out.println(user);}PageInfo<User> pageInfo = new PageInfo<User>(select);System.out.println("总条数:"+pageInfo.getTotal());System.out.println("总页数:"+pageInfo.getPages());System.out.println("当前页:"+pageInfo.getPageNum());System.out.println("每页显示长度:"+pageInfo.getPageSize());System.out.println("是否第一页:"+pageInfo.isIsFirstPage());System.out.println("是否最后一页:"+pageInfo.isIsLastPage());}
多表查询
多表查询最重要关注两点
一是当前实体类如何表示对方实体类与自己的关系
二是如何进行配置
一对一
<mapper namespace="com.ning.mapper.OrderMapper"><resultMap id="ordersMap" type="com.ning.entity.Orders"><id property="id" column="id"></id><result property="ordertime" column="ordertime"></result><result property="total" column="total"></result><result property="uid" column="uid"></result><!-- property是要封装的属性名, javatype是要封装的属性类型--><association property="user" javaType="com.ning.entity.User"><id property="id" column="uid"></id><result property="username" column="username"></result><result property="birthday" column="birthday"></result><result property="sex" column="sex"></result><result property="address" column="address"></result></association></resultMap><select id="findAllWithUser" resultMap="ordersMap">select * from orders o left join user u on o.uid = u.id</select></mapper>
核心就是这段代码,一个订单对应一个User,把订单对应的User封装到订单的User属性里。
新东西就是association
一个很精妙的手法在id那一行,封装的属性是id,column写的是外键uid。这是为了防止两个表联合查询有两个id列造成混淆导致的。当然也可以通过起别名的方式解决,但这样更简单精妙。
一对多
<resultMap id="userMap" type="com.ning.entity.User"><id property="id" column="id"></id><result property="username" column="username" ></result><result property="birthday" column="birthday" ></result><result property="sex" column="sex" ></result><result property="address" column="address" ></result><collection property="ordersList" ofType="com.ning.entity.Orders"><id property="id" column="oid"></id><result property="ordertime" column="ordertime"></result><result property="total" column="total"></result><result property="uid" column="uid"></result></collection></resultMap><select id="findAllWithOrder" resultMap="userMap">SELECT u.*, o.id oid, o.ordertime, o.total, o.uid FROM orders o RIGHT JOIN USER u ON u.`id` = o.`uid`</select>
User表里要有一个List 名字叫ordersList
多对多
<resultMap id="userRoleMap" type="com.ning.entity.User"><id property="id" column="id"></id><result property="username" column="username" ></result><result property="birthday" column="birthday" ></result><result property="sex" column="sex" ></result><result property="address" column="address" ></result><collection property="roleList" ofType="com.ning.entity.Role"><id property="id" column="rid"></id><result property="rolename" column="rolename"></result><result property="roleDesc" column="roleDesc"></result></collection></resultMap><select id="findAllWithRole" resultMap="userRoleMap">SELECT u.*, r.`id` rid, r.`rolename`, r.`roleDesc` FROM USER u LEFT JOIN sys_user_role ur ON u.`id` = ur.`userid` LEFT JOIN sys_role r ON ur.`roleid` = r.`id`</select>
同样的,需要一个List 存储中间表
嵌套查询
一对一
<resultMap id="orderMap2" type="com.ning.entity.Orders"><id property="id" column="id"></id><result property="ordertime" column="ordertime"></result><result property="total" column="total"></result><result property="uid" column="uid"></result><!-- 如何执行第二条sql? 如何在执行第二条sql的时候把uid进行传递? --><association property="user" javaType="com.ning.entity.User" select="com.ning.mapper.UserMapper.findUserById" column="uid"></association></resultMap><!-- 一对一--><select id="findAllWithUser2" resultMap="orderMap2">select * from orders</select>
注意在association里加了一个 select和一个column。一个代表接下来要执行的sql,一个代表把执行完的sql的结果的哪些列注入进来。<br />select引用了另一个mapper的sql语句。
一对多
<resultMap id="userOrderMap" type="com.ning.entity.User"><id property="id" column="id"></id><result property="username" column="username" ></result><result property="birthday" column="birthday" ></result><result property="sex" column="sex" ></result><result property="address" column="address" ></result><collection property="ordersList" ofType="com.ning.entity.Orders" select="com.ning.mapper.OrderMapper.findOrderById" column="id"></collection></resultMap><select id="findAllWithOrder2" resultMap="userOrderMap">select * from user</select>
道理基本同上
多对多
<resultMap id="userRoleMap2" type="User"><id property="id" column="id"></id><result property="username" column="username" ></result><result property="birthday" column="birthday" ></result><result property="sex" column="sex" ></result><result property="address" column="address" ></result><collection property="roleList" ofType="Role" column="id" select="com.ning.mapper.RoleMapper.findRoleById"></collection></resultMap><select id="findAllWithRole2" resultMap="userRoleMap2">select * from user</select>
