2016-07-11 5 views
1

У меня есть отношения OneToMany в моей БД, но я не хочу, чтобы Hibernate управлял им напрямую.JPA: левое объединение без @OneToMany аннотации

Этих отношений являются переводами, а 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 
    ... 
} 

Я хочу иметь конкретный FindAll метод (String языков) с параметром Ланга, и использовать критерии Specification, чтобы построить запрос. Нечто подобное:

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 положение, так как я не атрибут в модели, которая представляет отношения.

Я пытался использовать SELECT ... FROM ... LEFT JOIN запрос с SQL нотации,

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

и он работает, но не по своему желанию. Результирующий список объектов - это список из 2 объектов для каждого элемента: один - это объект MyTable, а другой - связанный объект MyTableTranslation. Мне нужно проанализировать список и программно построить объекты с помощью класса PropertyUtils из библиотеки Apache Commons.

Неплохо, я думаю ... Кто-нибудь знает, как сделать это легко, без использования нотации SQL?

+1

Было бы намного чище, чтобы определить '@ OneToMany' отношения в ваших лиц. Можете ли вы объяснить, почему вы не хотите использовать этот подход? –

+0

Причина не в том, чтобы позволить JPA управлять этими отношениями. Представьте, что мое приложение работает на испанском языке. Я не хочу извлекать в атрибуте все Языки, но только испанский язык. Вы знаете, можно ли это сделать? Заранее спасибо. –

+0

Вы все равно должны определить отношения как OneToMany, но затем укажите в своих критериях, что вы хотите только испанский, чтобы вы не получали языки, которые вам не нужны. –

ответ

-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, предикат не действует, я получаю все языки. Я не знаю, как действовать сейчас ...

0

Marc, вы можете сделать следующее, чтобы оно работало, и вам не нужны сложные предложения или предикат join прямо сейчас. Простая реализация во встроенном H2 баз данных и JUnit тестирования будет достаточно для доказательства концепции (РОС), как показано ниже

ПРИМЕЧАНИЯ:

  1. Я использую 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<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'); 
+0

Hi Ravindran, вы его протестировали? Не могли бы вы отправить мне ZIP-лист? Спасибо ... –

+0

Привет, Марк, я заархивировал это когда-то, после того, как я не получил от вас никакого ответа. Мне понадобится когда-нибудь для того, чтобы копать в конце моих деталей проекта, и теперь я как бы привязан к моему проекту. Пожалуйста, дайте мне когда-нибудь, может быть, к концу следующей недели. Благодарю. –