我有一个基于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查询?
对于那些好奇的人-当我试图在我的应用程序和相关论坛中同时创建用户帐户时出现了这个问题-我不希望论坛的users表映射到我的任何Java实体。
发布于 2013-08-16 06:13:31
您可以使用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");
}
}
发布于 2013-08-16 06:13:44
您可以使用EntityManager.createNativeQuery(String sql)直接使用sql代码,也可以使用EntityManager.createNamedQuery(String name)执行预编译的查询。您仍然使用spring管理的实体管理器,但使用的是非托管对象。
https://stackoverflow.com/questions/18262630
复制相似问题