2013-08-15 31 views
9

Tôi có một ứng dụng được xây dựng vào Mùa xuân. Tôi để cho mùa xuân làm tất cả các phép thuật @Transactional và tất cả mọi thứ hoạt động tốt miễn là tôi hoạt động trên các thực thể của tôi được ánh xạ tới các đối tượng Java.Cách thực hiện truy vấn SQL tùy chỉnh với giao dịch được quản lý bằng mùa xuân EntityManager

Tuy nhiên, khi tôi muốn thực hiện một số công việc tùy chỉnh trên một bảng không được ánh xạ tới bất kỳ thực thể Java nào của tôi, tôi bị kẹt. Một số thời gian trước đây, tôi tìm thấy một giải pháp để thực hiện một truy vấn tùy chỉnh như thế này:

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

Khi tôi thử điều này với người quản lý thực thể tiêm từ mùa xuân với @PersistenceContext chú thích, tôi nhận được ngoại lệ gần như rõ ràng:

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

Cuối cùng tôi quản lý để giải nén quản lý Entity không chia sẻ như thế này:

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

Tuy nhiên, tôi tìm thấy giải pháp này không phải cảm thấy thoải mái và cũng không e legant. Tôi tự hỏi liệu có cách nào khác để chạy các truy vấn SQL tùy chỉnh trong môi trường hướng-giao dịch mùa xuân này không?

Đối với những người tò mò - vấn đề này xuất hiện khi tôi cố gắng tạo tài khoản người dùng trong ứng dụng của tôi và trong diễn đàn liên quan cùng một lúc - Tôi không muốn bảng người dùng của diễn đàn được ánh xạ tới bất kỳ thực thể Java nào của tôi.

+0

Điều này không rõ ràng bạn làm gì với CMT và BMT ở cùng một nơi. –

Trả lời

8

Bạn có thể sử dụng createNativeQuery để thực thi bất kỳ SQL tùy ý nào trên cơ sở dữ liệu của mình.

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

Câu trả lời ở trên vẫn đúng nhưng tôi muốn chỉnh sửa một số thông tin bổ sung mà cũng có thể liên quan đến mọi người nhìn vào câu hỏi này.

Mặc dù đúng là bạn có thể sử dụng phương thức createNativeQuery để thực thi truy vấn gốc thông qua Trình quản lý thực thể; có một cách thay thế (được cho là tốt hơn) để thực hiện nó nếu bạn đang sử dụng Khung công tác Spring.

Phương pháp thay thế để thực hiện truy vấn bằng Spring (hoạt động với giao dịch được định cấu hình) là sử dụng JDBCTemplate. Có thể sử dụng cả JDBCTemplate một JPA EntityManager trong cùng một ứng dụng. Cấu hình sẽ giống như thế này:

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; 
    } 
} 

Và một kho lưu trữ ví dụ mà sẽ sử dụng cả hai JPA và 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"); 
    } 
} 
+0

Tuyệt vời! Tôi đã thử phương thức 'createQuery()' nhưng với cùng một ngoại lệ như trên. 'createNativeQuery()' giải quyết vấn đề, cảm ơn rất nhiều! – fracz