Springboot JPA自定义sql执行
前言
SpringBoot JPA是一个移植性很强的数据持久层框架,普通的CURD操作可以免去写sql的过程而且非常方便。但是遇到复杂的查询或者是多条插入/删除的需求就难以满足。
JPA默认的saveAll()方法会单条执行插入sql当有上万条或者上百万条数据需要插入的时候,效率很低。我们可以采用拼接sql的方式,一千条或者一万条数据开一个事务合并成一条sql插入,这样效率会高不少。
查询
有时JPA的@Query的注解不能满足我们的需求。可以通过EntityManager进行sql拼接,解决复杂查询和多条插入的业务需求。
@PersistenceContext
private EntityManager entityManager;
//sql为拼接好的原生sql字符串,E为我们需要将查询结果封装为什么类
Query query = entityManager.createNativeQuery(sql,E.class);
List list = query.getResultList();
如果查询时没有匹配或没有设定E.class时,默认返回的结果是List
public void testNativeQuery(){
Query query = entityManager.createNativeQuery("select id, name, age from t_user");
List rows = query.getResultList();
for (Object row : rows) {
Object[] cells = (Object[]) row;
System.out.println("id = " + cells[0]);
System.out.println("name = " + cells[1]);
System.out.println("age = " + cells[2]);
}
下标为0的究竟是什么我们也需要去数sql才能知道,为此,我们可以将查询返回的结果封装至map,这样key和value对应,看起来会舒服很多。可惜的是JPA的API中并没有提供这样的设置。其实很多JPA的底层实现都是支持返回Map对象的。例如:
EclipseLink的query.setHint(QueryHints.RESULT_TYPE,ResultType.Map);
Hibernate的.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
所以,如果我们想要返回Map并且确定底层用的是某一种JPA的实现时(如Hibernate)我们可以退而求其次,牺牲跨实现的特性来满足我们的需求:
public void testNativeQuery(){
Query query = entityManager.createNativeQuery("select id, name, age from t_user");
query.unwrap(SQLQuery.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
List rows = query.getResultList();
for (Object obj : rows) {
Map row = (Map) obj;
System.out.println("id = " + row.get("ID"));
System.out.println("name = " + row.get("NAME"));
System.out.println("age = " + row.get("AGE"));
}
即在查询前设置返回的结果集。这里需要注意的是,用Map肯定要比用Object数组来的效率低。所以要看性能下降是否在可接受范围内。
插入
插入过程和查询类似,不过不需要处理返回值的类型了。第一步也是先获得entityManager然后把sql循环拼接后执行。
拼接的时候可以使用StringBuffer类
int result = entityManager.createNativeQuery(sql).executeUpdate();
这边的result就是更新/插入后有几条数据变动。
结语
JPA的复杂查询和多条插入可以考虑用以上的方式,不得不说,Jpa的移植性比Mybatis还是要好上不少的,但是在面临复杂的业务可能我还是更喜欢Mybatis一些。感谢围观。