Help Mapping a Composite Foreign Key in JPA 2.0 - orm

I'm new to JPA and I'm trying to map a legacy database. The files load correctly individually but the relationships are not working correctly. Any help would be appreciated.
Java
#Entity
#IdClass(ParentKey.class)
public class Parent {
#Id
#Column(name="code")
private String code;
#Id
#Column(name="id")
private int id;
#OneToMany(mappedBy="parent")
private List<Child> children = new ArrayList<Child>();
}
public class ParentKey {
private String code;
private int id;
}
#Entity
#IdClass(ChildKey.class)
public class Child {
#Id
#JoinColumns({
#JoinColumn(name="code")
#JoinColumn(name="id")
})
private Parent parent;
#Id
#Column(name="index")
private int index;
}
public class ChildKey {
private String code;
private int id;
private int index;
}
SQL
create table Parent(
code char(4) not null,
id int not null,
primary key(code,id)
);
create table Child(
code char(4) not null,
id int not null,
index int not null,
primary key(code, id, index),
foreign key(code, id) references Parent(code,id)
);
edit 1:
add the ChildKey and ParentKey classes.

Here's a link to DataNucleus docs for compound identity 1-N relation. May help you identify what is wrong. For a start you have no IdClass defined on Child
http://www.datanucleus.org/products/accessplatform_3_0/jpa/orm/compound_identity.html#1_N_coll_bi

Here is what OpenJPA created using its ReverseMapping Tool and it appears to be working correctly.
#Entity
#Table(name="PARENT")
#IdClass(ParentId.class)
public class Parent {
#OneToMany(targetEntity=Child.class, mappedBy="parent", cascade=CascadeType.MERGE)
private Set childs = new HashSet();
#Id
#Column(length=4)
private String code;
#Id
private int id;
//omitted getters, setters
}
public class ParentId implements Serializable {
public String code;
public int id;
//omitted getters, setters, toString, equals, hashcode
}
#Entity
#Table(name="CHILD")
#IdClass(ChildId.class)
public class Child {
#Id
#Column(length=4)
private String code;
#Id
private int id;
#Id
private int index;
#ManyToOne(fetch=FetchType.LAZY, cascade=CascadeType.MERGE)
#JoinColumns({#JoinColumn(name="code"), #JoinColumn(name="id")})
private Parent parent;
//omitted getters, setters
}
public class ChildId implements Serializable {
public String code;
public int id;
public int index;
//omitted getters, setters, toString, equals, hashcode
}

See also http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#JPA_2.0
(add #Id to #ManyToOne field)

Related

How to write select named query in jpa for EmbeddedId?

I have a JPA entity called Parent and inside that there is embeddedPrimary key as Child
#Entity
#Table(name = "PARENT")
#NamedQuery(?????)
public class Parent implements Serializable {
#EmbeddedId
private ChildPK child;
}
and
#Embeddable
public class ChildPK implements Serializable {
#Column(name = "DEALERID")
private String dealerId;
#Column(name = "BRANDID")
private Long brandId;
..
}
How can I write named Query in Parent class so that i can perform select on dealerId and brandId from ChildPk.
SELECT p FROM Parent p WHERE p.child.dealerId = ? and p.child.brandId=?

Spring Data -- Query JOIN Validation ERROR -- Validation failed for query for method

I'm trying to do a 3 table Join Query with Spring data and I'm running into a query validation issue.
The exact error I get is:
Caused by: java.lang.IllegalArgumentException: Validation failed for query for method public abstract java.util.List com.somethinng.domain.subscriberCategoriesRepository.findByJoin()!
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:92)
at org.springframework.data.jpa.repository.query.SimpleJpaQuery.<init>(SimpleJpaQuery.java:62)
at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromMethodWithQueryString(JpaQueryFactory.java:72)
at org.springframework.data.jpa.repository.query.JpaQueryFactory.fromQueryAnnotation(JpaQueryFactory.java:53)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$DeclaredQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:144)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateIfNotFoundQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:212)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$AbstractQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:77)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.<init>(RepositoryFactorySupport.java:436)
at org.springframework.data.repository.core.support.RepositoryFactorySupport.getRepository(RepositoryFactorySupport.java:221)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.initAndReturn(RepositoryFactoryBeanSupport.java:277)
at org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport.afterPropertiesSet(RepositoryFactoryBeanSupport.java:263)
at org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean.afterPropertiesSet(JpaRepositoryFactoryBean.java:101)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 39 more
On the Database side here are my three tables
create table subscribers(
id int auto_increment primary key,
email varchar(255),
unique (email));
create table categories (
id int auto_increment primary key,
source varchar(255) not null,
description varchar(255) not null);
create table subscriberCategories(
subscriber int not null,
source int not null,
primary key (subscriber,source), -- prevents dupes
constraint `fk_2user` foreign key (subscriber) references subscribers(id));
And here are my POJO's and Repositories
Subscribers
#Entity
#Getter
#Setter
public class Subscribers {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotNull
private String email;
public Subscribers() { }
public Subscribers(Integer id) {
this.id = id;
}
public Subscribers(String email, String description) {
this.email = email;
}
}
Categories
#Entity
#Getter
#Setter
public class Categories {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotNull
private String description;
#NotNull
private String source;
public Categories() { }
public Categories(Integer id) {
this.id = id;
}
public Categories(String source, String description) {
this.source = source;
this.description = description;
}
}
SubscriberCategories
#Entity
#Getter
#Setter
public class subscriberCategories {
#Id
#Column(name = "subscriber")
private Integer id;
#NotNull
private Integer source;
public subscriberCategories() {
}
public subscriberCategories(Integer subscriberId) {
this.id = subscriberId;
}
public subscriberCategories(Integer source, Integer subscriberId) {
this.source = source;
this.id = subscriberId;
}
}
Repositories
SubscriberRepository
#Repository
public interface SubscriberRepository extends CrudRepository<Subscribers, Integer> {
}
CategoriesRepository
#Repository
public interface CategoriesRepository extends CrudRepository<Categories, Integer> {
List<Categories> findById(Integer id);
Long deleteBySource(String source);
List<Categories> findBySource(String source);
}
subscriberCategoriesRepository
#Transactional
#Repository
public interface subscriberCategoriesRepository extends CrudRepository<subscriberCategories, Integer> {
#Query(value = "SELECT DISTINCT s.email as Subscriber, c.source as Source from Subscribers s" +
"Inner Join subscriberCategories sc on s.id = sc.subscriber" +
"Inner Join Categories c on sc.subscriber = s.id where s.email = 'xxxxx#gmail.com'")
List<subscriberCategories> findByJoin();
Lastly I have the following unit test which is throwing the error when I run it
#RunWith(SpringRunner.class)
#SpringBootTest
#EnableJpaRepositories(basePackageClasses = subscriberCategories.class)
#Transactional
public class subscriberCategoriesTest {
#Autowired
subscriberCategoriesRepository subscriberCategoriesRepository;
#Test
public void testLoadCategories() {
List<subscriberCategories> subscriberCategories = (List<subscriberCategories>) subscriberCategoriesRepository.findByJoin();
assertEquals("Should contain something", 1, subscriberCategories.size());
}
If someone give me a hand with this I would really appreciate it
Thanks

Hibernate grouping and result set transformation

Suppose I have the following result set:
So, we have one-to-many-to-many relation here.
The question is what would be the best way to group it in Hibernate and convert this data structure to DTO with fields like:
String countryName
String companyName
List invoiceNumbers
Thank you!
For Country and Company ---- Many-to-Many
For Company and Invoice --- One-to-Many
#Entity
public class Country {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#ManyToMany(mappedBy="countries")
private Collection<Country> countries;
...
getters and setters
}
#Entity
public class Company {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#ManyToMany
private Collection<Country> countries;
#OneToMany
private Collection<Invoice> invoices;
...
getters and setters
}
#Entity
public class Invoice {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private int invoice_number;
...
getters and setters
}

Multiple unidirectional oneToMany relationship columns from one table to another table in JPA

I'm trying to have a localization table that is linked to from multiple tables.
I'm realizing that the problem is that I'm using the ID of Localization (eg Localization_Id) so I can't link to different localizations without some other key. Should I use a join table or some other sequential id in the database somehow? Not sure what the best approach is using JPA.
Thanks in advance.
#Entity
public class MyEntityWithLocalization {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
#OneToMany(fetch = FetchType.EAGER, cascade={CascadeType.ALL})
#JoinColumn(name="LOCALIZATION_KEY")
List<Localization> field1;
#OneToMany(fetch = FetchType.EAGER, cascade={CascadeType.ALL})
#JoinColumn(name="LOCALIZATION_KEY")
List<Localization> field2; //can't be unique from field one as it links to the MyEntityWithLocalization id.
#OneToMany(fetch = FetchType.EAGER, cascade={CascadeType.ALL})
#JoinColumn(name="LOCALIZATION_KEY")
List<Localization> field3; //can't be unique from field one as it links to the MyEntityWithLocalization id.
}
#Entity
public class Localization {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
String language;
String string;
public Localization(String language, String string) {
this.language = language;
this.string = string;
}
public Localization(){
}
}
This creates a localization_key in the localization table but that is just keyed to the ID of the MyEntityWithLocalization - it needs to be another unique value which makes me believe a join table may make sense in this case.
create table localization (
id number(19,0) not null,
language varchar2(255),
string varchar2(255),
localization_key number(19,0),
primary key (id)
);
Hmm. Why not just split the entity into constant and localizable part? Something like this:
#Entity
class MyEntity{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
#OneToMany
#MapKeyColumn
Map<String, MyEntityLocalization> localizations;
}
#Embeddable
class MyEntityLocalization {
String field1;
String field2;
String field3;
}
Where the localizations field has the map from the language to the localization? The other way is using Hibernate-specific annotations:
#Entity
class MyEntityWithLocalization {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
#OneToMany
#MapKeyColumn(name="language")
#WhereJoinTable(clause = "key=1")
Map<String, String> field1;
#OneToMany
#MapKeyColumn(name="language")
#WhereJoinTable(clause = "key=2")
Map<String, String> field2;
}
#Entity
public class Localization {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
long id;
long key;
String language;
String string;
}

Error reading annotations with composite key in EBean

Following this link
I would like to use OneToMany instead ManyToMany annotation, having middle class with composite key in it using Ebean. I have this error:
java.lang.RuntimeException: Error reading annotations for models.SoftwareTagPk
This is my SoftwareTagPk class:
#Embeddable
public class SoftwareTagPk implements Serializable {
#ManyToOne
private Tag tag;
#ManyToOne
private Software software;
...
}
And SoftwareTag class:
#Entity
public class SoftwareTag extends Model {
#EmbeddedId
private SoftwareTagPk pk = new SoftwareTagPk();
#Transient
public Tag getTag() {
return pk.getTag();
}
public void setTag(Tag aTag) {
pk.setTag(aTag);
}
#Transient
public Software getSoftware() {
return pk.getSoftware();
}
public void setSoftware(Software aSoftware) {
pk.setSoftware(aSoftware);
}
}
Also in logs:
Error with association to [class models.Tag] from
[models.SoftwareTagPk.tag]. Is class models.Tag registered?
How to fix it?
To make this code work you have to do:
In your SoftwareTagPk class put only id's of Tag and Software
Move #ManyToOne relations to SoftwareTag class
Add #JoinColumn annotations with attributes updatable and insertable set to false.
Override setters setTag and setSoftware in SoftwareTag class. In these setters you will rewrite id's to composite key.
Main idea of this solution is that SoftwareTag has composite key and #ManyToOne relations and they are mapped to the same collumns.
This is the code:
Tag.java
#Entity
public class Tag extends Model {
#Id
private Integer id;
#OneToMany(mappedBy="tag")
public List<SoftwareTag> softwareTags;
public Integer getId() {
return id;
}
public void setId(Integer aId) {
id=aId;
}
public static Finder<Integer,Tag> find = new Finder<Integer,Tag>(
Integer.class, Tag.class
);
}
Software.java
#Entity
public class Software extends Model {
#Id
private Integer id;
#OneToMany(mappedBy="software")
public List<SoftwareTag> softwareTags;
public Integer getId() {
return id;
}
public void setId(Integer aId) {
id=aId;
}
}
SoftwareTag.java
#Entity
public class SoftwareTag extends Model {
SoftwareTag() {
pk = new SoftwareTagPk();
}
#EmbeddedId
private SoftwareTagPk pk = new SoftwareTagPk();
#ManyToOne
#JoinColumn(name = "tag_id", insertable = false, updatable = false)
private Tag tag;
#ManyToOne
#JoinColumn(name = "software_id", insertable = false, updatable = false)
private Software software;
public Tag getTag() {
return tag;
}
public void setTag(Tag aTag) {
tag = aTag;
pk.tag_id = aTag.getId();
}
public Software getSoftware() {
return software;
}
public void setSoftware(Software aSoftware) {
software = aSoftware;
pk.software_id = aSoftware.getId();
}
}
SoftwareTagPk.java
#Embeddable
public class SoftwareTagPk implements Serializable {
public Integer tag_id;
public Integer software_id;
#Override
public int hashCode() {
return tag_id + software_id;
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
SoftwareTagPk pk = (SoftwareTagPk)obj;
if(pk == null)
return false;
if (pk.tag_id.equals(tag_id) && pk.software_id.equals(software_id)) {
return true;
}
return false;
}
}