MyBatis 注解模式
MyBatis注解模式
MyBatis 两种映射模式
MyBatis 有两种 SQL 语句映射模式:一种是基于XML,一种是基于注解。
在这之前,我们都是使用基于 XML 映射文件这种模式实现数据库的各种操作。这次,我打算使用 MyBatis 注解的方式重新实现之前的数据库操作。
MyBatis 注解与 XML 映射文件不同之处在于不需要创建 XML 映射文件,SQL 语句映射是直接写在 Mapper 映射器接口方法上即可。
使用 MyBatis 注解后,需要修改 mybatis-config.xml 全局配置文件,如下:
<!--配置映射路径-->
<mappers>
<!--配置 XML 映射文件路径-->
<!-- <mapper resource="mappers/UserMapper.xml" />-->
<mapper resource="mappers/GameMapper.xml" />
<!--配置注解接口路径-->
<mapper class="mapper.UserMapper" />
</mappers>
我们把配置的 UserMapper.xml 映射文件路径注释掉,然后添加配置注解 UserMapper 接口路径。如果不注释的话,MyBatis 会优先使用 XML 映射文件,也就是说注释不会生效。
注解实现 CURD + 动态 SQL 操作
以下是之前已创建好 UserMapper 映射器接口,里面的接口方法对应数据库的 CURD 操作。
package mapper;
import entity.UserEntity;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* @desc User映射器接口
* @date 2020/6/19 上午8:59
*/
public interface UserMapper {
/**
* 根据年龄查询用户信息
* @param age 年龄
* @return user 用户实体集合
*/
public List<UserEntity> selectUserByAge(@Param("age") int age);
/**
* 根据年龄和性别查询用户信息
* @param userOne 获取年龄
* @param userTwo 获取性别
* @return 用户实体集合
*/
public List<UserEntity> selectUserByAgeAndSex(@Param("userOne") UserEntity userOne,@Param("userTwo") UserEntity userTwo);
/**
* 根据姓名和年龄查询用户信息
* @param name 姓名
* @param user 获取年龄
* @return
*/
public List<UserEntity> selectUserByNameAndAge(@Param("name") String name, @Param("user") UserEntity user);
/**
* 查询所有用户信息
* @return 用户实体集合
*/
public List<UserEntity> selectUserAll();
/**
* 根据姓名集合查询用户
* @param names 姓名集合
* @return 用户实体集合
*/
public List<UserEntity> selectUserByNameList(List<String> names);
/**
* 新增用户
* @param user 用户实体
* @return 影响行数
*/
public int insertUser(UserEntity user);
/**
* 更新用户姓名
* @param user 用户姓名
* @return 影响行数
*/
public int updateUser(@Param("id") int id,@Param("name") String name);
/**
* 根据姓名删除用户
* @param name 用户姓名
* @return 影响行数
*/
public int deleteUserById(int id);
}
接下来,我直接在以上接口方法上使用 MyBatis 注解,以实现 SQL 语句映射,如下:
package mapper;
import entity.UserEntity;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* @author benjamin.xu
* @desc User映射器接口
* @date 2020/6/19 上午8:59
*/
public interface UserMapper {
/**
* 根据年龄查询用户信息
* @param age 年龄
* @return user 用户实体集合
*/
@Select("select * from tb_user where age > #{age}")
@Results(id="userMap", value={
@Result(property = "id",column = "id",id = true),
@Result(property = "userName",column = "user_name"),
@Result(property = "password",column = "password"),
@Result(property = "name",column = "name"),
@Result(property = "age",column = "age"),
@Result(property = "sex",column = "sex"),
@Result(property = "birthday",column = "birthday"),
@Result(property = "created",column = "created"),
@Result(property = "updated",column = "updated")
})
public List<UserEntity> selectUserByAge(@Param("age") int age);
/**
* 根据年龄和性别查询用户信息
* @param userOne 获取年龄
* @param userTwo 获取性别
* @return 用户实体集合
*/
@Select("select * from tb_user where age > #{userOne.age} and sex = #{userTwo.sex}")
@ResultMap("userMap")
public List<UserEntity> selectUserByAgeAndSex(@Param("userOne") UserEntity userOne,@Param("userTwo") UserEntity userTwo);
/**
* 根据姓名和年龄查询用户信息
* @param name 姓名
* @param user 获取年龄
* @return
*/
@Select("select * from tb_user where name = #{name} and age < #{user.age}")
@ResultMap("userMap")
public List<UserEntity> selectUserByNameAndAge(@Param("name") String name, @Param("user") UserEntity user);
/**
* 查询所有用户信息
* @return 用户实体集合
*/
@Select("select * from tb_user")
@ResultMap("userMap")
public List<UserEntity> selectUserAll();
/**
* 根据姓名集合查询用户
* @param names 姓名集合
* @return 用户实体集合
*/
@Select("<script>" +
"select * from tb_user\n" +
" <where>\n" +
" <foreach item=\"name\" collection=\"list\" index=\"index\" open=\"name in (\" separator=\",\" close=\")\">\n" +
" #{name}\n" +
" </foreach>\n" +
" </where>" +
"</script>")
@ResultMap("userMap")
public List<UserEntity> selectUserByNameList(List<String> names);
/**
* 新增用户
* @param user 用户实体
* @return 影响行数
*/
@Insert("insert into tb_user (id,user_name, password, name, age, sex, birthday, created, updated) values \n" +
"(null,#{userName},#{password},#{name},#{age},#{sex},#{birthday},now(),now())")
@Options(useGeneratedKeys = true, keyProperty = "id")
public int insertUser(UserEntity user);
/**
* 更新用户姓名
* @param user 用户姓名
* @return 影响行数
*/
@Update("update tb_user set name=#{name} where id=#{id}")
public int updateUser(@Param("id") int id,@Param("name") String name);
/**
* 根据姓名删除用户
* @param name 用户姓名
* @return 影响行数
*/
@Delete("delete from tb_user where id=#{id}")
public int deleteUserById(int id);
}
执行测试,结果和 XML 映射文件一样,我这里就不截图了。
注解说明:
- @Insert:插入 SQL 语句注解,相当于 XML 映射文件的 insert 标记
- @Options:该注解有两个属性:一个是 useGeneratedKeys 属性,设置为 true 表示启动自增主键回传,它只针对 insert 语句有效;一个是 keyProperty 属性,表示主键回传对应的实体类属性名
- @Update:更新 SQL 语句注解,相当于 XML 映射文件的 update 标记
- @Delete:删除 SQL 语句注解,相当于 XML 映射文件的 delete 标记
- @Select:查询 SQL 语句注解,相当于 XML 映射文件的 select 标记
- @Results:结果集映射注解,相当于 XML 映射文件的结果集映射 ResultMap 标记
- @Result:实体类属性与数据表字段映射注解,相当于 XML 映射文件中 ResultMap 标记中的 result 子标记(id = true表示该字段是主键)
- @ResultMap:引用 @Results 注解定义好的结果集映射
- @Results:结果集映射注解,相当于 XML 映射文件的结果集映射 ResultMap 标记
如果你已经掌握了 XML 映射文件,想要掌握以上这些注解就太 easy 了;因为和 XML 映射文件相比,无非就是把 SQL 语句映射换了一个地方罢了。
说白了,你可以直接把 XML 映射文件的 SQL 语句复制过来,放到对应的注解里面就完事了。
动态 SQL说明:
注解实现动态 SQL 和 XML 映射文件一样,也是把 SQL 语句复制过来,不过需要将其放到 script 标记里面,告诉 MyBatis 这是动态 SQL 语句,如下:
@Select("<script>" +
" select * from tb_user\n" +
" <where>\n" +
" <foreach item=\"name\" collection=\"list\" index=\"index\" open=\"name in (\" separator=\",\" close=\")\">\n" +
" #{name}\n" +
" </foreach>\n" +
" </where>" +
"</script>")
@ResultMap("userMap")
public List<UserEntity> selectUserByNameList(List<String> names);
大家可以看到,里面很多斜杠转义符,所以在注解中实现 动态 SQL,要特别小心,弄不好就会报错,搞得你焦头烂额。
注解实现一对一关联查询
这里需要先修改 mybatis-config.xml 全局配置文件,如下:
<!--配置映射路径-->
<mappers>
<!-- <mapper resource="mappers/UserMapper.xml" />-->
<!-- <mapper resource="mappers/GameMapper.xml" />-->
<mapper class="mapper.UserMapper" />
<mapper class="mapper.GameMapper" />
</mappers>
目的是将原来配置的 XML 映射文件路径注释掉,然后添加注解接口路径。
以下是之前已创建好 GameMapper 映射器接口,如下:
package mapper;
import entity.GameEntity;
import entity.PlayerEntity;
import entity.RoleEntity;
/**
* @desc 游戏映射器接口
* @date 2020/7/10 下午6:20
*/
public interface GameMapper {
/**
* 根据角色ID查询账号信息
* @param id 角色Id
* @return 角色实体对象
*/
public RoleEntity selectRoleById(int id);
/**
* 根据游戏名查询游戏账号
* @param name 游戏名
* @return 游戏实体类
*/
public GameEntity selectGameByName(String name);
/**
* 根据玩家名查询游戏
* @param name 玩家名
* @return 玩家实体类
*/
public PlayerEntity selectPlayerByName(String name);
}
接下来,我直接在以上接口方法上使用 MyBatis 注解,以实现 SQL 语句映射,如下:
@Select("select r.*,a.* from tb_role as r join tb_account as a on r.account_id=a.id where r.id=#{id}")
@Results(value = {
@Result(property = "id",column = "id",id = true),
@Result(property = "profession",column = "profession"),
@Result(property = "rank",column = "rank"),
@Result(property = "money",column = "money"),
//一对一关联映射
@Result(property = "account.id",column = "id"),
@Result(property = "account.userName",column = "user_name"),
@Result(property = "account.password",column = "password")
})
public RoleEntity selectRoleById(int id);
注:暂时没有找到 MyBatis 注解如何实现一对多以及多对多的关联查询的方法
注解实现一对一、一对多和多对多子查询
子查询需要用到两个注解:
- @One:一对一映射注解,相当于 XML 映射文件的 association 标记
- @Many:一对多映射注解,相当于 XML 映射文件的 collection 标记
我直接在以上接口方法上使用 MyBatis 注解,以实现 SQL 语句映射,如下:
package mapper;
import entity.AccountEntity;
import entity.GameEntity;
import entity.PlayerEntity;
import entity.RoleEntity;
import org.apache.ibatis.annotations.*;
/**
* @desc 游戏映射器接口
* @date 2020/7/10 下午6:20
*/
public interface GameMapper {
/**
* 根据角色ID查询账号信息
* @param id 角色Id
* @return 角色实体对象
*/
@Select("select * from tb_role where id=#{id}")
@Results(value = {
@Result(property = "id",column = "id",id = true),
@Result(property = "profession",column = "profession"),
@Result(property = "rank",column = "rank"),
@Result(property = "money",column = "money"),
@Result(property = "account",column = "account_id",
one = @One(select = "selectAccountById"))
})
public RoleEntity selectRoleById(int id);
@Select("select * from tb_account where id=#{id}")
@ResultType(AccountEntity.class)
public AccountEntity selectAccountById(int id);
/**
* 根据游戏名查询游戏账号
* @param name 游戏名
* @return 游戏实体类
*/
@Select("select * from tb_game where name =#{name}")
@Results(value = {
@Result(property = "id",column = "id",id = true),
@Result(property = "name",column = "name"),
@Result(property = "type",column = "type"),
@Result(property = "operator",column = "operator"),
@Result(property = "accounts",column = "id",
many = @Many(select = "selectAccountById"))
})
public GameEntity selectGameByName(String name);
/**
* 根据玩家名查询游戏
* @param name 玩家名
* @return 玩家实体类
*/
@Select("select * from tb_player where name = #{name}")
@Results(value = {
@Result(property = "id",column = "id",id = true),
@Result(property = "name",column = "name"),
@Result(property = "age",column = "age"),
@Result(property = "sex",column = "sex"),
@Result(property = "games",column = "id",
many = @Many(select = "selectGameById"))
})
public PlayerEntity selectPlayerByName(String name);
@Select(" select * from tb_game where id in (select game_id from tb_player_game where player_id=#{id})")
@ResultType(GameEntity.class)
public GameEntity selectGameById(int id);
}
我在接口中新创建了两个查询方法,selectAccountById 和 selectGameById,这两个方法和 XML 映射文件中作用相同,也就是用于子查询。