OneToMany注释而离开连接

OneToMany注释而离开连接

本文介绍了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
私人字符串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)方法,并使用规范标准构建查询。类似的东西:

  public void findAll(String language){
List< MyTable> (根据< MyTable>根,CriteriaQuery?查询,CriteriaBuilder cb){
/ / something there
return ...;
}
});

$ / code>

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



我尝试使用SELECT ... FROM ... LEFT使用SQL表示法加入查询,

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

它可以工作,但不是按需要的。得到的对象列表是每个项目2个对象的列表:一个是 MyTable 对象,另一个是与MyTableTranslation相关的对象。我需要解析列表并以编程方式使用Apache Commons库中的PropertyUtils类来构建对象。



这不是干净的,我认为...是否有人知道如何制作它很容易,而不使用SQL符号?

解决方案

Marc,您可以执行以下操作,使其不需要复杂的连接子句或谓词。嵌入 H2 数据库和 JUnit 测试的简单实现对于概念证明( POC

注意:


  1. 我为 POC 使用 Spring + Plain JPA和Hibernate 实现。
  2. 我使用 Spring 管理事务的推荐方式。

  3. com.mycompany.h2.jpa包中包含实体类。

  4. 查看与您的需求具有相似结构的 mytable.sql

MyTable.java

  @Entity 
@Table(name =my_table)
public class MyTable实现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长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实现Serializable {

private static final long serialVersionUID = 11L;

@Id
@Column(name =id)
私人长ID;

@Id
@Column(名称=扬声器)
弦乐扬声器;
...
// 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(){
返回新的EmbeddedDatabaseBuilder()。setType(EmbeddedDatabaseType.H2).addScript( 类路径:mytable.sql)建立();


$Be
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);

属性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();

返回factoryBean.getObject();

$ b $Bean
public PlatformTransactionManager transactionManager(){
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory());
返回txManager;
}

@Bean
public MyTableDAO myTableDAO(){
return new MyTableDAOJPAImpl();


$ Be $ b $ public MyTableServiceImpl myTableService(){
MyTableServiceImpl myTableService = new MyTableServiceImpl();
myTableService.setMyTableDAO(myTableDAO());
返回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 );

私人MyTableDAO myTableDAO;

public void setMyTableDAO(MyTableDAO myTableDAO){
this.myTableDAO = myTableDAO;
}

public MyTable saveMyTableTranslation(MyTable myTable){
return myTableDAO.saveMyTableTranslation(myTable);
}

公共列表< 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
私人EntityManager entityManager;

MyTable saveMyTableTranslation(MyTable myTable){
entityManager.persist(myTable);
返回myTable;
}

@SuppressWarnings(unchecked)
public List< MyTable> getAllMyTables(){
return(List< MyTable>)entityManager.createQuery(FROM MyTable)。getResultList();

$ b $ public MyTable getMyTable(long entityId){
return(MyTable)entityManager.createQuery(FROM MyTable m WHERE m.id =:id).setParameter( id,entityId).getSingleResult();

$ b $ public MyTable getMyTable(String lang){
return(MyTable)entityManager.createQuery(FROM MyTable m WHERE m.lang =:lang).setParameter( lang,lang).getSingleResult();
}
}

MyTableTest.java (一个JUnit测试类)

$ pre $ @ $ $ $ $ @ $
@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);
列表< MyTableTranslation> spanishTranslations = spanishTables.getTranslations();
log.debug(spanishTranslations SIZE:+ spanishTranslations.size()); (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
);

从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
);

从my_table_translation中删除;
INSERT INTO my_table_translation值(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');


I have a OneToMany relationship in my DB but I don't want that Hibernate manages it directly.

This relationships are translations, but a DTO represents itself a translated registry:

@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
    ...
}

I want to have an specific findAll(String lang) method with a lang parameter, and use an Specification Criteria to build the query. Something like that:

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

The fact is that I don't know how to do that, because I can't use JOIN clause, as I have not an attribute in the model that represents the relationship.

I tried to use the SELECT...FROM...LEFT JOIN query with SQL notation,

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

and it works, but not as desired. The resulting list of objects, is a list of 2 object per item: one is the MyTable object, and the other is the MyTableTranslation related object. I need to parse the list and programatically build the objects using PropertyUtils class from Apache Commons library.

It is not clean I think... Does anybody know how to make it easy, without using SQL notation?

解决方案

Marc, you can do the following to make it work and you do not need complicated join clauses or predicate right now. A simple implementation in embedded H2 database and JUnit testing will be sufficient for proof of concept (POC) as below

NOTE:

  1. I am using Spring + Plain JPA with Hibernate implementation for POC.
  2. I am using the Spring recommended way of managing transaction.
  3. com.mycompany.h2.jpa package contains the entity classes.
  4. Take a look at the mytable.sql which has similar structure to your needs.

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 (a JUnit test class)

@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');

这篇关于JPA:没有@OneToMany注释而离开连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-05 03:58