最近重构以前写的服务,最大的一个变动是将mybatis切换为spring data jpa,切换的原因很简单,有两点:第一、它是spring的子项目能够和spring boot很好的融合,没有xml文件(关于这一点hibernate似乎也很符合);第二、简单优雅,比如不需要写SQL、对分页有自动化的支持等等,基于以上两点开始了重构之路。在这之前了解了一下hibernate、mybatis和spring data jpa的区别,在这里也简单的记录一下:Hibernate的O/R Mapping实现了POJO 和数据库表之间的映射,以及SQL 的自动生成和执行;Mybatis则在于POJO 与SQL之间的映射关系,通过ResultMap对SQL的结果集进行映射;Spring Data jpa是一个用于简化数据库访问,并支持云服务的开源框架,容易上手,通过命名规范、注解查询简化查询操作。这三者都是ORM框架,但是mybatis可能并没有那么典型,原因就是mybatis映射的是SQL的结果集,另外hibernate和spring data jpa都是jpa(Java Persistence API,是从JDK5开始提供的,用来描述对象 <--> 关系表映射关系,并持久化的标准)的一种实现,从这一点上将这两者是一种并列的关系,spring data jpa用户手册上有这么一句话Improved compatibility with Hibernate 5.2.,这说明,spring data jpa又是hibernate的一个提升,下边先通过一个SQL:select * from User where name like '?' and age > ?的例子说明一下这三者在执行时候的区别:
首先看hibernate:
public interface UserDao{
List
}
public class UserDaoImpl implements UserDao{
@Override
public List
//具体hql查找:"from User where name like '%'"+firstName + "and age > " + age;
return hibernateTemplateMysql.execute(new HibernateCallback() {
@Override
public Object doInHibernate(Session session) throws HibernateException {
String hql = "from User where name like '?' and age > ?";
Query query = session.createQuery(hql);
query.setParameter(0, firstName+"");
query.setParameter(1, age);
return query.uniqueResult();
}
});
}
}
其次是mybatis:
@Mapper
public interface UserMapper {
Increment findByNameLikeAndAgeGreaterThan(String name,int age);
}
findByLastnameAndFirstname
… where x.lastname = ?1 and x.firstname = ?2
Or
findByLastnameOrFirstname
… where x.lastname = ?1 or x.firstname = ?2
Is,Equals
findByFirstname,findByFirstnameIs,findByFirstnameEquals
… where x.firstname = ?1
Between
findByStartDateBetween
… where x.startDate between ?1 and ?2
LessThan
findByAgeLessThan
… where x.age < ?1
LessThanEqual
findByAgeLessThanEqual
… where x.age <= ?1
GreaterThan
findByAgeGreaterThan
… where x.age > ?1
GreaterThanEqual
findByAgeGreaterThanEqual
… where x.age >= ?1
After
findByStartDateAfter
… where x.startDate > ?1
Before
findByStartDateBefore
… where x.startDate < ?1
IsNull
findByAgeIsNull
… where x.age is null
IsNotNull,NotNull
findByAge(Is)NotNull
… where x.age not null
Like
findByFirstnameLike
… where x.firstname like ?1
NotLike
findByFirstnameNotLike
… where x.firstname not like ?1
StartingWith
findByFirstnameStartingWith
… where x.firstname like ?1(parameter bound with appended %)
EndingWith
findByFirstnameEndingWith
… where x.firstname like ?1(parameter bound with prepended %)
Containing
findByFirstnameContaining
… where x.firstname like ?1(parameter bound wrapped in %)
OrderBy
findByAgeOrderByLastnameDesc
… where x.age = ?1 order by x.lastname desc
Not
findByLastnameNot
… where x.lastname <> ?1
In
findByAgeIn(Collection
… where x.age in ?1
NotIn
findByAgeNotIn(Collection
… where x.age not in ?1
True
findByActiveTrue()
… where x.active = true
False
findByActiveFalse()
… where x.active = false
IgnoreCase
findByFirstnameIgnoreCase
… where UPPER(x.firstame) = UPPER(?1)
下边记录一下切换的服务的后台架构,分为四层:controller、service、repository以及mapper,这样在修改的时候只修改repository即可,并添加新的层dao层,这样只要通过repository的切换就可以快速的实现spring data jpa和mybatis的快速切换,甚至可以同时使用这两个框架,从框架层面解决了切换的问题之后,由于spring data jpa的更新和添加是相似的两个方法,所以把所有的添加、批量添加、更新和批量更新抽象为以下的两个方法:
@Repository
public class CommonRepository
@PersistenceContext
protected EntityManager entityManager;
/**
* 添加和批量添加数据
* @param lists
*/
@Transactional
public void batchAddCommon(List<T> lists){
int size = lists.size();
for (int i = 0; i < size; i++) {
entityManager.persist(lists.get(i));
if (i % 100 == 0 || i == (size - 1)) {
entityManager.flush();
entityManager.clear();
}
}
}
/**
* 数据的批量更新
* @param lists
*/
@Transactional
public void batchUpdate(List<T> lists) {
int size = lists.size();
for (int i = 0; i < size; i++) {
entityManager.merge(lists.get(i));
if (i % 100 == 0 || i == (size - 1)) {
entityManager.flush();
entityManager.clear();
}
}
}
}
从这一点上讲spring data jpa会比mybatis要强很多,因为以上两个方法可以实现所有资源的更新和添加操作,而mybatis则需要为每一个资源实体去写添加、批量添加、更新和批量更新等,这会很麻烦。以下是切换过程中一些有记录意义的SQL,罗列一下:
//修改方法和删除方法都需要添加@Modifying,占位符是从1开始而不是开始的
@Modifying
@Query("update table n set n.column1 =?1 where n.column2 = ?2")
Integer updateObject(String one,String two);
@Modifying
@Query("delete from table n where n.column1 = ?1")
Integer getObject(String one);
//查询某一个字段的时候需要制定相应的类型,select全量数据的使用直接使用别名n即可,原生的SQL需要使用n.*
@Query("select n.column1 as String from table n where n.column2 is null or n.column2 =''")
List<String> getObject();
//原生SQL,进行了连表操作,并且查询了满足数组条件
@Query(value="select s., i. from table1 s, table2 i where i.column1 = s.column1 and i.column1 in (?1) order by s.id desc", nativeQuery = true)
List
在切换的使用遇到一个比较复杂的SQL,涉及联表、查询参数变量、in、case when、分页、group by等,下边给出mybatis和spring data jpa的写法:
spring data jpa方式:
public Page