Mybatis與Jpa的區(qū)別和性能對比總結(jié)
前言
這幾天聽朋友說JPA很好用,根本不用寫sql。我在想一個程序員不寫sql還能叫程序員?而且越高級的工具封裝越多的工具,可拓展性和效率就非常的低,況且我本身非常不喜歡過于封裝的東西,平時喜歡手寫sql,所以一直都是用mybatis去寫業(yè)務(wù)。然后發(fā)現(xiàn)jpa的saveAll()批量插入批量更新速度太慢了,導致一些用excel導入的一些東西非常慢,弄得原本同步可以解決的事情每次導入都要開啟一個異步,個人感覺這種做法非常不好。因為異步其實就是對當前的業(yè)務(wù)不影響去另外的時間段去做,例如跑定時任務(wù),異步更新增量信息等。代碼里非常多異步包異步的東西,也就是說excel導入是異步,然后jpa又慢,異步里面又包涵異步,整個鏈路非常長,可能發(fā)生問題都要排查半天。
安裝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作為父類就行
創(chuàng)建一個類
@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條數(shù)據(jù)。讓我們試試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條循環(huán)n條if判斷,那樣性能肯定是衰減得非常多的拉
結(jié)論
我在網(wǎng)上看到加入以下這些參數(shù)可以變成批量的,但是筆者試過根本沒用,可能想要解決這個問題,需要重寫他的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比較,但是作為一個碼農(nóng)深知,技術(shù)是為業(yè)務(wù)服務(wù)的。Jpa當然也有他的好處,例如創(chuàng)建一些方法findAllByIdIn(List ids)就可以直接獲取到以這個條件查詢的列表,還有findAllByOrderIdAndOrderType(String orderId, String orderType)這種一樣也可以,可以說非常的方便,也不需要再去寫sql,他會全自動的完成你的查詢操作。
小結(jié)
開發(fā)一個小型項目,Jpa效率肯定是比Mybatis高的,但是因為業(yè)務(wù)需求迭代更新越來越快,Jpa明顯是滿足不了很多東西,而且維護起來看Sql也是比MyBatis難。所以我更偏向于Mybatis,寫的Sql也更加簡潔更容易維護。
到此這篇關(guān)于Mybatis與Jpa的區(qū)別和性能對比的文章就介紹到這了,更多相關(guān)Mybatis與Jpa區(qū)別和性能內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java用三元運算符判斷奇數(shù)和偶數(shù)的簡單實現(xiàn)
這篇文章主要介紹了Java用三元運算符判斷奇數(shù)和偶數(shù)的簡單實現(xiàn),需要的朋友可以參考下2014-02-02
Mybatis如何使用ognl表達式實現(xiàn)動態(tài)sql
這篇文章主要介紹了Mybatis使用ognl表達式實現(xiàn)動態(tài)sql的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
SpringBoot利用Redis實現(xiàn)防止訂單重復提交的解決方案
在涉及訂單操作的業(yè)務(wù)中,防止訂單重復提交是一個常見需求,用戶可能會因誤操作或網(wǎng)絡(luò)延遲而多次點擊提交訂單按鈕,導致訂單重復提交,所以本文給大家介紹了SpringBoot利用Redis實現(xiàn)防止訂單重復提交的解決方案,需要的朋友可以參考下2024-10-10
Spring Security整合KeyCloak保護Rest API實現(xiàn)詳解
這篇文章主要為大家介紹了Spring Security整合KeyCloak保護Rest API實現(xiàn)實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11

