前面的章节我们已经认识了如何使用 xml 来配置 MyBatis 的用法。随着这几年来注解开发越来越流行,Mybatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。
我们先围绕一些基本的CRUD来学习,再学习复杂映射多表操作。不过,总体来说,注解开发方式一般只用在简单的SQL,其他复杂的方式一般还是写在 xml 文件中。
# 配置文件
0. 作用: 配置一些信息,给程序使用
程序读取配置文件的数据过程: 解析
1. properties 文件
解析: Properties类
2. xml 文件
解析: dom/sax/pull
3. 注解
解析: 反射
* @Insert:实现新增,代替了<insert></insert>
* @Update:实现更新,代替了<update></update>
* @Delete:实现删除,代替了<delete></delete>
* @Select:实现查询,代替了<select></select>
* @Result:实现结果集封装,代替了<result></result>
* @Results:可以与@Result 一起使用,封装多个结果集,代替了<resultMap></resultMap>
* @One:实现一对一结果集封装,代替了<association></association>
* @Many:实现一对多结果集封装,代替了<collection></collection>
需求:基于user模块通过注解实现,增删改查
public interface UserMapper {
// 查询所有
@Select("select id as uid,username as uname,birthday as bir , sex as gender, address as addr from user")
@Results({ // resultMap标签手动映射
@Result(column = "uid",property = "id",id=true), // result标签映射封装
@Result(column = "uname",property = "username"),
@Result(column = "bir",property = "birthday"),
@Result(column = "gender",property = "sex"),
@Result(column = "addr",property = "address")
})
public List<User> findAll();
// id查询
@Select("select * from user where id = #{id}")
public User findById(Integer id);
// 新增
@Insert("insert into user(username,birthday,sex,address) values(#{username},#{birthday},#{sex},#{address})")
public void save(User user);
// 修改(动态sql还是推荐使用xml)
@Update("update user set username = #{username}, birthday = #{birthday}, sex = #{sex}, address = #{address} where id = #{id}")
public void update(User user);
// 删除
@Delete("delete from user where id = #{id}")
public void delete(Integer id);
}
@Test
public void test16(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 查询所有用户信息
List<User> users = mapper.findAll();
for (User user : users) {
System.out.println(user);
}
MyBatisUtil.commitAndClose(sqlSession);
}
执行如下:
image-20210406081842726
@Test
public void test17(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 根据id查询用户信息
User user = mapper.findById(41);
System.out.println(user);
MyBatisUtil.commitAndClose(sqlSession);
}
执行如下:
image-20210406082307312
@Test
public void test18(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 新增用户
User user = new User();
user.setUsername("李大四");
user.setSex("男");
mapper.save(user);
MyBatisUtil.commitAndClose(sqlSession);
}
执行如下:
image-20210406082726038
@Test
public void test19(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 修改用户
User user = new User();
user.setId(49); // 设置修改的用户ID
user.setUsername("张大三");
user.setSex("男");
mapper.update(user);
MyBatisUtil.commitAndClose(sqlSession);
}
执行如下:
image-20210406083006221
@Test
public void test20(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 根据id, 删除用户
mapper.delete(46);
MyBatisUtil.commitAndClose(sqlSession);
}
执行如下:
image-20210406083234369
“注解开发的方式写多表查询很少用,因为很麻烦,大概了解一下就好。 ”
注解多表操作是基于嵌套查询来实现
1589334124076
需求:查询一个订单,与此同时查询出该订单所属的用户
一对一查询语句
-- 使用一对一的嵌套查询进行
SELECT * FROM orders where id = #{订单的id};
SELECT * FROM `user` WHERE id = #{订单的uid};
public interface UserMapper {
// id查询
@Select("select * from user where id = #{id}")
public User findById(Integer id);
}
public interface OrdersMapper {
/**
* <!--
* 设置查询结果集 resultMap, 结果映射为 Orders 类
* -->
* <resultMap id="myorder" type="Orders" autoMapping="true">
* <!-- 设置 id 字段映射 Orders 类的属性 id -->
* <id property="id" column="id"/>
* <!-- 设置普通字段 ordertime 映射 Orders 类的属性 ordertime -->
* <result property="ordertime" column="ordertime"/>
* <!--
* # 嵌套查询重点:
* 0. 目的
* select * from user where id = ?
* 映射到 orders.user属性中
* 1. 编写查询user表的语句:
* UserMapper.findUserById -> UserMapper.xml
* 2. 嵌套到这里
* association标签的两个属性
* a. column : 条件 (执行查询方法的参数)
* b. select : 调用第二句sql执行
* 接口的权限定名.方法名
* UserMapper.findUserById(用户id)
* -->
* <association property="user" javaType="user" autoMapping="true"
* column="uid"
* select="com.lijw.dao.UserMapper.findUserById">
* <id property="id" column="uid"/>
* <result property="username" column="username"/>
* </association>
* </resultMap>
*
* <select id="findOrderByIdWithUser" resultMap="myorder">
* select * from orders where id = #{id}
* </select>
*/
@Select("select * from orders where id = #{id}")
@Results({
@Result(property = "id",column = "id",id=true), // id标签: 主键字段映射
@Result(property = "ordertime",column = "ordertime"),
@Result(property = "user",javaType = User.class, column = "uid",
one = @One(select = "com.lijw.dao.UserMapper.findById",fetchType = FetchType.EAGER)) // 设置立即加载
})
Orders findOrderByIdWithUser(Integer id);
}
public class OrderMapperTest extends BaseMapperTest { // 继承父类,就可以直接使用 父类的方法和成员变量了
// 一对一嵌套测注解试
@Test
public void test21(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
OrdersMapper mapper = sqlSession.getMapper(OrdersMapper.class);
// 嵌套查询
Orders order = mapper.findOrderByIdWithUser(1);
System.out.println(order);
MyBatisUtil.commitAndClose(sqlSession);
}
}
执行如下:
image-20210407002433893
需求:查询一个用户,与此同时查询出该用户具有的订单
一对多查询语句
-- 使用嵌套查询演示
SELECT * FROM `user` where id = #{id};
SELECT * FROM orders where uid = #{用户id};
public interface OrdersMapper {
@Select("select * from orders where uid = #{id}")
public List<Orders> findByUid(Integer uid);
}
public interface UserMapper {
// 一对多注解嵌套查询
@Select("select * from user where id = #{id}")
@Results({ // resultMap标签手动映射
@Result(column = "id",property = "id",id=true), // result标签映射封装
@Result(column = "username",property = "username"),
@Result(column = "birthday",property = "birthday"),
@Result(column = "sex",property = "sex"),
@Result(column = "address",property = "address"),
// 嵌套查询:SELECT * FROM orders where uid = #{用户id};
// 传递 id 到 com.lijw.dao.OrdersMapper.findByUid 方法中,返回的结果设置到 user 对象的 list 属性
@Result(
property = "list", javaType = List.class, column = "id",
many = @Many(select = "com.lijw.dao.OrdersMapper.findByUid")
)
})
public User findByIdWithOrders(Integer id);
}
// 一对多嵌套查询测试
@Test
public void test22(){
SqlSession sqlSession = MyBatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 查询id=41用户的信息,包含订单信息
User user = mapper.findByIdWithOrders(41);
System.out.println(user);
List<Orders> ordersList = user.getList();
System.out.println(ordersList);
MyBatisUtil.commitAndClose(sqlSession);
}
执行如下:
image-20210407005203830
不管是一对多还是多对多 ,在注解配置中都有fetchType的属性
* fetchType = FetchType.LAZY 表示懒加载
* fetchType = FetchType.EAGER 表示立即加载
* fetchType = FetchType.DEFAULT 表示使用全局配置
配置SqlMapConfig.xml文件开启二级缓存的支持
<settings>
<!--
因为cacheEnabled的取值默认就为true,所以这一步可以省略不配置。
为true代表开启二级缓存;为false代表不开启二级缓存。
-->
<setting name="cacheEnabled" value="true"/>
</settings>
在Mapper接口中使用注解配置二级缓存
// 注解配置二级缓存
@CacheNamespace
public interface UserMapper {...}
相当于 xml 配置如下:
image-20210407005411170
1. 注解开发和xml配置相比,从开发效率来说,注解编写更简单,效率更高。
2. 从可维护性来说,注解如果要修改,必须修改源码,会导致维护成本增加。xml维护性更强。
* 经验:单表简单CRUD可以使用注解、多表及动态sql你就用xml