关于mybatis(不断更新中) mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。 mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。 采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。
在本篇文章中,除了一些通用的概念外,在实现上主要采用“注解开发”的方式呈现。
使用mybatis注解完成基本的CRUD 编写实体类
1 2 3 4 5 6 7 8 9 10 11 package com.sosactwt.forbatis;import java.io.Serializable;@Data public class UserPo implements Serializable { private Integer id; private String username; private String address; private String sex; }
编写持久层接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 package com.sosactwt.formybatis;import org.apache.ibatis.annotations.Delete;import org.apache.ibatis.annotations.Insert;import org.apache.ibatis.annotations.Select;import org.apache.ibatis.annotations.Update;import java.util.List;public interface UserMapper extends BaseMapper <UserPo > { @Select("select * from user") List<User> findAll () ; @Insert("insert into user(username,address,sex,birthday) values(#{username},#{address},#{sex},#{birthday})") void saveUser (User user) ; @Update("update user set username=#{username},sex=#{sex},birthday=#{birthday},address=#{address} where id=#{id}") void updateUser (User user) ; @Delete("delete from user where id=#{userId}") void deleteUser (Integer userId) ; @Select("select * from user where id=#{userId}") User findByUserId (Integer userId) ; @Select("select * from user where username like '%${value}%'") List<User> findByName (String username) ; @Select("select count(*) from user") int findTotal () ; }
如此之后,我们可以用JUnit来做一下简单的单元测试,以对findById()的测试举例:
虽然我们这里只测试了一个,但是当你测试多个的时候,为了避免需要反复初始化与销毁,我们往往会用到@BeforeEach
和@AfterEach
,在每个@Test
之前和之后他俩都会运行一次。(具体可以在JUnit那篇里面看看)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.sosactwt.formybatis;public class UserMapperTest extends ApplicationTest { @Autowired private UserMapper userMapper; @Autowired private JdbcTemplate jdbcTemplate; @BeforeEach public void runBeforeEachTest () { UserPo userPo = new UserPo(3 ,"sosactwt" ,"China" ,"male" ); userMapper.insert(userPo); } @AfterEach public void runAfterEachTest () { jdbcTemplate.update("delete from user" ); } @Test public void findByUserIdTest () { Integer userId = 1 ; User result = usermapper.findById(userId); assertEquals(UserId,result.getId(),"失败啦!" ); } private UserPo createUserPo ( Integer id; String username; String address; String sex;) { UserPo userPo = new UserPo; userPo.setId(id); userPo.setUsername(username); userPo.setAddress(address); userPo.setSex(sex); return userPo; } }
处理更加复杂一点的关系映射-多表查询 除了上面简单的CRUD之外,我们还会遇到一些复杂一些的关系映射。
我们将用到**@Results
注解,@Result
注解,@One
注解,@Many
注解。**
现在来解释一下各个注解的意思:
@Results 注解 代替的是标签<resultMap>
(与xml相比) 该注解中可以使用单个@Result 注解
,也可以使用@Result
集合@Results({@Result(),@Result()})
或@Results(@Result())
@Resut 注解 代替了<id>
标签和<result>
标签(与xml相比) @Result 中的属性介绍:
@Result 中的属性
介绍
id
是否是主键字段
column
数据库的列名
property
需要装配的属性名
one
需要使用的@One 注解(@Result(one=@One)()))
many
需要使用的@Many 注解(@Result(many=@many)()))
现在我们设想一个需求:加载账户信息并且加载该账户的用户信息,根据情况可实现立即加载
为了展示实例,我们先建一个“账户实体类”AccountPo,配合前面的UserPo。
1 2 3 4 5 6 7 8 9 10 11 package com.sosactwt.forbatis;import java.io.Serializable;@Data public class AccountPo implements Serializable {private Integer id;private Integer uid;private double money;private User user;}
@One 注解(一对一) 代替了<association>
标签,是多表查询的关键,在注解中用来指定子查询返回单一对象。@One
注解属性介绍: select 指定用来多表查询的 sqlmapper fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。 使用格式:@Result(column=" “,property=”",one=@One(select=""))
AccountMapper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface AccountMapper { @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(property = "user",column = "uid", one=@One(select="com.sosactwt.formybatis.UserMapper.findById",fetchType= FetchType.EAGER))//即一对一查找,且为立即加载 }) List<Account> findAll () ;
UserMapper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public interface UserMapper { @Select("select * from user") @Results(id="userMap",value={ @Result(id = true,column = "id",property = "userId"), @Result(column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "sex",property = "userSex") }) List<User> findAll () ; @Select("select * from user where id=#{id}") @ResultMap("userMap") User findById (Integer userId) ; @Select("select * from user where username like #{username}") @ResultMap("userMap") List<User> findByName (String username) ; }
现在我们考虑另一个需求:
需求:查询用户信息时,也要查询他的账户列表。使用注解方式实现。并设置延时加载。 分析:一个用户具有多个账户信息,所以形成了用户(User)与账户(Account)之间的一对多关系。
@Many 注解(一对多)
代替了<collection>
标签,是是多表查询的关键,在注解中用来指定子查询返回对象集合。 注意:聚集元素用来处理“一对多”的关系。需要指定映射的 Java 实体类的属性,属性的 javaType(一般为 ArrayList)但是注解中可以不定义。 使用格式:@Result(property="",column="",many=@Many(select=""))
由于我们要在查User的时候查到多个Account,故我们考虑重写一个UserPo,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.sosactwt.forbatis;import java.io.Serializable;@Data public class UserPo implements Serializable { private Integer id; private String username; private String address; private String sex; private List<AccountPo> accountPos; }
然后开始写Mapper
UserMapper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public interface UserMapper { @Select("select * from user") @Results(id="userMap",value={ @Result(id = true,column = "id",property = "userId"), @Result(column = "id",property = "userId"), @Result(column = "username",property = "userName"), @Result(column = "sex",property = "userSex"), @Result(column = "birthday",property = "userBirthday"), @Result(property = "accounts" ,column = "id", many = @Many(select = "com.keafmd.dao.IAccountDao.findAccountByUid", fetchType = FetchType.LAZY)) }) List<User> findAll () ; @Select("select * from user where id=#{userId}") @ResultMap("userMap") User findById (Integer userId) ; @Select("select * from user where username like #{username}") @ResultMap("userMap") List<User> findByName (String username) ; }
AccountMapper:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public interface AccountMapper { @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(property = "user",column = "uid",one=@One(select="com.keafmd.dao.IUserDao.findById",fetchType= FetchType.EAGER)) }) List<Account> findAll () ; @Select("select * from account where uid = #{userId}") List<Account> findAccountByUid (Integer userId) ; }