高级查询

ResultMap

解决实体类属性名与数据库表不一致的问题

用法:

  1. <resultMap id="userResultMap" type="User">
  2. <id property="id" column="id"></id>
  3. <result property="username" column="username"></result>
  4. <result property="birthday" column="birthday"></result>
  5. <result property="sex" column="sex"></result>
  6. <result property="address" column="address"></result>
  7. </resultMap>
  8. <select id="findAllResultMap" resultMap="userResultMap">
  9. select * from user
  10. </select>

property是java的实体的属性名 column是数据库表的列

id用来配置主键,其他列的对应关系用result来完成。

最后把sql语句上的resultType换成resultMap即可

多条件查询

方式一 用arg0 arg1 …/ param1 param2 占位

  1. <select id="findByIdAndUserName1" resultMap="userResultMap">
  2. select * from user where id = #{arg0} and username = #{arg1}
  3. </select>

不用写parameterType

方式二 加param注解

  1. List<User> findByIdAndUserName2(@Param("id") int id, @Param("username") String name);
  1. <select id="findByIdAndUserName2" resultMap="userResultMap">
  2. select * from user where id = #{id} and username = #{username}
  3. </select>

方式三 封装一个实体对象来查询

和以前一样,不多说了。

模糊查询

  1. <select id="findByUserName" resultMap="userResultMap" parameterType="string">
  2. select * from user where username like #{username}
  3. </select>
  4. <select id="findByUserName2" resultMap="userResultMap" parameterType="string">
  5. -- 必须写value
  6. select * from user where username like '${value}'
  7. </select>

其实就是用$还是#的区别。用#可以自动进行java类型到jdbc类型的转换,可以有效防止sql注入。而$就是原样拼接了,没有进行任何转换。
注意在写java代码的时候要加%

  1. InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
  2. SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(stream);
  3. SqlSession sqlSession = fac.openSession();
  4. UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  5. List<User> users = mapper.findByUserName2("%l%");
  6. System.out.println(users);

映射配置深入

返回主键

有时我们需要在插入后马上返回主键的值。

方式一

  1. <!-- useGeneratedKeys 返回主键值-->
  2. <!-- keyProperty 把返回的值封在哪个属性上-->
  3. <insert id="saveUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
  4. insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address})
  5. </insert>
  1. @Test
  2. public void test8() throws IOException {
  3. InputStream stream = Resources.getResourceAsStream("sqlMapConfig.xml");
  4. SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(stream);
  5. SqlSession sqlSession = fac.openSession();
  6. UserMapper mapper = sqlSession.getMapper(UserMapper.class);
  7. User user = new User();
  8. user.setUsername("盖伦");
  9. user.setBirthday(new Date());
  10. user.setSex("男");
  11. user.setAddress("召唤师峡谷");
  12. System.out.println(user);
  13. mapper.saveUser(user);
  14. System.out.println(user);
  15. sqlSession.commit();
  16. sqlSession.close();
  17. }
  1. 但是他有局限性,只适用于支持主键自增的数据库,比如mysqlsqlserveroracle不行。

方式二

  1. <insert id="saveUser2" parameterType="User">
  2. <selectKey order="AFTER" keyColumn="id" keyProperty="id" resultType="int">
  3. select last_insert_id();
  4. </selectKey>
  5. insert into user(username, birthday, sex, address) values(#{username}, #{birthday}, #{sex}, #{address})
  6. </insert>
  1. 这种方式适用范围广。如果是oracle要把order改成beforeorder就是这句selectkey在插入前执行还是插入后执行。<br />last_insert_id();是MySQL的函数。

动态sql

当我们要根据不同的条件,来执行不同的 sql 语句的时候,需要用到动态sql

if

简化我们以前用jdbc的时候 先拼接where1=1再一顿if判断的手法。

  1. <select id="findByIdAndUserNameIf" parameterType="User" resultType="User">
  2. select * from user
  3. -- 相当于where 1=1,没条件就不拼接
  4. <where>
  5. <if test="id != null">
  6. and id = #{id}
  7. </if>
  8. <if test="username != null">
  9. and username = #{username}
  10. </if>
  11. </where>
  12. </select>

set

  1. <update id="updateIf" parameterType="User">
  2. update user
  3. -- set可以去掉最后一个逗号,所以每一行if的sql后面都要加逗号
  4. <set>
  5. <if test="username != null">
  6. username = #{username},
  7. </if>
  8. <if test="birthday != null">
  9. birthday = #{birthday},
  10. </if>
  11. <if test="sex != null">
  12. sex = #{sex},
  13. </if>
  14. <if test="address != null">
  15. address = #{address},
  16. </if>
  17. </set>
  18. where id = #{id}
  19. </update>

foreach

主要是用在 where id in (1,2,3)这类场景之下
注意,如果list是空的,出现的错误会非常奇怪。整个foreach语句都会消失。然后报SQL ERROR。

  1. List<User> findByList(List<Integer> list);
  2. List<User> findByArray(Integer[] arr);
  1. <select id="findByList" parameterType="list" resultType="User">
  2. select * from user
  3. -- foreach里面的collection代表集合元素。如果泛型是基本类型或String,可以直接写list/collection
  4. -- open 语句开始部分
  5. -- close 语句结束部分
  6. -- item 每个元素的变量名
  7. -- seprator 分隔符
  8. <where>
  9. <foreach collection="list" open="id in (" close=")" separator="," item="id">
  10. #{id}
  11. </foreach>
  12. </where>
  13. </select>
  1. <select id="findByArray" parameterType="int" resultType="User">
  2. select * from user
  3. <where>
  4. <foreach collection="array" open="id in (" close=")" separator="," item="id">
  5. #{id}
  6. </foreach>
  7. </where>
  8. </select>

sql

抽取公共部分用的

  1. <sql id="selectUser">
  2. select * from user
  3. </sql>
  4. 使用的时候只需要
  5. <select id="findByArray" parameterType="int" resultType="User">
  6. <include refid="selectUser"></include>
  7. <where>
  8. <foreach collection="array" open="id in (" close=")" separator="," item="id">
  9. #{id}
  10. </foreach>
  11. </where>
  12. </select>

核心配置深入

plugins

MyBatis 可以使用第三方的插件来对功能进行扩展
分页助手PageHelper 是将分页的复杂操作进行封装,使用简单的方式即可获得分页的相关数据

开发步骤:
①导入通用PageHelper 的坐标
②在mybatis 核心配置文件中配置PageHelper 插件
③测试分页数据获取

  1. <dependency>
  2. <groupId>com.github.pagehelper</groupId>
  3. <artifactId>pagehelper</artifactId>
  4. <version>3.7.5</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.github.jsqlparser</groupId>
  8. <artifactId>jsqlparser</artifactId>
  9. <version>0.9.1</version>
  10. </dependency>
  1. <!-- 注意:分页助手的插件 配置在通用馆mapper 之前 -->
  2. <plugin interceptor="com.github.pagehelper.PageHelper">
  3. <!-- 指定方言 -->
  4. <property name="dialect" value="mysql"/>
  5. </plugin>
  1. @Test
  2. public void testPageHelper(){
  3. //设置分页参数
  4. PageHelper.startPage(1,2);
  5. List<User> select = userMapper2.select(null);
  6. for(User user : select){
  7. System.out.println(user);
  8. }
  9. PageInfo<User> pageInfo = new PageInfo<User>(select);
  10. System.out.println("总条数:"+pageInfo.getTotal());
  11. System.out.println("总页数:"+pageInfo.getPages());
  12. System.out.println("当前页:"+pageInfo.getPageNum());
  13. System.out.println("每页显示长度:"+pageInfo.getPageSize());
  14. System.out.println("是否第一页:"+pageInfo.isIsFirstPage());
  15. System.out.println("是否最后一页:"+pageInfo.isIsLastPage());
  16. }

多表查询

多表查询最重要关注两点
一是当前实体类如何表示对方实体类与自己的关系
二是如何进行配置

一对一

  1. <mapper namespace="com.ning.mapper.OrderMapper">
  2. <resultMap id="ordersMap" type="com.ning.entity.Orders">
  3. <id property="id" column="id"></id>
  4. <result property="ordertime" column="ordertime"></result>
  5. <result property="total" column="total"></result>
  6. <result property="uid" column="uid"></result>
  7. <!-- property是要封装的属性名, javatype是要封装的属性类型-->
  8. <association property="user" javaType="com.ning.entity.User">
  9. <id property="id" column="uid"></id>
  10. <result property="username" column="username"></result>
  11. <result property="birthday" column="birthday"></result>
  12. <result property="sex" column="sex"></result>
  13. <result property="address" column="address"></result>
  14. </association>
  15. </resultMap>
  16. <select id="findAllWithUser" resultMap="ordersMap">
  17. select * from orders o left join user u on o.uid = u.id
  18. </select>
  19. </mapper>

核心就是这段代码,一个订单对应一个User,把订单对应的User封装到订单的User属性里。
新东西就是association
一个很精妙的手法在id那一行,封装的属性是id,column写的是外键uid。这是为了防止两个表联合查询有两个id列造成混淆导致的。当然也可以通过起别名的方式解决,但这样更简单精妙。

一对多

  1. <resultMap id="userMap" type="com.ning.entity.User">
  2. <id property="id" column="id"></id>
  3. <result property="username" column="username" ></result>
  4. <result property="birthday" column="birthday" ></result>
  5. <result property="sex" column="sex" ></result>
  6. <result property="address" column="address" ></result>
  7. <collection property="ordersList" ofType="com.ning.entity.Orders">
  8. <id property="id" column="oid"></id>
  9. <result property="ordertime" column="ordertime"></result>
  10. <result property="total" column="total"></result>
  11. <result property="uid" column="uid"></result>
  12. </collection>
  13. </resultMap>
  14. <select id="findAllWithOrder" resultMap="userMap">
  15. SELECT u.*, o.id oid, o.ordertime, o.total, o.uid FROM orders o RIGHT JOIN USER u ON u.`id` = o.`uid`
  16. </select>
  1. User表里要有一个List 名字叫ordersList

多对多

  1. <resultMap id="userRoleMap" type="com.ning.entity.User">
  2. <id property="id" column="id"></id>
  3. <result property="username" column="username" ></result>
  4. <result property="birthday" column="birthday" ></result>
  5. <result property="sex" column="sex" ></result>
  6. <result property="address" column="address" ></result>
  7. <collection property="roleList" ofType="com.ning.entity.Role">
  8. <id property="id" column="rid"></id>
  9. <result property="rolename" column="rolename"></result>
  10. <result property="roleDesc" column="roleDesc"></result>
  11. </collection>
  12. </resultMap>
  13. <select id="findAllWithRole" resultMap="userRoleMap">
  14. 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`
  15. </select>

同样的,需要一个List 存储中间表

嵌套查询

一对一

  1. <resultMap id="orderMap2" type="com.ning.entity.Orders">
  2. <id property="id" column="id"></id>
  3. <result property="ordertime" column="ordertime"></result>
  4. <result property="total" column="total"></result>
  5. <result property="uid" column="uid"></result>
  6. <!-- 如何执行第二条sql? 如何在执行第二条sql的时候把uid进行传递? -->
  7. <association property="user" javaType="com.ning.entity.User" select="com.ning.mapper.UserMapper.findUserById" column="uid">
  8. </association>
  9. </resultMap>
  10. <!-- 一对一-->
  11. <select id="findAllWithUser2" resultMap="orderMap2">
  12. select * from orders
  13. </select>
  1. 注意在association里加了一个 select和一个column。一个代表接下来要执行的sql,一个代表把执行完的sql的结果的哪些列注入进来。<br />select引用了另一个mappersql语句。

一对多

  1. <resultMap id="userOrderMap" type="com.ning.entity.User">
  2. <id property="id" column="id"></id>
  3. <result property="username" column="username" ></result>
  4. <result property="birthday" column="birthday" ></result>
  5. <result property="sex" column="sex" ></result>
  6. <result property="address" column="address" ></result>
  7. <collection property="ordersList" ofType="com.ning.entity.Orders" select="com.ning.mapper.OrderMapper.findOrderById" column="id"></collection>
  8. </resultMap>
  9. <select id="findAllWithOrder2" resultMap="userOrderMap">
  10. select * from user
  11. </select>
  1. 道理基本同上

多对多

  1. <resultMap id="userRoleMap2" type="User">
  2. <id property="id" column="id"></id>
  3. <result property="username" column="username" ></result>
  4. <result property="birthday" column="birthday" ></result>
  5. <result property="sex" column="sex" ></result>
  6. <result property="address" column="address" ></result>
  7. <collection property="roleList" ofType="Role" column="id" select="com.ning.mapper.RoleMapper.findRoleById"></collection>
  8. </resultMap>
  9. <select id="findAllWithRole2" resultMap="userRoleMap2">
  10. select * from user
  11. </select>