JPA / EntityManager是否提供类似于Spring的RowMapper

时间:2015-05-06 21:39:34

标签: java spring hibernate jpa entitymanager

我正在努力标准化遗留代码库和新开发,以便使用EntityManager进行持久化。但是,为了成功,我需要提供突破本机SQL并手动处理结果的能力。 JPA EntityManager几乎通过以下方式提供此功能:

em.createNativeQuery("select ... from my_table where ...");

但我缺少的是能够做Spring样式的RowMappers。我可以看到有一些机制很接近。我当然可以使用注释,xml等来映射实体,但我需要将代码放入结果集中每一行的处理中,因此这不是一个选项。我可以看到我可以传入SQLResultSetMapping,但是据我所知,这仅支持元数据映射。理想情况下我需要的是:

em.createNativeQuery("select ... from my_table where ...",  
  new RowMapper {  
    public Object mapRow(ResultSet rs, int rowNum) throws SQLException {  
         MyObject o = new MyObject();  
         o.setMyCustomProperty( rs.get...() );  
         //...  
         return b;  
    }  
  }  
); 

是否存在与上述相同的内容?我可以构造一个SQLResultSetMapping,它完全控制上面的映射。

如果没有,我是否可以至少以标准方式获取EM下的数据源,以便我可以使用JdbcTemplate包装它,而不是有两个配置数据源的路径?

DataSource ds = em.getDataSource();  
JdbcTemplate t = new JdbcTemplate(ds);  

感谢您的帮助。

3 个答案:

答案 0 :(得分:2)

实现此目的的一种方法是通过Hibernate会话(假设您正在使用Hibernate)并使用其https://docs.jboss.org/hibernate/core/3.3/api/org/hibernate/transform/ResultTransformer.html。代码应该如下所示,您不需要遍历列表两次:

ResolverStyle

希望这有帮助。

答案 1 :(得分:2)

JPA原生查询结果可以映射到DTO POJO类:

  • DTO POJO课程

    @lombok.Getter
    @lombok.AllArgsConstructor
    public class MyDto {
        private String x;
        private Long y;
    }
    
  • 存储库bean:

    @Repository
    public class MyRepository {
    
        @PersistenceContext private EntityManager em;
    
        static final String MY_SQLMAP = "My-SQL-Mapping";
    
        public List<MyDto> findMy() {
            Query query = em.createNativeQuery("select x, y from my_table", MY_SQLMAP);
            return query.getResultList();
        }
    
        @SqlResultSetMapping(name= MY_SQLMAP, classes = {
            @ConstructorResult(targetClass = MyDto.class,
                columns = {
                    @ColumnResult(name="x",type = String.class),
                    @ColumnResult(name="y",type = Long.class)
                }
            )
        }) @Entity class MyCfgEntity{@Id int id;} // <- walkaround
    
    }
    

答案 2 :(得分:0)

另一种方法是,如果您不想拥有该解决方案实体,那么将其置于任何有效实体之上,例如

@Entity
@SqlResult(...)

但是,您将添加与您的实体无关的映射。不幸的是,如果不在实体上,JPA(如JPA 2.1)不会处理SqlResult注释。另一个选择是将它添加到XML ORM映射文件,类似这样。

<?xml version="1.0" encoding="UTF-8" ?>

<entity-mappings xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xsi:schemaLocation="
                 http://xmlns.jcp.org/xml/ns/persistence/orm
                 http://xmlns.jcp.org/xml/ns/persistence/orm_2_1.xsd" version="2.1">

    <sql-result-set-mapping name="MySummaryResultMapping">
        <constructor-result target-class="model.MySummaryResult">
            <column name="x" class="java.lang.String" />
            <column name="average" class="java.lang.Double"/>
            <column name="foo" class="java.lang.Double"/>
            <column name="bar" class="java.lang.Integer"/>
        </constructor-result>
    </sql-result-set-mapping>
</entity-mappings>

然后在设置实体管理器的Spring环境中,您可以加载映射XML

<bean id="myEntityManagerFactory"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="packagesToScan" value="model"/>
    <property name="mappingResources" value="META-INF/persistenceMapping.xml" />
    ...
</bean>

通过这种方式,您可以执行类似的操作并使用POJO返回查询结果。

@Component
public class MyReportDAO {

@PersistenceContext
private EntityManager entityManager;

public MySummaryResult calculateReportFoo(String foo, LocalDate startDay, LocalDate endDay) {
    Query query = entityManager.createNativeQuery("SELECT x,  AVG(bar_column)  AS average, SUM(foo) AS foo," +
            " bar " +
            " FROM My_table WHERE FOO= :foo AND bar_DATE BETWEEN :startDay AND :endDay" +
            " GROUP BY x,bar", "MySummaryResultMapping");
    query.setParameter("foo", foo);
    query.setParameter("startDay", startDay);
    query.setParameter("endDay", endDay);
    return (MySummaryResult) query.getSingleResult();
  }
}