Using #OneToOne in PlayFramework 2 / Ebean ORM where the child and parent shares the same primary key - jpa

There is two models:
models/User.java
#Entity
#Table(name="users")
public class User extends Model
{
#Id
public int user_id;
public String firstName;
public String lastName;
#OneToOne
#PrimaryKeyJoinColumn
public UserProfile profile;
public static Finder<Integer,User> find = new Finder<Integer,User>( Integer.class, User.class );
}
models/UserProfile.java
#Entity
#Table(name="user_profiles")
public class UserProfile extends Model
{
#Id
public int user_id;
public String bio;
#OneToOne(mappedBy = "user_id")
public User user;
public static Finder<Integer,UserProfile> find = new Finder<Integer,UserProfile>( Integer.class, UserProfile.class );
}
some data:
INSERT INTO users VALUES(1,"Joe","Bloh");
INSERT INTO users VALUES(2,"Maria","Luis");
INSERT INTO user_profiles VALUES(1, "programmer");
INSERT INTO user_profiles VALUES(2, "tester");
and same code that fetches the profile from a user:
User user = User.find.byId(2);
UserProfile profile = UserProfile.find.byId(1);
which triggers the exception:
javax.persistence.PersistenceException: Error on models.UserProfile.user. mappedBy property [models.UserBad.user_id]is not a OneToOne?
How can two models share the same primary key in Ebean ORM, and have a #OneToOne relationship ?

I found it, the associations should be:
models/User.java
[...]
#OneToOne(mappedBy = "user")
public UserProfile profile;
[...]
models/UserProfile.java
[...]
#OneToOne
#JoinColumn(name = "user_id")
public User user;
[...]

Related

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 primary key which contains a foreign key

I have an entity called UserWithRoles:
#Entity
#Getter
#Setter
public class UserWithRoles implements Serializable
{
#Id
#GeneratedValue( strategy = GenerationType.AUTO )
private int id;
private String name;
private String password;
#OneToMany( mappedBy = "user" )
private List<UserRole> roles;
}
A UserRole entity:
#Entity
#Getter
#Setter
#IdClass( UserRolePK.class )
#Inheritance( strategy = InheritanceType.JOINED )
#DiscriminatorColumn( name = "roleType", discriminatorType = DiscriminatorType.STRING, length = 10 )
abstract public class UserRole implements Serializable
{
#Id
// It should be mapped as a foreign PK by user.id (user field declared below)
private int userID;
#Id
private String roleType;
#ManyToOne
#JoinColumn( name="user_id", referencedColumnName = "id" )
private UserWithRoles user;
}
The primary key class UserRolePK:
#Data
public class UserRolePK implements Serializable
{
private int userID;
private String roleType;
}
I want to create a composite PK to UserRole: UserWithRoles.id + UserRole.roleType
How can I map it to the database? Should I use the UserWithRoles type in the PK class instead of the ID? Is it a good idea at all? Or I just should use normal PK to UserRole? The relation would be something like that between the ClientOrder and ClientOrdetItem entities: (ClientOrder.id + ClientOrderItem.num)
You are using Derived Identity.
You need to change UserRole to look like this:
#Entity
#Getter
#Setter
#IdClass( UserRolePK.class )
#Inheritance( strategy = InheritanceType.JOINED )
#DiscriminatorColumn( name = "roleType", discriminatorType = DiscriminatorType.STRING, length = 10 )
abstract public class UserRole implements Serializable
{
#Id
private String roleType;
#Id
#ManyToOne
#JoinColumn( name="user_id", referencedColumnName = "id" )
private UserWithRoles user;
}
That is, get rid of the userID field and add an #Id annotation to the user field.
And change UserRolePK to look like this:
#Data
public class UserRolePK implements Serializable
{
private int user;
private String roleType;
}
That is, change the name of the userID field to user, to match the name of the #Id field in UserRole (but its type must still match the type of the UserWithRoles PK field, id).
Derived identity is discussed in JPA 2.1 spec, section 2.4.1.

using #Embedabble with a foreign key and manyToMany relation

I wrote an example for the code i am trying to implement, i get an error with Constraint "Student_Teacher_FK" already exists.
the #embiddable class has a foreign key that is created twice with current code.
#Entity
public class Teacher {
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Entity
public class Student{
#Id
#GeneratedValue
private Long id;
#Column(name = "Name")
private String name;
}
#Embeddable
public class StudentList implements Serializable {
#ManyToMany
#JoinTable(name = "Student_Teacher",
joinColumns =
#JoinColumn(name = "Student_ID", referencedColumnName = "ID"),
inverseJoinColumns =
#JoinColumn(name = "Teacher_ID", referencedColumnName = "ID")
)
#ForeignKey(name = "Student_Teacher_FK", inverseName = "Teacher_Student_FK")
public List<Student> studentList = new ArrayList<Student>();
}
#Entity
public class HistoryTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class LangTeacher extends Teacher {
#Embedded
#NotNull
private StudentList StudentList = new StudentList ();
}
#Entity
public class RetiredTeacher extends Teacher {
// has no students
}
#embeddable : Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity (http://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html)
As you are declaring it in 2 different entity, jpa will create associated association table (student-teacher) 2 times with associated fk, which is explicitely named, and so created 2 times too with the same name. Here is your error.
I don't think using #embeddable is appropriated for what you're intending to do. A student has is own existence and is not part of teacher itself (not an uml composition / black diamond) so it's not an embeddable entity. Student list should be held by teacher entity using a simple manyToMany association.

Mapping JPA entity relationships

I would like some advice on how to best layout my JPA entity classes. Suppose I have 2 tables I would like to model as entities, user and role.
Create Table users(user_id primary key,
role_id integer not null )
Create table role(role_id primary key,
description text,
)
I create the following two JPA Entities:
#Entity
#Table(name="users")
#Access(AccessType.PROPERTY)
public class User implements Serializable {
private Long userId;
private Long roleId;
private Role role;
#Column(name = "user_id")
#Id
public Long getUserId() {}
#Column(name = "role_id")
public Long getRoleId() {}
#ManyToOne()
JoinColumn(name="role_id")
public Role getRole() {}
}
Role Entity:
#Entity
#Table(name="Role")
#Access(AccessType.PROPERTY)
public class Role implements Serializable {
private String description;
private Long roleId;
#Column(name = "role_id")
#Id
public Long getRoleId() {}
#Column(name = "description")
public Long getDescrition(){}
#ManyToOne()
#JoinColumn(name="role_id")
public Role getRole() {}
}
Would the correct way to model this relationship be as above, or would I drop the private Long roleId; in Users? Any advice welcomed.
When I map it this way, I receive the following error:
org.hibernate.MappingException: Repeated column in mapping for entity:
Yes, you would drop the private Long roleId mapping when you have a #ManyToOne on the same column.
As the error implies, you can only map each column in an #Entity once. Since role_id is the #JoinColumn for the #ManyToOne reference, you cannot also map it as a property.
You can, however, add a convenience method to return the role ID, like
public Long getRoleId() {
return role.getId();
}

OneToOne Mapping - Play Framework Entity

I'm struggling to successfully implement a OneToOne mapping on my play framework application.
examples I have are:
#Entity
public class Profile extends GenericModel {
#Id
#GeneratedValue(generator = "foreignGenerator")
#GenericGenerator(name = "foreignGenerator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "user"))
public static int id;
#OneToOne(mappedBy="profile", cascade = {CascadeType.ALL})
public static User user;
}
and :
#Entity
public class User extends Model {
#Required
public String firstName;
#Required
public String surname;
}
in this setup it throws:
org.hibernate.AnnotationException: No identifier specified for entity: models.Profile
EDIT: As per Christian Boariu's answer, I have modified Profile to what you have suggested and User to:
#Entity
public class User extends GenericModel {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public Long user_id;
#Required
public String firstName;
#Required
public String surname;
#OneToOne(cascade = {CascadeType.ALL})
#PrimaryKeyJoinColumn(name = "user_id", referencedColumnName = "profile_id")
public Profile profile;
public Profile getProfile() {
return profile;
}
public void setProfile(Profile profile) {
this.profile = profile;
}
}
Also added getter/setter to Profile:
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
But I am now getting hibernate.id.IdentifierGenerationException: null id generated for:class models.Profile.. not sure how to correct?
The problem is about your Id definition.
It should not be static.
Also, user should not be static as well.
UPDATE:
So your class should be like this:
#Entity
public class Profile extends GenericModel {
#Id
#GeneratedValue(generator = "foreignGenerator")
#GenericGenerator(name = "foreignGenerator", strategy = "foreign",
parameters = #Parameter(name = "property", value = "user"))
public int id;
#OneToOne(mappedBy="profile", cascade = {CascadeType.ALL})
public User user;
}
Fixed. suggesstions, as above fixed the #OneToOne issue and the hibernate.id.IdentifierGenerationException: null id generated for:class models.Profile was due to trying to persist an entity with a null id - due to using #primaryKeyJoin, so changed to #JoinColumn