简体   繁体   English

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

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

I have an application built on Spring. 我有一个基于Spring的应用程序。 I let the Spring do the all @Transactional magic and everything works fine as long as I operate on my entities that are mapped to Java objects. 我让Spring执行所有@Transactional魔术,只要我对映射到Java对象的实体进行操作,一切正常。

However, when I want to do some custom job on a table that is not mapped to any of my Java entities, I'm stuck. 但是,当我想在未映射到任何Java实体的表上执行某些自定义作业时,我陷入困境。 Some time ago, I found a solution to execute a custom query like this: 前段时间,我找到了一个执行自定义查询的解决方案,如下所示:

// 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();

When I try this with the entity manager injected from Spring with the @PersistenceContext annotation, I receive almost obvious exception: 当我使用带有@PersistenceContext注释的Spring注入的实体管理器尝试这个时,我收到了几乎明显的异常:

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

I finally managed to extract non-shared Entity Manager like this: 我终于设法提取非共享实体管理器,如下所示:

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

Nevertheless, I find this solution neither comfortable nor elegant. 不过,我发现这种解决方案既不舒适也不优雅。 I just wonder if there is any other way to run custom SQL queries in this Spring-transactional-driven environment? 我只是想知道在这个Spring-transactional驱动的环境中是否还有其他方法可以运行自定义SQL查询?

For those who are curious - this problem appeared when I tried to create user accounts in my application and in the related forum at once - I did not want the forum's users table to be mapped to any of my Java entities. 对于那些好奇的人 - 当我尝试在我的应用程序和相关论坛中同时创建用户帐户时出现了这个问题 - 我不希望论坛的用户表被映射到我的任何Java实体。

You can use createNativeQuery to execute any arbitrary SQL on your database. 您可以使用createNativeQuery在数据库上执行任意SQL。

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

The above answer still holds true but I would like to edit in some additional information that may also be relevant to people looking at this question. 上述答案仍然适用,但我想编辑一些其他信息,这些信息也可能与查看此问题的人有关。

While it is true that you can use the createNativeQuery method to execute native queries through an EntityManager; 虽然您可以使用createNativeQuery方法通过EntityManager执行本机查询; there is an alternative (arguably better) way of doing it if you are using the Spring Framework. 如果您使用Spring Framework,还有一种替代方法(可以说是更好的方法)。

The alternative method for executing queries with Spring (that will behave with the configured transactions) is to use the JDBCTemplate . 使用Spring执行查询的替代方法(将使用已配置的事务执行)是使用JDBCTemplate It is possible to use both the JDBCTemplate and a JPA EntityManager within the same application. 可以在同一个应用程序中同时使用JDBCTemplate JPA EntityManager。 The configuration would look something like this: 配置看起来像这样:

InfrastructureConfig.class: 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: 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;
    }
}

And an example repository that would use both JPA and JDBC: 以及将同时使用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");
    }
}

You can use EntityManager.createNativeQuery(String sql) to use direct sql code or use EntityManager.createNamedQuery(String name) to execute precompiled query. 您可以使用EntityManager.createNativeQuery(String sql)来使用直接sql代码或使用EntityManager.createNamedQuery(String name)来执行预编译查询。 You still use spring-managed Entity manager, but working on non managed objects 您仍然使用Spring管理的实体管理器,但处理非托管对象

暂无
暂无

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

相关问题 如何使用 Mockito 模拟对 spring 管理的 @Bean 的静态访问? - How to mock static access to spring-managed @Bean with Mockito? 如何在Spring管理的MySQL JDBC连接上设置useUnicode = true和characterEncoding = utf8属性 - How to set useUnicode=true and characterEncoding=utf8 properties on Spring-managed MySQL JDBC connection 有一个共同的SpringApplicationContextProvider供所有bean使用spring-managed和其他 - Having a common SpringApplicationContextProvider to be used by all bean spring-managed and otherwise 如何获取EntityManager执行本机SQL - How get EntityManager to execute native sql 使用ORM,EntityManager,@ Query的Spring Data Repository,处理自定义SQL查询的最优雅方式是什么? - Spring Data Repository with ORM, EntityManager, @Query, what is the most elegant way to deal with custom SQL queries? 如何使用@Transactional方法将jmockit的模拟对象注入弹簧管理对象? - How to inject mocked object by jmockit to a spring managed object with @Transactional method? Spring 启动 + Hibernate + JPA 没有事务实体管理器可用 - Spring boot + Hibernate + JPA No transactional EntityManager available Spring Web App + JPA没有可用的事务性EntityManager - Spring Web App + JPA No transactional EntityManager available Ibatis startBatch()仅适用于SqlMapClient自己的启动和提交事务,不适用于Spring管理的事务 - Ibatis startBatch() only works with SqlMapClient's own start and commit transactions, not with Spring-managed ones 按下JSF命令按钮两次调用了Spring管理的请求范围的Bean构造函数 - Spring-managed request scoped bean constructor called twice on press of JSF command button
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM