Jpa自定义sql

Jpa自定义sql并执行

Posted by catmai on January 22, 2020

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,也就是查询到的每一行数据被作为一个object对象返回,这样的结果是我们无法知道对应的值表示什么意思。常见的处理方式是:

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一些。感谢围观。