JPA:没有@OneToMany注释的左连接

我的数据库中有一个OneToMany关系,但我不希望Hibernate直接管理它。

这种关系是翻译,但DTO代表自己是翻译的注册表:

@Entity @Table(name = "my_table") public class MyTable { @Id @Column(name = "id", nullable = false, unique = true) private Integer id; @Transient private String lang; @Transient private String text; // getters and setters ... } @Entity @Table(name = "my_table_translation") public class MyTableTranslation { @Id @Column(name = "id", nullable = false, unique = false) private Integer id; @Id @Column(name = "lang", nullable = false, unique = false, length = 2) private String lang; @Column(name = "text", nullable = false, unique = false, length = 200) private String text; // getters and setters ... } 

我希望有一个带有lang参数的特定findAll(String lang)方法,并使用Specification Criteria来构建查询。 像这样的东西:

 public void findAll(String language) { List list = repository.findAll(new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { // something there return ...; } }); } 

事实是我不知道该怎么做,因为我不能使用JOIN子句,因为我在模型中没有代表关系的属性。

我尝试使用带有SQL表示法的SELECT … FROM … LEFT JOIN查询,

 SELECT t1, t2 FROM MyTable t1 LEFT JOIN MyTableTranslation t2 ON t1.id = t2.id 

它有效,但不是很理想。 生成的对象列表是每个项目的2个对象的列表:一个是MyTable对象,另一个是MyTableTranslation相关对象。 我需要解析列表并使用Apache Commons库中的PropertyUtils类以编程方式构建对象。

我觉得它不干净……有没有人知道如何轻松实现,而不使用SQL表示法?

Marc,你可以做以下工作使它工作,你现在不需要复杂的join子句或谓词。 嵌入式H2数据库和JUnit测试中的简单实现将足以用于概念validation( POC ),如下所述

注意:

  1. 我正在使用Spring + Plain JPA with Hibernate POC的 Spring + Plain JPA with Hibernate实现。
  2. 我正在使用Spring推荐的管理事务的方式。
  3. com.mycompany.h2.jpa包中包含实体类。
  4. 看看mytable.sql ,它具有与您的需求相似的结构。

MyTable.java

 @Entity @Table(name = "my_table") public class MyTable implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy=GenerationType.IDENTITY) @Column(name = "id", nullable = false, unique = true) @JoinColumn(referencedColumnName="id", insertable=true, updatable=false) private Long id; @Column(name = "lang", unique=true) private String lang; @Column(name = "text") private String text; @OneToMany(fetch = FetchType.LAZY) @JoinColumn(name="id", insertable=true, updatable=true, referencedColumnName="id") private List translations = new ArrayList(); ... // getters and setters, toString() } 

MyTableTranslation.java

 @Entity @Table(name = "my_table_translation") public class MyTableTranslation implements Serializable { private static final long serialVersionUID = 11L; @Id @Column(name = "id") private Long id; @Id @Column(name = "speaker") String speaker; ... // getters and setters, toString() } 

TestH2DatabaseConfiguration.java

 @Configuration @EnableTransactionManagement public class TestH2DatabaseConfiguration { final static Logger log = LoggerFactory.getLogger(TestH2DatabaseConfiguration.class); @Bean @Qualifier("dataSource") public DataSource h2DataSource() { return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).addScript("classpath:mytable.sql").build(); } @Bean public EntityManagerFactory entityManagerFactory() { HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); jpaVendorAdapter.setGenerateDdl(true); LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean(); factoryBean.setDataSource(h2DataSource()); factoryBean.setJpaVendorAdapter(jpaVendorAdapter); factoryBean.setPackagesToScan("com.mycompany.h2.jpa"); factoryBean.setPersistenceUnitName("my_table"); Properties prop = new Properties(); prop.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); prop.put("hibernate.show_sql", "true"); prop.put("hibernate.hbm2ddl.auto", "none"); factoryBean.setJpaProperties(prop); factoryBean.afterPropertiesSet(); return factoryBean.getObject(); } @Bean public PlatformTransactionManager transactionManager() { JpaTransactionManager txManager = new JpaTransactionManager(); txManager.setEntityManagerFactory(entityManagerFactory()); return txManager; } @Bean public MyTableDAO myTableDAO() { return new MyTableDAOJPAImpl(); } @Bean public MyTableServiceImpl myTableService() { MyTableServiceImpl myTableService = new MyTableServiceImpl(); myTableService.setMyTableDAO(myTableDAO()); return myTableService; } } 

MyTableService.java

 public interface MyTableService { public MyTable saveMyTableTranslation(MyTable myTable); public List getAllMyTables(); public MyTable getMyTable(Long entityId); public MyTable getMyTable(String lang); } 

MyTableServiceImpl.java

 @Transactional public class MyTableServiceImpl implements MyTableService { final static Logger log = LoggerFactory.getLogger(MyTableServiceImpl.class); private MyTableDAO myTableDAO; public void setMyTableDAO(MyTableDAO myTableDAO) { this.myTableDAO = myTableDAO; } public MyTable saveMyTableTranslation(MyTable myTable) { return myTableDAO.saveMyTableTranslation(myTable); } public List getAllMyTables() { return myTableDAO.getAllMyTables(); } public MyTable getMyTable(Long entityId) { return myTableDAO.getMyTable(entityId); } public MyTable getMyTable(String lang) { return myTableDAO.getMyTable(lang); } } 

MyTableDAO.java

 public interface MyTableDAO { public MyTable saveMyTableTranslation(MyTable myTable); public List getAllMyTables(); public MyTable getMyTable(Long entityId); public MyTable getMyTable(String lang); } 

MyTableDAOJPAImpl.java

 public class MyTableDAOJPAImpl implements MyTableDAO { final static Logger log = LoggerFactory.getLogger(MyTableDAOJPAImpl.class); @PersistenceContext private EntityManager entityManager; public MyTable saveMyTableTranslation(MyTable myTable) { entityManager.persist(myTable); return myTable; } @SuppressWarnings("unchecked") public List getAllMyTables() { return (List) entityManager.createQuery("FROM MyTable").getResultList(); } public MyTable getMyTable(Long entityId) { return (MyTable) entityManager.createQuery("FROM MyTable m WHERE m.id = :id ").setParameter("id", entityId).getSingleResult(); } public MyTable getMyTable(String lang) { return (MyTable) entityManager.createQuery("FROM MyTable m WHERE m.lang = :lang ").setParameter("lang", lang).getSingleResult(); } } 

MyTableTest.java (一个JUnit测试类)

 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { TestH2DatabaseConfiguration.class }, loader = AnnotationConfigContextLoader.class) public class MyTableTest extends AbstractTransactionalJUnit4SpringContextTests { final static Logger log = LoggerFactory.getLogger(MyTableTest.class); @Autowired @Qualifier("myTableService") MyTableService myTableService; @Test public void test() throws ParseException { MyTable parent = new MyTable(); parent.setLang("Italian"); parent.setText("Fast..."); MyTableTranslation child = new MyTableTranslation(); child.setSpeaker("Liotta"); parent = myTableService.saveMyTableTranslation(parent); log.debug("parent ID : " + parent.getId()); MyTable spanishTables= myTableService.getMyTable("Spanish"); List spanishTranslations = spanishTables.getTranslations(); log.debug("spanishTranslations SIZE : " + spanishTranslations.size()); for (MyTableTranslation myTableTranslation : spanishTranslations) { log.debug("myTableTranslation -> : " + myTableTranslation); } } } 

mytable.sql

 CREATE TABLE IF NOT EXISTS my_table ( id IDENTITY PRIMARY KEY, lang VARCHAR UNIQUE, text VARCHAR ); delete from my_table; INSERT INTO my_table VALUES (1, 'Spanish', 'Beautiful...'); INSERT INTO my_table VALUES (2, 'English', 'Great...'); INSERT INTO my_table VALUES (3, 'French', 'Romantic...'); CREATE TABLE IF NOT EXISTS my_table_translation ( id INTEGER, speaker VARCHAR ); delete from my_table_translation; INSERT INTO my_table_translation VALUES (1, 'Eduardo'); INSERT INTO my_table_translation VALUES (1, 'Diego'); INSERT INTO my_table_translation VALUES (2, 'George'); INSERT INTO my_table_translation VALUES (3, 'Pierre'); 

我尝试使用@OneToMany注释,然后更改了我的DTO:

 @Entity @Table(name = "my_table") public class MyTable { @Id @Column(name = "id", nullable = false, unique = true) private Integer id; @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY) @JoinColumn(name = "id", referencedColumnName = "id") private List translations = new ArrayList(); // getters and setters ... } 

并将标准改为:

 public void findAll(String isolang) { List list = repository.findAll(new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { Join langJoin = root.join("translations", JoinType.LEFT); return cb.equal(langJoin.get("lang"), isolang); } }); } 

但该清单没有任何物品。 如果我将FetchType更改为EAGER ,谓词无效,我会获得所有语言。 我现在不知道怎么办……