Maybe somebody can clarify what is wrong with the code below. When I create one-to-one association within embedded class (it is composite primary key) like in the code below:
#Entity
public class Test {
#EmbeddedId
private TestId id;
#Embeddable
public static class TestId implements Serializable {
private static final long serialVersionUID = 1950072763330622759L;
#OneToOne(optional = false)
#JoinColumn(name = "linkedTable_id")
private LinkedTable linkedTable;
}
..........
}
I get the following stack trace:
--------------------------------------------
Caused by: java.lang.NullPointerException
at org.hibernate.cfg.AnnotationBinder.bindOneToOne(AnnotationBinder.java:1867)
at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1286)
at org.hibernate.cfg.AnnotationBinder.fillComponent(AnnotationBinder.java:1662)
at org.hibernate.cfg.AnnotationBinder.bindId(AnnotationBinder.java:1695)
at org.hibernate.cfg.AnnotationBinder.processElementAnnotations(AnnotationBinder.java:1171)
at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:706)
at org.hibernate.cfg.AnnotationConfiguration.processArtifactsOfType(AnnotationConfiguration.java:452)
at org.hibernate.cfg.AnnotationConfiguration.secondPassCompile(AnnotationConfiguration.java:268)
at org.hibernate.cfg.Configuration.buildMappings(Configuration.java:1121)
at org.hibernate.ejb.Ejb3Configuration.buildMappings(Ejb3Configuration.java:1211)
at org.hibernate.ejb.EventListenerConfigurator.configure(EventListenerConfigurator.java:154)
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:847)
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:178)
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:235)
... 26 more
What is interesting why the sample above works if I change association type to many-to-one and doesn't work with one-to-one?
I wasn't aware this was possible but, according to the Hibernate Annotation reference documentation, it is (this is Hibernate specific though):
2.2.3.2.1. #EmbeddedId property
(...)
While not supported in JPA, Hibernate
lets you place your association
directly in the embedded id component
(instead of having to use the
#MapsId annotation).
#Entity
class Customer {
#EmbeddedId CustomerId id;
boolean preferredCustomer;
}
#Embeddable
class CustomerId implements Serializable {
#OneToOne
#JoinColumns({
#JoinColumn(name="userfirstname_fk", referencedColumnName="firstName"),
#JoinColumn(name="userlastname_fk", referencedColumnName="lastName")
})
User user;
String customerNumber;
}
#Entity
class User {
#EmbeddedId UserId id;
Integer age;
}
#Embeddable
class UserId implements Serializable {
String firstName;
String lastName;
}
And with the code you provided, the following snippet just works for me:
LinkedTable linkedTable = new LinkedTable();
linkedTable.setId(1l);
session.persist(linkedTable);
session.flush();
Test.TestId testId = new Test.TestId();
testId.setLinkedTable(linkedTable);
Test test = new Test();
test.setId(testId);
session.persist(test);
session.flush();
Tested with Hibernate EM 3.4.0.GA, Hibernate Annotations 3.4.0.GA and Hibernate Core 3.3.0.SP1.
If it doesn't work for you, can you provide a bit more code allowing to reproduce the problem?
Related
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.
I'm trying to do a JPA mapping for an existing database. I can't really change the existing structure.
I managed to make it works, but Intellij is telling me that some column doesn't exist even if it works. So I don't know if there's a better way to do this or if it's Intellij that doesn't support all the use cases.
I simplified my mapping and table for the question.
The 2 tables with primary composite keys are:
Table_A
some_id_a
some_seq_a
Table B
some_id_a
some_seq_a
some_seq_b
And my mapping is:
#Data
#Entity(name="Table_A")
public class TableA {
#EmbeddedId
private Key key;
#OneToMany
#JoinColumn(name = "someIdA")
#JoinColumn(name = "someSeqA")
private List<TableB> tableBs;
#Data
#Embeddable
public static final class Key implements Serializable {
private String someIdA;
private long someSeqA;
}
}
#Data
#Entity(name="Table_B")
public class TableB {
#EmbeddedId
private Key key;
#Data
#Embeddable
public static final class Key implements Serializable {
private String someIdA;
private long someSeqA;
private long someSeqB;
}
}
So like I said it works but I have an error in Intellij saying that the #JoinColumn(name ="someIdA") #JoinColumn(name = "someSeqA") don't exist and is expecting something like #JoinColumn(name ="some_id_a") #JoinColumn(name = "some_seq_a").
Using it the way Intellij is telling me, JPA has en error that says: Table [table_b] contains physical column name [some_id_a] referred to by multiple logical column names: [some_id_a], [someIdA].
My mapping is ok despite Intellij but is there's a better alternative ?
Thanks
You can use a "derived identity" and map your classes like this:
#Data
#Entity(name="Table_A")
public class TableA {
#EmbeddedId
private Key key;
#OneToMany(mappedBy = "tableA")
private List<TableB> tableBs;
#Data
#Embeddable
public static final class Key implements Serializable {
private String someIdA;
private long someSeqA;
}
}
#Data
#Entity(name="Table_B")
public class TableB {
#EmbeddedId
private Key key;
#MapsId("tableAKey") // maps tableAKey attribute of embedded id
#JoinColumns({
#JoinColumn(name="some_id_a", referencedColumnName="some_id_a"),
#JoinColumn(name="some_seq_a", referencedColumnName="some_seq_a")
})
#ManyToOne
private TableA tableA;
#Data
#Embeddable
public static final class Key implements Serializable {
private TableA.Key tableAKey; // corresponds to PK type of TableA
private long someSeqB;
}
}
Derived identities are discussed (with examples) in the JPA 2.2 spec in section 2.4.1.
I have main entity:
#Entity
#Table(name = "partners")
public class Partner {
#ElementCollection
#CollectionTable(
name = "external_login",
joinColumns = #JoinColumn(name = "partner_id")
)
private List<ExternalLogin> externalLogins;
...
}
And ExternalLogin is embeded entity
#Embeddable
public class ExternalLogin {
#Column(name = "type")
#Enumerated(value = EnumType.STRING)
private ExternalLoginType type;
#Column(name = "login")
private String login;
#Column(name = "password_value")
private String passwordValue;
}
public enum ExternalLoginType {
ABC;
}
#Column and #Enumerated not works in ExternalLogin entity.
For example in query will be external_login.passwordValue instead of external_login.password_value.
#Enumerated(value = EnumType.STRING) doesn't work too. Hibernate is trying to get int value of filed instead string.
Can anyone help me?
You misuse annotation #Embeddable. See description in oracle docs https://docs.oracle.com/javaee/6/api/javax/persistence/Embeddable.html
Defines a class whose instances are stored as an intrinsic part of an owning entity and share the identity of the entity. Each of the persistent properties or fields of the embedded object is mapped to the database table for the entit
#Embeddable annotation makes sense only for singular assotiation fields. Annotating list fields as #Embeddable is wrong.
Just replace
#Embeddable
public class ExternalLogin {
to
#Entity
public class ExternalLogin {
I had exactly the same issue just now.
The solution for me ended up being adding
#Access(FIELD)
To the Embeddable object.
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 :)
I have an Evaluation entity that has an associated list of EvaluationEvaluator. I need to explicitly create that entity because it required an extra column "STATUS". Before I continue evaluation. I do: evaluation.setEvaluationEvaluator(listEvaluator) where listEvaluator is a list of EvaluationEvaluator type. Then persist(evaluation). When I run this, it does not throw any kind of exception. But in the database, it inserts in the Evaluation table, and not inserted into the EvaluationEvaluator table.
Below my Evaluation entity.
#Entity
public class Evaluation implements Serializable{
#Id
#GeneratedValue
private Long id;
//MORE FIELDS
#OneToMany(mappedBy="evaluation")
private List<EvaluationEvaluator> evaluators;
//CONSTRUCTORS
//GETTER AND SETTERS
}
This is my EvalutionEvaluator Entity:
#Entity
#Table(name= "EVALUATION_EVALUATOR")
#IdClass(EvaluationEvaluatorId.class)
public class EvaluationEvaluator implements Serializable{
#Id
#Column(name="EMPLOYEE_ID", insertable=false , updatable=false)
private Long EmployeeID;
#Id
#Column(name="EVALUATION_ID", insertable=false, updatable=false)
private Long EvaluationID;
#ManyToOne
#JoinColumn(name"EMPLOYEE_ID")
private Employee employee;
#ManyToOne
#JoinColumn(name"EVALUATION_ID")
private Evaluation evaluation;
#NotNull
private String status;
//CONSTRUCTORS
//GETTER AND SETTERS
}
This is my EvaluationEvaluatorId class
public class EvaluationEvaluatorId implements Serializable{
private Long employeeID;
private Long evaluationID;
//CONSTRUCTOR
//GETTER AND SETTERS
}
And finally, this is my EvaluationBean class
#Stateful
#Named
#LocalBean
#ConversationScoped
public class EvaluationBean {
#PersistentContext(type= PersistenceContextType.EXTENDED)
private EntityManager em;
#Inject
Conversation conversation;
private Evaluation evaluation;
//IN MY WEBPAGE I IMPLEMENT PRIMEFACES PICKLIST AND IT REQUIRE DUALIST TO HANDLE
private DualListModel<Employe> evaluators;
private EvaluationEvaluator evaluationEvaluator;
private List<EvaluationEvaluator> listEvaluators;
#Inject
private EmployeeList employeeList;
//GETTER AND SETTERS
public String begin(){
if (conversation.isTransient()){
converstaion.begin();
}
evaluationEvaluator = new EvaluationEvaluator();
listEvaluators = new ArrayList<EvaluationEvaluator>();
evaluation = new Evaluation();
List<Employee> source = employeeList.findAll();
target = new ArrayList<Employee>();
evaluators = new DualListModel<Employee>(source, target);
return "/evalution/evaluationAsig.xhtml"
}
public String save(){
Iterator<Employee> iterator = evaluators.getTarget().iterator();
while (iterator.hasNext()){
EvaluationEvaluator ev = new EvaluationEvaluator();
ev.setEmployee(iterator.next());
listEvaluators.add(ev);
}
evalution.setEvaluationEvaluators(listEvaluators);
if(evaluation.getId()==null){
em.persist(evalution);
} else{
em.merge(evalution);
}
if(!conversation.isTransient()){
convesation.end();
}
return "/evalution/evaluationsAsig.xhtml"
}
}
When I debug my application,apparently everything is correct, but I mentioned above, doesn't persist in EvaluationEvaluator table.
Your #OneToMany association is missing cascading configuration.
Add cascade = CascadeType.ALL or cascade = {CascadeType.PERSIST, CascadeType.MERGE} to the #OneToMany annotation. JPA assumes no cascading by default so you would need to persist each EvaluationEvaluator by yourself explicitely otherwise.
UPDATE
There is another thing wrong with the code - the Ids of EvaluationEvaluators are never assigned. You have a complex key made of two Long columns. Both are marked not insertable nor updatable which tells to JPA that the id is going to be somehow generated on database level and it should not care about it. There is however no sequence configured explicitely in your entity (although it is not necessarily required) and also from your comment:
I did what you recommended but it throws the following exception. "A different object with same identifier was already associated with the session"
I assume that this is not the case and both id column values default to null or zero and are same for all EvaluationEvaluators you are trying to persist. If you'd like the database to generate the id for you automatically use #GeneratedValue - Configure JPA to let PostgreSQL generate the primary key value - here you can find explanation how to do this (the database part is database dependent, this is for PostgreSQL). The most common use case however, is to configure the sequence but let hibernate pick the next value, instructions here - Hibernate sequence on oracle, #GeneratedValue(strategy = GenerationType.AUTO)