13

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

Spring から@PersistenceContextアノテーションを挿入したエンティティ マネージャーでこれを試すと、ほとんど明らかな例外が発生します。

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 トランザクション駆動型環境でカスタム SQL クエリを実行する方法が他にあるのでしょうか?

この問題は、自分のアプリケーションと関連するフォーラムで同時にユーザー アカウントを作成しようとしたときに発生しました。フォーラムのユーザー テーブルを自分の Java エンティティにマップしたくありませんでした。

4

2 に答える 2

13

createNativeQueryを使用して、データベースで任意の SQL を実行できます。

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

上記の回答は引き続き当てはまりますが、この質問を見ている人にも関連する可能性のある追加情報を編集したいと思います。

確かにcreateNativeQueryメソッドを使用して、EntityManager を介してネイティブ クエリを実行できます。Spring Framework を使用している場合は、別の (おそらくより良い) 方法があります。

Spring でクエリを実行する (構成されたトランザクションで動作する) 別の方法は、JDBCTemplateを使用することです。同じアプリケーション内でJDBCTemplateJPA 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");
    }
}
于 2013-08-15T22:13:31.430 に答える
2

EntityManager.createNativeQuery(String sql)を使用して直接 SQL コードを使用するか、EntityManager.createNamedQuery(String name)を使用してプリコンパイル済みクエリを実行できます。まだSpring管理のEntity Managerを使用していますが、管理されていないオブジェクトに取り組んでいます

于 2013-08-15T22:13:44.070 に答える