原文:

最近重构以前写的服务,最大的一个变动是将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<User> findByNameLikeAndAgeGreaterThan(String firstName,Integer age);
} public class UserDaoImpl implements UserDao{
@Override
public List<User> findByFirstNameAndAge(String firstName, Integer age) {
//具体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);
} <select id="findByNameLikeAndAgeGreaterThan" parameterType="java.lang.Integer" resultMap="UserMap">
select
u.*
from
user u
<where>
u.name like ?1 and u.age>?2
</where>
</select> <resultMap id="UserMap" type="com.txxs.po.User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="age" property="age"/>
</resultMap>

最后是spring data jpa:

public interface UserDao extends JpaRepository<User, Serializable>{
List<User> findByNameLikeAndAgeGreaterThan(String name,Integer age);
}
//为了增加代码的可读性可以使用注解的方式,这样方法的命名就不需要严格按照规范
@Query("select * from User u where u.name like ?1 and u.age>?2")

通过上边代码的对比我们可以发现spring data jpa只要按照规范使用起来非常简单,下边是spring data jpa方法的命名规范:其他的可以参考用户手册
Keyword      Sample                                 JPQL snippet
And        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<Age> ages)                  … where x.age in ?1

NotIn          findByAgeNotIn(Collection<Age> ages)                … 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<T> { @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<Server> getObject(List<Integer> arry);

在切换的使用遇到一个比较复杂的SQL,涉及联表、查询参数变量、in、case when、分页、group by等,下边给出mybatis和spring data jpa的写法:

    <select id="queryNotUsedObject" parameterType="com.txxs.po.Object" resultType="java.lang.Integer" >
select
DISTINCT (i.column1),
SUM(CASE WHEN i.column7=#{column7} THEN 1 ELSE 0 END) used,
sum(CASE WHEN i.column7 IS NULL THEN 1 ELSE 0 END) not_used
from
table1 i,
table2 s
<where>
<if test="column2 != null and column2 != '' ">
and s.column2 = #{column2}
</if>
<if test="column3 != null and column3 != '' ">
and s.column3 = #{column3}
</if>
<if test="column4 != null and column4 != '' ">
and i.column4 like '${column4}%'
</if>
<if test="column5 != null and column5 != '' ">
and i.column5 like '${column5}%'
</if>
<if test="column6 != null and column6 != '' ">
and i.column6 like '${column6}%'
</if>
and s.column1 = i.column1
</where>
GROUP BY column1
having used =0 and not_used>0
ORDER BY s.id DESC
<if test="page != null and page>=0" >
limit #{page} , #{size}
</if>
</select>

spring data jpa方式:

    public Page<Object> queryNotUsedObject(final Object query){
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery();
//查询的根
Root<Server> root = criteriaQuery.from(entityManager.getMetamodel().entity(Object.class));
//判断参数
List<Predicate> predicates = new ArrayList<Predicate>();
if(null != query.getColumn1()){
predicates.add(criteriaBuilder.equal(root.get("Column1"), query.getColumn1()));
}
if(null != query.getColumn2()){
predicates.add(criteriaBuilder.equal(root.get("Column2"), query.getColumn2()));
}
//联表操作
if(null != query.getColumn3()){
predicates.add(criteriaBuilder.equal(root.join("table1Column").get("Column3"), query.getColumn3()));
}
if(null != query.getColumn4()){
predicates.add(criteriaBuilder.equal(root.join("table1Column").get("Column4"), query.getColumn4()));
}
if(null != query.getColumn5()){
predicates.add(criteriaBuilder.equal(root.join("table1Column").get("Column5"), query.getColumn5()));
}
//拼接Sum
Expression<Integer> sumExpOne = criteriaBuilder.sum(criteriaBuilder.<Integer>selectCase().when(criteriaBuilder.equal(root.join("table1Column").get("Column6"), query.getColumn6()), 1).otherwise(0)).as(Integer.class);
Expression<Integer> sumExpTwo = criteriaBuilder.sum(criteriaBuilder.<Integer>selectCase().when(criteriaBuilder.isNull(root.join("table1Column").get("Column6")), 1).otherwise(0)).as(Integer.class);
//查询参数
List<Expression<?>> expressions = new ArrayList<Expression<?>>();
expressions.add(root.join("table1Column").get("Column1"));
//having参数
List<Predicate> predicateArrayList = new ArrayList<Predicate>();
Predicate predicate = criteriaBuilder.equal(sumExpOne,0);
predicate = criteriaBuilder.and(predicate,criteriaBuilder.greaterThan(sumExpTwo,0));
predicateArrayList.add(predicate);
//拼接SQL
criteriaQuery.multiselect(expressions.toArray(new Expression[expressions.size()])).distinct(true);
criteriaQuery.where(predicates.toArray(new Predicate[predicates.size()]));
criteriaQuery.groupBy(root.join("table1Column").get("Column1"));
criteriaQuery.having(predicateArrayList.toArray(new Predicate[predicateArrayList.size()]));
//获取第一次执行的结果
final List<Integer> list = entityManager.createQuery(criteriaQuery).getResultList(); Sort sort = new Sort(Sort.Direction.DESC, "id");
Pageable objectDao.findAll(new Specification<Object>(){
@Override
public Predicate toPredicate(Root<Object> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
//判断参数
List<Predicate> predicates = new ArrayList<Predicate>();
predicates.add(root.get("id").in(list));
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
},pageable);
}

上边代码里边很多column不对应,是为了隐去痕迹,方法已经测试通过,从上边的代码看spring data jpa对于复杂语句的支持不够,需要通过代码的方式实现,而这种方式代码的可读性会比较差,优化等都会有一些难度

最后总结一下就是如果业务简单实用spring data jpa即可,如果业务复杂还是实用mybatis吧

spring data jpa还是只使用简单的操作.

感觉最终还是要用mybatis,觉得jpa这种东西只适合简单的增删改查比较多、SQL不怎么变化的情况。

我们可以二者结合使用!!

实例对比 hibernate, spring data jpa, mybatis 选型参考的更多相关文章

  1. Spring data Jpa,Mybatis,读写锁,@Lock 使用

    Spring data jpa 支持注解式的读写锁(悲观锁),实际上这个东西硬编码也简单,但是基于Jpa 命名方式定义的Sql,只能用注解添加支持读写锁了, 不了解读写锁的可以点这里 mysql读写锁 ...

  2. 【hibernate spring data jpa】执行了save()方法 sql语句也执行了,但是数据并未插入数据库中

    执行了save()方法  sql语句也执行了,但是数据并未插入数据库中 解决方法: 是因为执行了save()方法,也执行了sql语句,但是因为使用的是 @Transactional 注解,不是手动去提 ...

  3. ORM框架 Mybatis、Hibernate、Spring Data JPA之到底该用谁,谁更牛*

    在持久层框架中无可厚非的就是mybatis了,但是也会经常被人问到为啥要用mybatis,为啥不用hibernate,jpa.很多人各级都是地铁爷爷看手机的表情,似乎从来没想过这个问题.“公司叫用我就 ...

  4. 来说说JPA、Hibernate、Spring Data JPA之间的什么关系?

    目录 JPA Hibernate Spring Data JPA 实践 来说说JPA.Hibernate.Spring Data JPA之间的什么关系 Java 持久层框架访问数据库的方式大致分为两种 ...

  5. Spring Boot 2.x基础教程:使用Spring Data JPA访问MySQL

    在数据访问这章的第一篇文章<Spring中使用JdbcTemplate访问数据库> 中,我们已经介绍了如何使用Spring Boot中最基本的jdbc模块来实现关系型数据库的数据读写操作. ...

  6. Hibernate、Mybatis与Spring Data JPA

    从零开始集成Springboot+MyBatis+JPA https://www.jianshu.com/p/e14c4a6f6871 MyBatis 与Hibernate的区别 http://xhr ...

  7. jdbc、jpa、spring data jpa、hibernate、mybatis之间的关系及区别

    基础概念 jdbc(Java DataBase Connectivity)是java连接数据库操作的原生接口.JDBC对Java程序员而言是API,对实现与数据库连接的服务提供商而言是接口模型.作为A ...

  8. Spring Data JPA、MyBatis还有Hibernate有什么区别

    原文:https://www.imooc.com/article/19754?block_id=tuijian_wz Spring Data JPA.MyBatis还有Hibernate有什么区别 2 ...

  9. MyBatis 与 Spring Data JPA 选择谁?

    MyBatis 与 Spring Data JPA 选择谁? https://www.v2ex.com/t/285081 jpa predicate优缺点 https://blog.csdn.net/ ...

随机推荐

  1. 前后台分离开发--文件上传与下载,cookie,session

    一.前后台分离开发的概念 ''' 1. 前台页面运行在前台服务器上,负责页面的渲染(静态文件的加载)与转跳 2. 后台代码运行在后台服务器上,负责数据的处理(提供数据请求的接口) ''' #如果没有前 ...

  2. 【做题】agc002D - Stamp Rally——整体二分的技巧

    题意:给出一个无向连通图,有\(n\)个顶点,\(m\)条边.有\(q\)次询问,每次给出\(x,y,z\),最小化从\(x\)和\(y\)开始,总计访问\(z\)个顶点(一个顶点只计算一次),经过的 ...

  3. 【做题】cf603E——线段树分治

    首先感谢题解小哥,他在标算外又总结了三种做法. 此处仅提及最后一种做法. 首先考虑题目中要求的所有结点度数为奇数的限制. 对于每一个联通块,因为所有结点总度数是偶数,所以总结点数也必须是偶数的.即所有 ...

  4. hihoCoder week15 最近公共祖先·二

    tarjan求lca  就是dfs序中用并查集维护下,当访问到询问的第二个点u的时候  lca就是第一点的find(fa[v]) fa[v] = u; // 当v为u的儿子 且 v已经dfs完毕 #i ...

  5. module.exports 与 exports

    module.exports 与 exports 注意:1 对于要导出的属性,可以简单直接挂到 exports 对象上2 对于类,为了直接使导出的内容作为类的构造器可以让调用者使用 new 操作符创建 ...

  6. 51nod 1378 夹克老爷的愤怒(树型dp+贪心)

    http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1378 题意: 思路:要想放得少,尽量放在叶子节点处,叶子节点处点比较多. ...

  7. Android之使用传感器获取相应数据

    Android的大部分手机中都有传感器,传感器类型有方向.加速度(重力).光线.磁场.距离(临近性).温度等. 方向传感器:   Sensor.TYPE_ORIENTATION 加速度(重力)传感器: ...

  8. layer 弹出层 回调函数调用 弹出层页面 函数

    1.项目中用到layer 弹出层,定义一个公用的窗口,问题来了窗口弹出来了,如何保存页面上的数据呢?疯狂百度之后,有了结果,赶紧记下. 2.自己定义的公共页面方法: layuiWindow: func ...

  9. mybatis(错误一) 项目启动时报“Result Maps collection already contains value forxxx”的解决方案

    Result Maps collection already contains value for xyx.dsw.dao.mapper.admin.quotationwish.TempTestTab ...

  10. 【Selenium2】【问题】

    [iframe 和 HTML 相互嵌套] 比如126登录页,我的几个方法都不好用 1. iframeFather = driver.find_element(By.XPATH,"//div[ ...