我的数据库中有一个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<MyTable> list = repository.findAll(new Specification<MyTable>() {
@Override
public Predicate toPredicate(Root<MyTable> 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表示法?
答案 0 :(得分:0)
Marc,您可以执行以下操作使其正常工作,而您现在不需要复杂的join子句或谓词。嵌入式H2
数据库和JUnit
测试中的简单实现足以证明概念( POC ),如下所示
注意:
Spring + Plain JPA with Hibernate
实施 POC 。Spring
推荐的管理交易方式。<强> 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<MyTableTranslation> translations = new ArrayList<MyTableTranslation>();
...
// 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<MyTable> 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<MyTable> 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<MyTable> 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<MyTable> getAllMyTables() {
return (List<MyTable>) 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<MyTableTranslation> 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');
答案 1 :(得分:-1)
我尝试使用@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<MyTableTranslation> translations = new ArrayList<MyTableTranslation>();
// getters and setters
...
}
并将标准更改为:
public void findAll(String isolang) {
List<MyTable> list = repository.findAll(new Specification<MyTable>() {
@Override
public Predicate toPredicate(Root<MyTable> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Join<Object, Object> langJoin = root.join("translations", JoinType.LEFT);
return cb.equal(langJoin.get("lang"), isolang);
}
});
}
但是这个清单没有任何物品。如果我将 FetchType 更改为 EAGER ,则谓词无效,我会获得所有语言。我现在不知道该怎么办......