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 ),如下所述
注意:
- 我正在使用
Spring + Plain JPA with Hibernate
POC的Spring + Plain JPA with Hibernate
实现。 - 我正在使用
Spring
推荐的管理事务的方式。 - com.mycompany.h2.jpa包中包含实体类。
- 看看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
但该清单没有任何物品。 如果我将FetchType更改为EAGER ,谓词无效,我会获得所有语言。 我现在不知道怎么办……
- Hibernate:级联类型
- org.hibernate.MappingException:实体映射中的重复列
- Hibernate-懒得初始化一个角色集合:beans.Language.patients,无法初始化代理 – 没有Session
- JPA Map 映射
- Hibernate在更新集合时删除孤立
- 正确使用Session.persist()
- 部署应用程序的java.lang.AbstractMethodError(Spring 4 MVC + Hibernate 4/5)
- Bitronix + Spring + Hibernate + Persistence
- 使用OpenEntityManagerInViewFilter进行延迟初始化?