[英]How to mock static access to spring-managed @Bean with Mockito?
[英]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.