这几年来注解开发越来越流行,MyBatis也可以使用注解开发方式,这样我们就可以减少编写Mapper映射文件了。本次我们先围绕一些基本的 CRUD来学习,再学习复杂映射关系及延迟加载。
1 MyBatis的常用注解说明
@Insert
:实现新增@Update
:实现更新@Delete
:实现删除@Select
:实现查询@Result
:实现结果集封装@Results
:可以与@Result
一起使用,封装多个结果集@ResultMap
:实现引用@Results定义的封装@One
:实现一对一结果集封装@Many
:实现一对多结果集封装@SelectProvider
:实现动态SQL映射@CacheNamespace
:实现注解二级缓存的使用
2 使用MyBatis注解实现基本CRUD
单表的CRUD操作是最基本的操作,前面我们的学习都是基于MyBatis的映射文件来实现的。
2.1 编写实体类
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + ", userBirthday=" + userBirthday + ", userSex=" + userSex + ", userAddress=" + userAddress + "]";
}
}
注意:此处我们故意和数据库表的列名不一致。
2.2 使用注解方式开发持久层接口
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(id="userMap",
value= {
@Result(id=true,column="id",property="userId"),
@Result(column="username",property="userName"),
@Result(column="sex",property="userSex"),
@Result(column="address",property="userAddress"),
@Result(column="birthday",property="userBirthday")
})
List<User> findAll();
/**
* 根据id查询一个用户
* @param userId
* @return
*/
@Select("select * from user where id = #{uid}")
@ResultMap("userMap")
User findById(Integer userId);
/**
* 保存操作
* @param user
* @return
*/
@Insert("insert into user(username,sex,birthday,address)values(#{username},#{sex},#{birthday},#{address})")
@SelectKey(keyColumn="id",keyProperty="id",resultType=Integer.class,before = false, statement = {"select last_insert_id()"})
int saveUser(User user);
/**
* 更新操作
* @param user
* @return
*/
@Update("update user set username=#{username},address=#{address},sex=#{sex},birthday=#{birthday} where id =#{id}")
int updateUser(User user);
/**
* 删除用户
* @param userId
* @return
*/
@Delete("delete from user where id = #{uid}")
int deleteUser(Integer userId);
/**
* 查询使用聚合函数
* @return
*/
@Select("select count(*) from user")
int findTotal();
/**
* 模糊查询
* @param name
* @return
*/
@Select("select * from user where username like #{username}")
List<User> findByName(String name);
}
2.3 编写SqlMapConfig 配置文件
<?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>
<!-- 配置properties文件的位置 -->
<properties resource="jdbcConfig.properties"></properties>
<!-- 配置别名的注册 -->
<typeAliases>
<package name="com.itheima.domain"/>
</typeAliases>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境 -->
<environment id="mysql">
<!-- 配置事务的类型是JDBC -->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!-- 配置映射信息 -->
<mappers>
<!-- 配置dao接口的位置,它有两种方式
第一种:使用mapper标签配置class属性
第二种:使用package标签,直接指定dao接口所在的包 -->
<package name="com.itheima.dao"/>
</mappers>
</configuration>
2.4 编写测试方法
public class MyBatisAnnotationCRUDTest {
/**
* 测试查询所有
*/
@Test
public void testFindAll() {
List<User> users = userDao.findAll();
for (User user : users) {
System.out.println(user);
}
}
/**
* 测试查询一个
*/
@Test
public void testFindById() {
User user = userDao.findById(41);
System.out.println(user);
}
/**
* 测试保存
*/
@Test
public void testSave() {
User user = new User();
user.setUserName("mybatis annotation");
user.setUserSex("男");
user.setUserAddress("北京市顺义区");
user.setUserBirthday(new Date());
int res = userDao.saveUser(user);
System.out.println("影响数据库记录的行数:"+res);
System.out.println("插入的主键值:"+user.getUserId());
}
/**
* 测试更新
*/
@Test
public void testUpdate() {
User user = userDao.findById(63);
user.setUserBirthday(new Date());
user.setUserSex("女");
int res = userDao.updateUser(user);
System.out.println(res);
}
/**
* 测试删除
*/
@Test
public void testDelete() {
int res = userDao.deleteUser(63);
System.out.println(res);
}
/**
* 测试查询使用聚合函数
*/
@Test
public void testFindTotal() {
int res = userDao.findTotal();
System.out.println(res);
}
/**
* 测试模糊查询
*/
@Test
public void testFindByName() {
List<User> users = userDao.findByName("%m%");
for (User user : users) {
System.out.println(user);
}
}
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Before//junit的注解
public void init()throws Exception{
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
//3.创建session
session = factory.openSession();
//4.创建代理对象
userDao = session.getMapper(IUserDao.class);
}
@After//junit的注解
public void destroy()throws Exception {
//提交事务
session.commit();
//释放资源
session.close();
//关闭流
in.close();
}
}
3 使用注解实现复杂关系映射开发
实现复杂关系映射之前我们可以在映射文件中通过配置<resultMap>来实现,在使用注解开发时我们需要借助@Results注解,@Result注解,@One注解,@Many注解。
3.1 复杂关系映射的注解说明
-
@Results注解:代替的是标签<resultMap>。
该注解中可以使用单个@Result注解,也可以使用@Result集合:
@Results(@Result())
或@Results({@Result(), @Result()})
。 -
@Result注解:代替了<id>标签和<result>标签。
@Result
中属性介绍:id
:是否是主键字段;column
:数据库的列名;property
:需要装配的属性名;one
:需要使用的@One注解;many
:需要使用的@Many注解。
-
@One注解(一对一):代替了<assocation>标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。
@One
注解属性介绍:select
:指定用来多表查询的sqlmapperfetchType
:会覆盖全局的配置参数lazyLoadingEnabled。
使用格式:
@Result(column="", property="", one=@One(select=""))
-
@Many注解(多对一):代替了<collection>标签,是多表查询的关键,在注解中用来指定子查询返回对象集合。
使用格式:@Result(property="", column="", many=@Many(select=""))
注意:聚集元素用来处理“一对多”的关系。需要指定映射的Java实体类的属性,属性的javaType(一般为ArrayList)但是注解中可以不定义。
3.2 使用注解实现一对一复杂关系映射及延迟加载
需求:加载账户信息时并且加载该账户的用户信息,根据情况可实现延迟加载。(注解方式实现)
3.2.1 添加User实体类及Account实体类
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + ", userBirthday=" + userBirthday + ", userSex=" + userSex + ", userAddress=" + userAddress + "]";
}
}
public class Account implements Serializable {
private Integer id;
private Integer uid;
private double money;
//多对一关系映射:从表方应该包含一个主表方的对象引用
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
@Override
public String toString() {
return "Account [id=" + id + ", uid=" + uid + ", money=" + money + "]";
}
}
3.2.2 添加账户的持久层接口并使用注解配置
public interface IAccountDao {
/**
* 查询所有账户,采用延迟加载的方式查询账户的所属用户
* @return
*/
@Select("select * from account")
@Results(id="accountMap",
value= {
@Result(id=true,column="id", property="id"),
@Result(column="uid", property="uid"),
@Result(column="money", property="money"),
@Result(column="uid",
property="user",
one=@One(select="com.itheima.dao.IUserDao.findById",
fetchType=FetchType.LAZY))
})
List<Account> findAll();
}
3.2.3 添加用户的持久层接口并使用注解配置
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(id="userMap",
value= {
@Result(id=true,column="id", property="userId"),
@Result(column="username", property="userName"),
@Result(column="sex", property="userSex"),
@Result(column="address", property="userAddress"),
@Result(column="birthday", property="userBirthday")
})
List<User> findAll();
/**
* 根据id查询一个用户
* @param userId
* @return
*/
@Select("select * from user where id = #{uid}")
@ResultMap("userMap")
User findById(Integer userId);
}
3.2.4 测试一对一关联及延迟加载
public class AccountTest {
@Test
public void testFindAll() {
List<Account> accounts = accountDao.findAll();
// for(Account account : accounts) {
// System.out.println(account);
// System.out.println(account.getUser());
// }
}
}
3.3 使用注解实现一对多复杂关系映射
需求:查询用户信息时,也要查询他的账户列表。使用注解方式实现。
分析:一个用户具有多个账户信息,所以形成了用户(User)与账户(Account)之间的一对多关系。
3.3.1 User实体类加入List<Account>
public class User implements Serializable {
private Integer userId;
private String userName;
private Date userBirthday;
private String userSex;
private String userAddress;
//一对多关系映射:主表方法应该包含一个从表方的集合引用
private List<Account> accounts;
public List<Account> getAccounts() {
return accounts;
}
public void setAccounts(List<Account> accounts) {
this.accounts = accounts;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Date getUserBirthday() {
return userBirthday;
}
public void setUserBirthday(Date userBirthday) {
this.userBirthday = userBirthday;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserAddress() {
return userAddress;
}
public void setUserAddress(String userAddress) {
this.userAddress = userAddress;
}
@Override
public String toString() {
return "User [userId=" + userId + ", userName=" + userName + ", userBirthday=" + userBirthday + ", userSex=" + userSex + ", userAddress=" + userAddress + "]";
}
}
3.3.2 编写用户的持久层接口并使用注解配置
public interface IUserDao {
/**
* 查询所有用户
* @return
*/
@Select("select * from user")
@Results(id="userMap",
value= {
@Result(id=true,column="id", property="userId"),
@Result(column="username", property="userName"),
@Result(column="sex", property="userSex"),
@Result(column="address", property="userAddress"),
@Result(column="birthday", property="userBirthday"),
@Result(column="id", property="accounts",
many=@Many(
select="com.itheima.dao.IAccountDao.findByUid",
fetchType=FetchType.LAZY))
})
List<User> findAll();
}
@Many
注解:相当于<collection>的配置;select
属性:代表将要执行的sql语句;fetchType
属性:代表加载方式,一般如果要延迟加载都设置为LAZY的值。
3.3.3 编写账户的持久层接口并使用注解配置
public interface IAccountDao {
/**
* 根据用户id查询用户下的所有账户
* @param userId
* @return
*/
@Select("select * from account where uid = #{uid}")
List<Account> findByUid(Integer userId);
}
3.3.4 添加测试方法
public class UserTest {
/**
* 测试查询所有
*/
@Test
public void testFindAll() {
List<User> users = userDao.findAll();
// for(User user : users) {
// System.out.println("-----每个用户的内容-----");
// System.out.println(user);
// System.out.println(user.getAccounts());
// }
}
private InputStream in;
private SqlSessionFactory factory;
private SqlSession session;
private IUserDao userDao;
@Before//junit的注解
public void init() throws Exception{
//1.读取配置文件
in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
factory = builder.build(in);
//3.创建session
session = factory.openSession();
//4.创建代理对象
userDao = session.getMapper(IUserDao.class);
}
@After//junit的注解
public void destroy()throws Exception {
//提交事务
session.commit();
//释放资源
session.close();
//关闭流
in.close();
}
}
4 MyBatis基于注解的二级缓存
4.1 在SqlMapConfig中开启二级缓存支持
<!-- 配置二级缓存 -->
<settings>
<!-- 开启二级缓存的支持 -->
<setting name="cacheEnabled" value="true"/>
</settings>
4.2 在持久层接口中使用注解配置二级缓存
@CacheNamespace(blocking=true)//MyBatis基于注解方式实现配置二级缓存
public interface IUserDao {}