高级查询
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">
-- 必须写value
select * 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>
@Test
public 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>
@Test
public 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>