Problems with Relationship Composite Primary Key on Hibernate - java

I have this diagram
How can I create the relationship on Hibernate of the entities
EscalaDetalhes and DefinicaoEscala, because on DefiniciaoEscala entity there is a composite primary key formed by idDetalhe (foreign with (EscalaDetalhes)) and idTurma(foreign with (Turma)).
I've already done the class like below :
#Embeddable
public class DefinicaoEscalaPK implements Serializable {
#Column(name="myColumn")
private Integer idTurma;
#Column(name="myColumn2")
private Integer idEscalaDia;
//GETTERS , SETTERS , HASH AND EQUALS
}
#Entity
public class DefinicaoEscala implements Serializable {
#EmbeddedId
private DefinicaoEscalaPK id;
#ManyToOne()
#JoinColumn(name = "idTurno")
private TurnoBean turno;
//GETTERS , SETTERS , HASH AND EQUALS
}
#Entity
#Table(name ="table")
public class EscalaDetalhes{
#id
private Integer idDetalhe;
#ManyToOne()
#JoinColumn(name="mybdColumn")
private EscalaBean escala;
#Column(name="myColumn")
private Calendar dia;
//MY QUESTION IS HERE , HOW WOULD I DESCRIBE THE RELATION HERE?
private List<DefinicaoEscala> escalaDiaDetalhes;
//GETTERS , SETTERS , HASH AND EQUALS
}
My question is here, How can I describe the relation?
private List<DefinicaoEscala> escalaDiaDetalhes;

referencedColumnName is basically used to convey that which column of the other entity will be mapped could be used for mapping in the current entity, thus, you can use the property referencedColumnName while mentioning the relationship:
#Entity
#Table(name ="table")
public class EscalaDetalhes{
#id
private Integer idDetalhe;
#ManyToOne()
#JoinColumn(name="mybdColumn")
private EscalaBean escala;
#Column(name="myColumn")
private Calendar dia;
//MY QUESTION IS HERE , HOW WOULD I DESCRIBE THE RELATION HERE?
#OneToMany
#JoinColumn(name="idDetalhe",referencedColumnName="idEscalaDia")
private List<DefinicaoEscala> escalaDiaDetalhes;
//GETTERS , SETTERS , HASH AND EQUALS
}
P.S: It is entirely on the basis of code you have shared.

Related

How do I cascade persist an #OneToMany relationship with an #EmbeddedId using Spring Data / Hibernate

I've seen a lot of similar questions asked about this but haven't found a solution that fixes the problem I'm seeing, so apologies up front if this is a redundant question. In my situation I have various types of entities and they're each going to have their own tag associations. So I want a generic Tag class that won't have it's own id, but rather an id / composite key made of the id of the entity it's tagging, plus the tag type. To (attempt to) achieve this I made an #Embeddable id class:
#Embeddable
public class TagId implements Serializable {
#Column(columnDefinition = "BINARY(16)")
private UUID parentId;
private String value;
// Getters, setters...
}
That Id is in turn used by a #MappedSuperclass:
#MappedSuperClass
public class Tag {
#EmbeddedId
private TagId id;
// Other attributes, getters, setters...
}
... and then when I want to tag a specific entity, for example using a BookTag, the table would have a book_id column as a foreign key to a Book table taking the place of parentId :
#Entity
#Table(name = "book_tag")
#AttributeOverride(name = "parentId", column = #Column(name = "book_id"))
public class BookTag extends Tag {
// other attributes, getters, setters...
}
Then finally, I have a Book entity:
#Entity
#Table(name = "book")
public class Book {
#Id
#GeneratedValue
#Column(columnDefinition = "Binary(16)")
private UUID id;
// other attributes, getters, setters...
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "id.parentId")
private List<BookTag> tags;
}
When I then try to save a new Book, with a populated BookTag collection, using a Spring Data JPA repository to repo.save(book), my desired behavior is that the Book is saved, then the id is copied to the BookTag objects, and those are saved. Unfortunately, what I'm seeing in the log is that Book is inserted as expected, then the inserts for the Tag objects are run, but book_id is being bound as null for each of the entries.
I've tried a few other approaches:
#JoinColumn instead of mappedBy
#MapsId with a #ManyToOne reference to Book on BookTag
#GeneratedValue on parentId
None worked, but it is possible my syntax was off. Thanks in advance for anyone who knows how to tackle this problem.
To anyone who wants to do something similar, I finally found a solution that meets my criteria.
TagId was modified to this:
#Embeddable
public class TagId<T> implements Serializable {
#ManyToOne
private T taggedEntity;
private String value;
// Getters, setters...
}
...which leads to a slight modification to Tag...
#MappedSuperClass
public class Tag<T> {
#EmbeddedId
private TagId id;
// Other attributes, getters, setters...
}
...and then BookTag...
#Entity
#Table(name = "book_tag")
public class BookTag extends Tag<Book> {
// other attributes, getters, setters...
}
...and finally Book:
#Entity
#Table(name = "book")
public class Book {
#Id
#GeneratedValue
#Column(columnDefinition = "Binary(16)")
private UUID id;
// other attributes, getters, setters...
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "id.taggedEntity")
private List<BookTag> tags;
}
Now I can add 1...* BookTags to a Book, and in turn I have to set the Book on all the BookTags, but then it's one call to bookRepository.save() and everything cascades down. It would have been nicer to just do it with an id, but a generic is flexible enough. I'll just have it implement an interface so that toString/hashCode/equals can call getId on parent.
The only other drawback is I couldn't get #AttributeOverride to work, so while I'd prefer that my BookTag table have a book_id column, tagged_entity_id will have to suffice.

Hibernate. Repeated column when mapping #IdClass annotated entity with composite key

I've ran into problem with composite primary key handling by Hibernate as a JPA provider.
My entities look like below
// Entity class
#Entity
#IdClass(ExternalMatchPK.class)
#Table(name = "external_match")
public class ExternalMatch {
#Id
#Column(name = "place_id")
private Integer placeId;
#Id
#Column(name = "external_object_id")
private Integer externalObjectId;
// ... Other stuff here
}
// Key class
public class ExternalMatchPK implements Serializable {
private Integer placeId;
private Integer externalObjectId;
}
Looks pretty simple yet no matter what I do I keep getting the following exception (lines are splitted for readability):
org.hibernate.MappingException:
Repeated column in mapping for entity: ExternalMatch
column: external_object_id (should be mapped with insert="false" update="false")
I've tried placing annotation on entity class fields and key class fields together as well as separately, moving all annotations from fields to getters on each one of the classes, using key calss as #Embeddable and putting it into the entity class with #EmbeddedId. Nothing seems to work.
This case seems trivial so maybe it's something wrong with our setup but I can't even imagine where to look for the issue.
Any advice is much appreciated.
It appears that I shot myself in the foot with this.
The issue was that I had a biderectional mapping between ExternalMatch and ExternalObject I forgot about trying to replace the actual entity with its integer id.
So changing
// Entity class
#Entity
#IdClass(ExternalMatchPK.class)
#Table(name = "external_match")
public class ExternalMatch {
#Id
#Column(name = "place_id")
private Integer placeId;
#Id
#Column(name = "external_object_id")
private Integer externalObjectId;
// ... Other stuff here
}
// Key class
public class ExternalMatchPK implements Serializable {
private Integer placeId;
private Integer externalObjectId;
}
// Related entity class
#Entity
#Table(name = "external_object")
public class ExternalObject extends AbstractNameableEntity {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "external_object_id", nullable = false)
private List<ExternalMatch> matches;
// ...
}
to reprsent actual mappings like this
// Entity class
#Entity
#IdClass(ExternalMatchPK.class)
#Table(name = "external_match")
public class ExternalMatch {
#Id
#ManyToOne
#JoinColumn(name = "external_object_id", referencedColumnName = "id")
private ExternalObject externalObject;
#Id
#ManyToOne
#JoinColumn(name = "place_id")
private Poi place;
// ... Other stuff here
}
// Key class
public class ExternalMatchPK implements Serializable {
private Poi place;
private ExternalObject externalObject;
}
// Related entity class
#Entity
#Table(name = "external_object")
public class ExternalObject extends AbstractNameableEntity {
#OneToMany(cascade = CascadeType.ALL, mappedBy = "externalObject")
private List<ExternalMatch> matches;
// ...
}
resolved the repeated mapping issue yet leaving us with all the familiar troubles a biderectional mapping creates :)

JPA #MapsId vs #JoinColumn(updatable=false, insertable=false)

It seems to me that there is virtually no difference between the below two ways of mapping. Here is an example base on #MapsId javadoc:
// parent entity has simple primary key
#Entity
public class Employee {
#Id long empId;
...
}
// dependent entity uses EmbeddedId for composite key
#Embeddable
public class DependentId {
String name;
long empid; // corresponds to primary key type of Employee
}
#Entity
public class Dependent {
#EmbeddedId DependentId id;
...
#MapsId("empid") // maps the empid attribute of embedded id
#ManyToOne Employee emp;
}
What if I change Dependent's mapping to:
#Entity
public class Dependent {
#EmbeddedId DependentId id;
#ManyToOne
#JoinColumn("empid", insertable=false, updatable=false)
Employee emp;
}
What is the difference of the above two approach?
So, I tested #MapsId for my usage when in the table I have only one foregin key it was no different. But for tables where I have two foregin keys to one table like ...
UserTable, and EmailTable-> #MapsId(owner)UserTable owner, #MapsId(receiver) UserTable receiver i have problems with that. Hibernate throws exceptions. So i have to back to old #JoinColumn way of doing that. That was a one differemce that I met with that adnotations.
I am using combination of both #MapsId and #JoinColumn together to avoid getting extra field getting created in DB for associating the entities. IF I ignore #JoinColumn, an extra field is getting created in DB.
#Entity
public class BookingsModel implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private SlotDateModel slotDateModelObj;
#JsonProperty
String slotnumber;
#MapsId("memberid")
#JsonBackReference
#ManyToOne
#JoinColumn(name="memberid",referencedColumnName = "memberid")
#NotNull
MemberModel memberModel;
.
.
.
}
#Entity
public class MemberModel implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#JsonProperty
#Id
String memberid;
#JsonProperty
String name;
#JsonIgnore
String phoneno;
#JsonManagedReference
#OneToMany
Set<BookingsModel> bookings;
.
.
.
}
#Embeddable
public class SlotDateModel implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
String memberid;
String slotdate;
.
.
.
}
Tables generated with #JoinColumn
Table generated when #JoinColumn is commented Can notice that the extra field "member_model_memberid" is getting added.

Bidirectional OneToMany relationship with composite entity key cause null identifier

I am struggling with mapping a one-to-many bidirectional relationship with composite primary keys based on entites. I have created some classes that represents what I want to do. They are just made so it is easier to explain, but I think they communicate what I am trying to do well.
I have left out implementing some interfaces/methods, but I can ensure you that it works when I don't add the set of tournaments to the Player/Game entity. So something is wrong with my mapping.
When I try to find an entity (pretty much do anything) Hibernate 4.x cause a null identifier error saying nothing more about the error.
What would be the correct way to map this when I have a composite primary key on the "one" and I want bidirectional navigation?
#Entity
#IdClass(TournamentPk.class)
public class Tournament {
#Id
#ManyToOne
#JoinColumn("player_id")
private Player player;
#Id
#ManyToOne
#JoinColumn("game_id")
private Game game;
private int score;
// Getters and Setters
// Hashcode and Equals
}
// Primary key class.
public class TournamentPk {
private Player player;
private Game game;
// Getters and Setters
// Hashcode and Equals
}
#Entity
public class Player {
// ID
// Other fields
#OneToMany(fetchType = FetchType.EAGER, mappedBy = "player")
private Set<Tournament> tournaments;
// Getters and Setters
// Hashcode and Equals
}
#Entity
public class Game {
// ID
// Other fields
#OneToMany(fetchType = FetchType.EAGER, mappedBy = "game")
private Set<Tournament> tournaments;
// Getters and Setters
// Hashcode and Equals
}
Your problem is that you annotated with #Id fields of not-allowed types (e.g of type Player and Game). See the documentation of the #Id annotation for details
Note for completeness: some references allow to have another Entity class annotated with #Id.
In your place I would have an independent #Id private Long id; field in your Tournament entity + an #Unique constraint on the Game+Player combination with a standard mapping.
If you really want to stick with #IdClass, you might try adding two allowed fields in your tournament class (I have found a similar mapping here (search for #IdClass)).
#Entity
#IdClass(TournamentPk.class)
public class Tournament {
#Id
#Column(name="player_id", insertable = false, updatable = false)
private Long playerId;
#ManyToOne
#JoinColumn("player_id")//consider adding cascade=PERSIST
private Player player;
#Id
#Column(name="game_id", insertable = false, updatable = false)
private Long gameId;
#ManyToOne
#JoinColumn("game_id")//consider adding cascade=PERSIST
private Game game;
private int score;
// Getters and Setters
// Hashcode and Equals
}
public class TournamentPk {
private Long playerId;
private Long gameId;
// Getters and Setters
// Hashcode and Equals
}

GAE unowned JPA relationship

I have an unowned relationship in my Domain model
#Entity
public class A {
#Id
private String id;
private Key firstB;
private Key secondB;
// getters & setters
}
#Entity
public class B {
#Id
private Key id;
private String name;
// getter & setter
}
KeyFactory.createKey(B.class.getSimpleName(), name) is the way I generate the Key for class B
I save B independently from A and assign it to an instance of A some time. The problem is that after saving A both fields firstB and firstA are null.
Any idea of what I'm doing wrong?
Key objects are not persisted by default so require explicit annotation which is why you are seeing null values.
Try annotating firstB and secondB as #Enumerated (this should really be #Basic but there is a bug which prevents this from working):
#Entity
public class A {
#Id
private String id;
#Enumerated
private Key firstB;
#Enumerated
private Key secondB;
}
Update: The latest SDK and DataNucleus JARs now correctly allow the use of #Basic.

Categories

Resources