JPA #OneToMany and composite PK - jpa

I am working on a JPA project. I need to use a #OneToMany mapping on a class that has three primary keys. You can find the errors and the classes after this.
javax.persistence.PersistenceException: No Persistence provider for EntityManager named JTA_pacePersistence: Provider named oracle.toplink.essentials.PersistenceProvider threw unexpected exception at create EntityManagerFactory:
javax.persistence.PersistenceException
javax.persistence.PersistenceException: Exception [TOPLINK-28018] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.EntityManagerSetupException
Exception Description: predeploy for PersistenceUnit [JTA_pacePersistence] failed.
Internal Exception: Exception [TOPLINK-7220] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.ValidationException
Exception Description: The #JoinColumns on the annotated element [private java.util.Set isd.pacepersistence.common.Action.permissions] from the entity class [class isd.pacepersistence.common.Action] is incomplete. When the source entity class uses a composite primary key, a #JoinColumn must be specified for each join column using the #JoinColumns. Both the name and the referenceColumnName elements must be specified in each such #JoinColumn.
at oracle.toplink.essentials.internal.ejb.cmp3.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:643)
at oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider.createEntityManagerFactory(EntityManagerFactoryProvider.java:196)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:110)
at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:83)
at isd.pacepersistence.common.DataMapper.(Unknown Source)
at isd.pacepersistence.server.MainServlet.getDebugCase(Unknown Source)
at isd.pacepersistence.server.MainServlet.doGet(Unknown Source)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:718)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:831)
at org.apache.catalina.core.ApplicationFilterChain.servletService(ApplicationFilterChain.java:411)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:290)
at org.apache.catalina.core.StandardContextValve.invokeInternal(StandardContextValve.java:271)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:202)
Here is the source code of my classes :
Action :
#Entity
#Table(name="action")
public class Action {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int num;
#ManyToOne(cascade= { CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.REFRESH })
#JoinColumn(name="domain_num")
private Domain domain;
private String name;
private String description;
#OneToMany
#JoinTable(name="permission", joinColumns= { #JoinColumn(name="action_num", referencedColumnName="action_num", nullable=false, updatable=false) }, inverseJoinColumns= { #JoinColumn(name="num") })
private Set<Permission> permissions;
public Action() {
}
Permission :
#SuppressWarnings("serial")
#Entity
#Table(name="permission")
public class Permission implements Serializable {
#EmbeddedId
private PermissionPK primaryKey;
#ManyToOne
#JoinColumn(name="action_num", insertable=false, updatable=false)
private Action action;
#ManyToOne
#JoinColumn(name="entity_num", insertable=false, updatable=false)
private isd.pacepersistence.common.Entity entity;
#ManyToOne
#JoinColumn(name="class_num", insertable=false, updatable=false)
private Clazz clazz;
private String kondition;
public Permission() {
}
PermissionPK :
#SuppressWarnings("serial")
#Entity
#Table(name="permission")
public class Permission implements Serializable {
#EmbeddedId
private PermissionPK primaryKey;
#ManyToOne
#JoinColumn(name="action_num", insertable=false, updatable=false)
private Action action;
#ManyToOne
#JoinColumn(name="entity_num", insertable=false, updatable=false)
private isd.pacepersistence.common.Entity entity;
#ManyToOne
#JoinColumn(name="class_num", insertable=false, updatable=false)
private Clazz clazz;
private String kondition;
public Permission() {
}

Good morning,
After a long day searching how JPA and #OneToMany works with composite PK, I did find a solution. In order to make it work, I used the parameter mappedBY of #OneToMany. As you can see in the code sample, I mapped the Set of Permission with the attribute action of the class Permission. And that's it! Simple when you know it!
FF
Action Class :
#Entity
#Table(name="action")
public class Action {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int num;
#ManyToOne(cascade= { CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.REFRESH })
#JoinColumn(name="domain_num")
private Domain domain;
private String name;
private String description;
#OneToMany(mappedBy="action")
private Set<Permission> permissions;
Permission Class
#SuppressWarnings("serial")
#Entity
#Table(name="permission")
public class Permission implements Serializable {
#EmbeddedId
private PermissionPK primaryKey;
#ManyToOne
#JoinColumn(name="action_num", insertable=false, updatable=false)
private Action action;

The error message seems pretty clear: you need to declare the three columns of your composite PK as #JoinColum and the name and referenceColumnName must be specified for each. I didn't test the mapping but try this:
#OneToMany
#JoinTable(name="permission", joinColumns= {
#JoinColumn(name="col1", referencedColumnName="col1", nullable=false, updatable=false),
#JoinColumn(name="col2", referencedColumnName="col2", ...),
#JoinColumn(name="col3", referencedColumnName="col3", ...)
}, inverseJoinColumns= { #JoinColumn(name="num") })
private Set<Permission> permissions;

Related

#OneToOne Composite Primary Key of Entities

I need help for this case.
I have the following entities (I removed getters/setters/hash/toString for easy reading):
#Entity
public class Company implements Serializable{
#Id
private String id;
}
#Entity
public class Document implements Serializable{
#Id
private String id;
}
#Entity
#IdClass(Inbox.PK.class)
public class Inbox implements Serializable {
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Company company;
#Id
#ManyToOne(fetch = FetchType.LAZY)
private Document document;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "inbox")
private Invoice invoice;
public class PK implements Serializable{
private Company company;
private Document document;
}
}
First question is, should I use Company and Document types in PK class or String and String?
And here ... the headache :
#Entity
#IdClass(Invoice.PK.class)
public class Invoice implements Serializable {
#Id
#OneToOne(fetch = FetchType.LAZY, mappedBy = "invoice")
// #MapsId // ???
#JoinColumn(name = "companyId")//, referencedColumnName = "company")// ???
#JoinColumn(name = "documentId")//, referencedColumnName = "document")// ???
// #PrimaryKeyJoinColumn // ????
private Inbox inbox;
#Data
public static class PK implements Serializable {
// private Inbox inbox; // ???
// private String company,document; // ???
// private String companyId,documentId; // ???
// private String inboxCompanyId,inboxDocumentId; // ???
}
}
The PK of the Invoice Entity is also the FK to Inbox (I would like constraints to be generated), and the PK of Inbox is composed of two Entities (Company and Document).
I prefer to use IdClass rather EmbeddedId.
How could I configure Invoice to have, at the end, (company_id,document_id) as PK AND FK to Inbox?
I saw your question posted in upwork. I think you should use string + string type fields with #Id and #Column annotations in PK class.

Detached Entity passed to persist error

I have a #ManyToOne relationship:
#Entity
public class Produkt implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Basic
private String ausstattung;
#Basic
private String hersteller;
#Basic
private String name;
#Basic
private Boolean veraltet;
#ManyToOne(targetEntity = ProduktArt.class, cascade= CascadeType.PERSIST)
private ProduktArt typ;
#Basic
private String ausstattungsTyp;
// ...
}
#Entity
public class ProduktArt implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Basic
private String name;
#Basic
private Boolean sichtbarFuerKunde;
// ...
}
Which is persisted as below:
getEntityManager().persist(produkt);
It always throws this exception:
org.hibernate.PersistentObjectException: detached entity passed to
persist: ProduktArt
When I remove cascade = CascadeType.PERSIST:
org.hibernate.exception.ConstraintViolationException: could not
execute statement
How is this caused and how can I solve it?

Infinite recursion (Stackoverflow) with JPA and Biderectional ManyToMany Relantionship

I have a Spring Boot 1.3.5-RELEASE application which is using JPAto Relate my USERS to the ROLES with a Bi-directional ManyToMany relationship.
User
#Table(name = "Users")
#Entity
public class User extends BaseEntity {
#NotEmpty
#Column(unique = true)
private String username;
#NotEmpty
private String password;
#JoinColumn(name = "user_iid")
#OneToMany
private Set<UserRole> userRoles;
//getters and setters
UserRole (intermediary table)
#Table(uniqueConstraints = #UniqueConstraint(columnNames = { "user_iid", "role_iid" }))
#Entity
public class UserRole extends BaseEntity {
#RestResource(exported = false)
#ManyToOne
#NotNull
private User user;
#ManyToOne
private Role role;
//getters and setters
Role
#Entity
public class Role extends BaseEntity {
#NotEmpty
#Column(unique = true)
private String name;
#JoinColumn(name = "role_iid")
#OneToMany
private Set<UserRole> userRoles;
//getters and setters
BaseEntity is a class with Ids and Version generator.
Repository
#Repository
public interface Repository extends JpaRepository<Role, String> {
Role findByIid(#Param("iid") final String iid);
When I cURL a localhost:8080/roles/search/findByIid?iid=1 I get a StackOverflow. If the object does not exist, the application respond fine.
I already tried #JsonIgnore but does not work.
Thanks
I got the answer.
I updated the Spring Boot to 1.4.2-RELEASE (which is the last) and everything worked like a charm. I think with the update it updates JPA and Hibernate and make them handle better those ManyToMany relantionships.

Composite foreign key JPA TypeMismatchException

I've explained my scenario through simple Parent Child tables.
My composite primary key is also a composite foreign key referencing Parent table.
create table parent(
code varchar(10) not null,
id int not null,
parentcol varchar(10),
primary key(code,id)
);
create table child(
code varchar(10) not null,
id int not null,
childcol varchar(10) not null,
primary key(code, id),
foreign key(code, id) references parent(code,id)
);
Entities created (this is through Eclipse JPA plugin)
#Entity
#Table(name="parent")
#NamedQuery(name="Parent.findAll", query="SELECT p FROM Parent p")
public class Parent implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private ParentPK id;
#Column(length=10)
private String parentcol;
//bi-directional one-to-one association to Child
#OneToOne(mappedBy="parent")
private Child child;
public Parent() {
}
/* getters and setters */
}
#Embeddable
public class ParentPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(unique=true, nullable=false, length=10)
private String code;
#Column(unique=true, nullable=false)
private int id;
/* getters and setters */
/** Overridden equals and hashcode **/
}
#Entity
#Table(name="child")
#NamedQuery(name="Child.findAll", query="SELECT c FROM Child c")
public class Child implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private ChildPK id;
#Column(nullable=false, length=10)
private String childcol;
//bi-directional one-to-one association to Parent
#OneToOne
#JoinColumns({
#JoinColumn(name="code", referencedColumnName="code", nullable=false, insertable=false, updatable=false),
#JoinColumn(name="id", referencedColumnName="id", nullable=false, insertable=false, updatable=false)
})
private Parent parent;
/* getters and setters */
}
#Embeddable
public class ChildPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(insertable=false, updatable=false, unique=true, nullable=false, length=10)
private String code;
#Column(insertable=false, updatable=false, unique=true, nullable=false)
private int id;
/* overridden equals and hashcode */
I am using Spring data to save my entities as below. Parent table consist of a record with code as "code" and Id as 1.
Child child = new Child();
ChildPK childPK = new ChildPK();
childPK.setCode("code");
childPK.setId(1);
child.setId(childPK);
child.setChildcol("child1");
childRepository.save(child);
It succeeds with the 1st run when it has to insert a new record. But the issue is on the 2nd run when it has to update let's say with,
child.setChildcol("child2");
I face an error
HHH000327: Error performing load command : org.hibernate.TypeMismatchException: Provided id of the wrong type for class com.xebia.eTechLog.entities.Parent. Expected: class com.xebia.eTechLog.entities.ParentPK, got class com.xebia.eTechLog.entities.ChildPK
In case I try to give a reference of ParentPk in the Child table as
#Entity
#Table(name="child")
#NamedQuery(name="Child.findAll", query="SELECT c FROM Child c")
public class Child implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private ParentPK id;
#Column(nullable=false, length=10)
private String childcol;
//bi-directional one-to-one association to Parent
#OneToOne
#JoinColumns({
#JoinColumn(name="code", referencedColumnName="code", nullable=false, insertable=false, updatable=false),
#JoinColumn(name="id", referencedColumnName="id", nullable=false, insertable=false, updatable=false)
})
private Parent parent;
It does work, but it won't in case there are more fields in the Parent class, which is my real scenario.
You should use a derived identity. Which means you should indicate that the child's reference to its parent maps the child's ID (with a #MapsId annotation):
#Entity
public class Child implements Serializable {
#EmbeddedId
private ChildPK id;
#Column(nullable=false, length=10)
private String childcol;
#OneToOne
#MapsId // <<< NB
#JoinColumns({
#JoinColumn(name="code", referencedColumnName="code"),
#JoinColumn(name="id", referencedColumnName="id")
})
private Parent parent;
...
}
Derived identities are discussed in the JPA 2.1 spec in section 2.4.1.

Multiple writable mappings exception in EclipseLink

I have these tables:
Which my intention is : A user can be a company or a person but each one of them have something in common, as username which is the email and password, so I used the JPA Tools to generate the entities from the table which result on this:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String email;
private String password;
private int reputation;
//bi-directional one-to-one association to Company
#OneToOne(mappedBy="user", cascade={CascadeType.ALL})
private Company company;
//bi-directional many-to-one association to Location
#OneToMany(mappedBy="user")
private List<Location> locations;
//bi-directional one-to-one association to Person
#OneToOne(mappedBy="user")
private Person person;
//bi-directional many-to-one association to Product
#OneToMany(mappedBy="user")
private List<Product> products;
//bi-directional many-to-one association to UserType
#ManyToOne
#JoinColumn(name="type")
private UserType userType;
//bi-directional many-to-one association to UserPhone
#OneToMany(mappedBy="user")
private List<UserPhone> userPhones;
//bi-directional many-to-one association to UserPicture
#OneToMany(mappedBy="user")
private List<UserPicture> userPictures;
//bi-directional many-to-one association to UserSocialNetwork
#OneToMany(mappedBy="user")
private List<UserSocialNetwork> userSocialNetworks;
// getter and setters
}
Now if I try to persist an user object launchs the follow exception in EclipseLink:
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [COMPANY.id_user]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.OneToOneMapping[user]
Descriptor: RelationalDescriptor(entity.Company --> [DatabaseTable(COMPANY)])
Exception [EclipseLink-48] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Multiple writable mappings exist for the field [PERSON.id_user]. Only one may be defined as writable, all others must be specified read-only.
Mapping: org.eclipse.persistence.mappings.OneToOneMapping[user]
Descriptor: RelationalDescriptor(entity.Person --> [DatabaseTable(PERSON)])
Runtime Exceptions:
Is the generated mapping wrong ?
How can I solve this exception ?
Update
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id_user")
private int idUser;
private String email;
private String name;
//bi-directional many-to-one association to Area
#ManyToOne
#JoinColumn(name="area")
private Area areaBean;
//bi-directional one-to-one association to User
#OneToOne(cascade={CascadeType.ALL})
#JoinColumn(name="id_user", insertable=false, updatable=false)
private User user;
// getter and setters
}
#Entity
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id_user")
private int idUser;
#Temporal( TemporalType.DATE)
private Date birthdate;
private String gender;
private String name;
private String surname;
//bi-directional one-to-one association to User
#OneToOne
#JoinColumn(name="id_user", insertable=false, updatable=false)
private User user;
// getters and setters
}
I solved my problem placing the insertable=false, updatable=false in the #JoinColumn annotation in both classes, Person and Company.
The proper way to do this is to use #PrimaryKeyJoinColumn instead of plain old #JoinColumn.
reference wiki example on PrimaryKeyJoinColumn
My guess is that you have the id_user mapped twice, once using a Basic #Id mapping, and once using the #ManyToOne. You need to make one of them read-only, i.e. insertable/updatable=false. Or better, just remove the basic id, and put the #Id on the #ManyToOne.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Primary_Keys_through_OneToOne_and_ManyToOne_Relationships