Mybatis与Jpa的区别和性能对比总结
作者:Kakki 发布时间:2022-09-17 16:08:45
前言
这几天听朋友说JPA很好用,根本不用写sql。我在想一个程序员不写sql还能叫程序员?而且越高级的工具封装越多的工具,可拓展性和效率就非常的低,况且我本身非常不喜欢过于封装的东西,平时喜欢手写sql,所以一直都是用mybatis去写业务。然后发现jpa的saveAll()批量插入批量更新速度太慢了,导致一些用excel导入的一些东西非常慢,弄得原本同步可以解决的事情每次导入都要开启一个异步,个人感觉这种做法非常不好。因为异步其实就是对当前的业务不影响去另外的时间段去做,例如跑定时任务,异步更新增量信息等。代码里非常多异步包异步的东西,也就是说excel导入是异步,然后jpa又慢,异步里面又包涵异步,整个链路非常长,可能发生问题都要排查半天。
安装jpa和mybatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
这些东西只要引入一个springboot的xml作为父类就行
创建一个类
@Data
public class TestMybatis {
private Long id;
/**
* 域账号
*/
private String userId;
/**
* 主度量
*/
private String mainMetric;
/**
* 子度量
*/
private String subMetric;
/**
* 度量条目
*/
private String metricItem;
}
@SuppressWarnings("serial")
@javax.persistence.Entity
@javax.persistence.Table(name = "test")
@lombok.Data
public class TestJpa {
@javax.persistence.Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 域账号
*/
private String userId;
/**
* 主度量
*/
private String mainMetric;
/**
* 子度量
*/
private String subMetric;
/**
* 度量条目
*/
private String metricItem;
}
/**
* @author Kakki
* @version 1.0
* @create 2021-06-17 17:39
* 这个是用来Jpa跟Mapper差不多
*/
@Repository
public interface TestRee extends JpaRepository<TestRe, String> {
}
这是mybatis的xml
<insert id="insertList">
insert into test(user_id,main_metric, sub_metric, metric_item) values
<foreach collection="param" item="item" separator=",">
(#{item.userId}, #{item.mainMetric}, #{item.subMetric}, #{item.metricItem})
</foreach>
</insert>
下面我们来看看速度
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ColaDemoApplication.class})
class ColaDemoApplicationTests {
@Autowired
private TestRee testRee;
@Autowired
private MetricMapper metricMapper;
@Test
void contextLoads() {
List<TestJpa> jpaList = new ArrayList<>(1000);
List<com.kakki.colademo.gatewayimpl.database.dataobject.TestMybatis> mybatisList = new ArrayList<>(1000);
for (int i = 0; i < 1000; i++) {
TestJpa testJpa = new TestJpa();
testJpa.setMainMetric(String.format("mainMetric%d", i));
testJpa.setSubMetric(String.format("subMetric%d", i));
testJpa.setUserId(String.format("userId%d", i));
testJpa.setMetricItem(String.format("metricItem%d", i));
jpaList.add(testRe);
com.kakki.colademo.gatewayimpl.database.dataobject.TestMybatis testMybatis = new com.kakki.colademo.gatewayimpl.database.dataobject.TestMybatis();
testMybatis.setMainMetric(String.format("mainMetric%d", i));
testMybatis.setSubMetric(String.format("subMetric%d", i));
testMybatis.setUserId(String.format("userId%d", i));
testMybatis.setMetricItem(String.format("metricItem%d", i));
mybatisList.add(testR);
}
StopWatch jpa = new StopWatch();
jpa.start();
testRee.saveAll(jpaList);
jpa.stop();
log.info("[jpa]{}ms", jpa.getTotalTimeMillis());
StopWatch m = new StopWatch();
m.start();
metricMapper.insertList(mybatisList);
m.stop();
log.info("[m]{}ms", m.getTotalTimeMillis());
}
}
22:35:10.708 [main] INFO c.e.c.ColaDemoApplicationTests - [jpa]10576ms
22:35:31.366 [main] INFO c.e.c.ColaDemoApplicationTests - [m]138ms
可以说相差差不多10倍了吧?这仅仅只是1000条数据。让我们试试10000条
22:36:48.505 [main] INFO c.e.c.ColaDemoApplicationTests - [jpa]8081ms
22:37:05.005 [main] INFO c.e.c.ColaDemoApplicationTests - [m]613ms
# 再试试10w条
22:38:49.085 [main] INFO c.e.c.ColaDemoApplicationTests - [jpa]65710ms
22:39:09.844 [main] INFO c.e.c.ColaDemoApplicationTests - [m]9448ms
那么这样能看出来很大的差距了吧?为什么会差距这么大呢?我们看看saveAll()源码
@Transactional
@Override
public <S extends T> List<S> saveAll(Iterable<S> entities) {
Assert.notNull(entities, "Entities must not be null!");
List<S> result = new ArrayList<S>();
for (S entity : entities) {
result.add(save(entity));
}
return result;
}
@Transactional
@Override
public <S extends T> S save(S entity) {
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
从上面可以看出来是一条条save进去的并且save里面还会去判断这个主键是否为空也就是说n条循环n条if判断,那样性能肯定是衰减得非常多的拉
结论
我在网上看到加入以下这些参数可以变成批量的,但是笔者试过根本没用,可能想要解决这个问题,需要重写他的saveAll()方法然后分片去插入或者更新这样性能会好很多。
spring.jpa.properties.hibernate.jdbc.batch_size=10000
spring.jpa.properties.hibernate.jdbc.batch_versioned_data=true
spring.jpa.properties.hibernate.order_inserts=true
spring.jpa.properties.hibernate.order_updates=true
当然今天我仅仅是用jpa的性能跟mybatis比较,但是作为一个码农深知,技术是为业务服务的。Jpa当然也有他的好处,例如创建一些方法findAllByIdIn(List ids)就可以直接获取到以这个条件查询的列表,还有findAllByOrderIdAndOrderType(String orderId, String orderType)这种一样也可以,可以说非常的方便,也不需要再去写sql,他会全自动的完成你的查询操作。
小结
开发一个小型项目,Jpa效率肯定是比Mybatis高的,但是因为业务需求迭代更新越来越快,Jpa明显是满足不了很多东西,而且维护起来看Sql也是比MyBatis难。所以我更偏向于Mybatis,写的Sql也更加简洁更容易维护。
来源:https://juejin.cn/post/6974775055397945352


猜你喜欢
- 1、String 是最基本的数据类型吗?不是。Java 中的基本数据类型只有 8 个 :byte、short、int、long、float、
- 自定义log4j日志文件命名规则项目中的日志需要采用一致的命名规范和文件规范,命名规则为:项目模块标识_index_日期时间_日志级别.lo
- 背景两张表,分别是 :sys_tbl,和 sys_field,其中:sys_tbl 是系统所有表的信息,包含两个字段 :code(表名),n
- 发一个库存程序,好像是几个礼拜之前写的吧,是一个用安卓实现的简易的计算器,写这个小程序之前,看了很多人写的计算器,觉得使用一个 EditTe
- 概述动态SQL:SQL语句会随着用户输入或外部条件的变化而变化 。例如:我们在做多条件查询的时候,编写SQL语句的查询操作,我们并不知道用户
- 前言:Java数据结构与算法专题会不定时更新,欢迎各位读者监督。本文从最简单的一个排序算法——桶排序开始,分析桶排序的实现思路,代码实现,性
- 聊一聊kotlin协程“低级”apiKotlin协程已经出来很久了,相信大家都有不同程度的用上了,由
- 0. 前言在上一篇中,我故意留下了查询的示范没讲。虽然说可以通过以下代码获取一个DataReader:IDataReader reader
- 在做 Android App 的时候,为了给用户省流量,为了不激起用户的愤怒,为了更好的用户体验,是需要根据用户当前网络情况来做一些调整的,
- 方式1:dependency 本地jar包<dependency> <groupId>com.jouyp
- 我们来讨论一个经常存在的账户转账的问题。账户A要转账给账户B。为了保证在转账的过程中A和B不被其他的线程意外的操作,我们需要给A和B加锁,然
- 这篇文章主要介绍了Java 比较接口comparable与comparator区别解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作
- 需求:用TreeSet集合存储多个学生信息(姓名,语文成绩,数学成绩),并遍历该集合;要按照总分从高到低进行排序分析:1.创建学生类 成员变
- 本文实例讲述了Android程序美化之自定义ListView背景的方法。分享给大家供大家参考,具体如下:在Android中,ListView
- 在SpringAMQP的发送方法中,接收消息的类型是Object,也就是说我们可以发送任意对象类型的消息,SpringAMQP会帮我们序列化
- 前言本文的多租户是基于多数据库进行实现的,数据是通过不同数据库进行隔离。下面话不多说,来看看详细的介绍:MyCat 基本配置首先针对多租户配
- 背景后台系统需要接入 企业微信登入,满足企业员工快速登入系统流程图简单代码说明自定义一套 springsecurity 认证逻辑主要就是 根
- 一、什么是Memcached?Memcached是danga.com开发的分布式内存对象缓存系统,所谓分布式,意味着它不是本地的,而是基于网
- Android绘图常用方法有哪些,下面一一为大家列举:1、有关画笔(Paint)的方法Paint mPaint= new Paint();m
- 在用HTML5做跨平台应用开发时,尝尝会用到java和js方法互调的问题,对初学者而言,可能会有点难,在这里分享一些自己在实际开发过程中的用