繁体   English   中英

如何使用Spring管理的事务性EntityManager执行自定义SQL查询

[英]How to execute custom SQL query with spring-managed transactional EntityManager

我有一个基于Spring的应用程序。 我让Spring执行所有@Transactional魔术,只要我对映射到Java对象的实体进行操作,一切正常。

但是,当我想在未映射到任何Java实体的表上执行某些自定义作业时,我陷入困境。 前段时间,我找到了一个执行自定义查询的解决方案,如下所示:

// em is instance of EntityManager
em.getTransaction().begin();
Statement st = em.unwrap(Connection.class).createStatement();
ResultSet rs = st.executeQuery("SELECT custom FROM my_data");
em.getTransaction().commit();

当我使用带有@PersistenceContext注释的Spring注入的实体管理器尝试这个时,我收到了几乎明显的异常:

java.lang.IllegalStateException: 
Not allowed to create transaction on shared EntityManager - 
use Spring transactions or EJB CMT instead

我终于设法提取非共享实体管理器,如下所示:

@Inject
public void myCustomSqlExecutor(EntityManagerFactory emf){
    EntityManager em = emf.createEntityManager();
    // the em.unwrap(...) stuff from above works fine here
}

不过,我发现这种解决方案既不舒适也不优雅。 我只是想知道在这个Spring-transactional驱动的环境中是否还有其他方法可以运行自定义SQL查询?

对于那些好奇的人 - 当我尝试在我的应用程序和相关论坛中同时创建用户帐户时出现了这个问题 - 我不希望论坛的用户表被映射到我的任何Java实体。

您可以使用createNativeQuery在数据库上执行任意SQL。

EntityManager em = emf.createEntityManager();
List<Object> results = em.createNativeQuery("SELECT custom FROM my_data").getResultList();

上述答案仍然适用,但我想编辑一些其他信息,这些信息也可能与查看此问题的人有关。

虽然您可以使用createNativeQuery方法通过EntityManager执行本机查询; 如果您使用Spring Framework,还有一种替代方法(可以说是更好的方法)。

使用Spring执行查询的替代方法(将使用已配置的事务执行)是使用JDBCTemplate 可以在同一个应用程序中同时使用JDBCTemplate JPA EntityManager。 配置看起来像这样:

InfrastructureConfig.class:

@Configuration
@Import(AppConfig.class)
public class InfrastructureConfig {

    @Bean //Creates an in-memory database.
    public DataSource dataSource(){
        return new EmbeddedDatabaseBuilder().build(); 
    }   

    @Bean //Creates our EntityManagerFactory
    public AbstractEntityManagerFactoryBean entityManagerFactory(DataSource dataSource){
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource);
        emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        return emf;
    }

    @Bean //Creates our PlatformTransactionManager. Registering both the EntityManagerFactory and the DataSource to be shared by the EMF and JDBCTemplate
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf, DataSource dataSource){
        JpaTransactionManager tm = new JpaTransactionManager(emf);
        tm.setDataSource(dataSource);
        return tm;
    }

}

AppConfig.class:

@Configuration
@EnableTransactionManagement
public class AppConfig {

    @Bean
    public MyService myTransactionalService(DomainRepository domainRepository) {
        return new MyServiceImpl(domainRepository);
    }

    @Bean
    public DomainRepository domainRepository(JdbcTemplate template){
        return new JpaAndJdbcDomainRepository(template);
    }

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate template = new JdbcTemplate(dataSource);
        return template;
    }
}

以及将同时使用JPA和JDBC的示例存储库:

public class JpaAndJdbcDomainRepository implements DomainRepository{

    private JdbcTemplate template;
    private EntityManager entityManager;

    //Inject the JdbcTemplate (or the DataSource and construct a new JdbcTemplate)
    public DomainRepository(JdbcTemplate template){
        this.template = template;
    }

    //Inject the EntityManager
    @PersistenceContext
    void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    //Execute a JPA query
    public DomainObject getDomainObject(Long id){
        return entityManager.find(id);
    }

    //Execute a native SQL Query
    public List<Map<String,Object>> getData(){
        return template.queryForList("select custom from my_data");
    }
}

您可以使用EntityManager.createNativeQuery(String sql)来使用直接sql代码或使用EntityManager.createNamedQuery(String name)来执行预编译查询。 您仍然使用Spring管理的实体管理器,但处理非托管对象

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM