I would like to know the difference between #JsonManagedReference and #JsonBackReference in Jackson?
#JsonManagedReference is the forward part of reference – the one that
gets serialized normally. #JsonBackReference is the back part of
reference – it will be omitted from serialization.
So they really depend on the direction of your relationship
public class User {
public int id;
public String name;
#JsonBackReference
public List<Item> userItems;
}
public class Item {
public int id;
public String itemName;
#JsonManagedReference
public User owner;
}
#JsonManagedReference -> Manages the forward part of the reference and the fields marked by this annotation are the ones that get Serialised
#JsonBackReference -> Manages the reverse part of the reference and the fields/collections marked with this annotation are not serialised.
Use case:
You have a one-many or many-many relationships in your entities/tables and not using the above would lead to errors like
Infinite Recursion and hence stackoverflow - > Could not write content: Infinite recursion (StackOverflowError)
The above errors occurs because Jackson (or someother similiar) tries to serialise both ends of the relationship and ends up in a recursion.
#JsonIgnore performs similiar functions but the above mentioned annotations are preferable.
As write Rajat Verma, his solution works perfectly. Thanks man you saved me lot of time and anger :-)
The important Part:
You need define fields as List, I had that as Set before and this solution NOT WORKING (appears as infinite loop)!
I add my solution:
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Long.class)
public class Agent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToMany(mappedBy = "subscribers")
#ApiModelProperty(dataType = "List", example = "[1,2,3]") // for Swagger
#JsonIdentityReference(alwaysAsId = true) // show only id of Topic
private final List<Topic> subscribeTopics = new ArrayList<>()
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id", scope = Long.class)
public class Topic {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})
#JoinTable(name = "topic_agent",
joinColumns = #JoinColumn(name = "fk_topic_id"),
inverseJoinColumns = #JoinColumn(name = "fk_agent_id"))
#ApiModelProperty(dataType = "List", example = "[1,2,3]")
#JsonIdentityReference(alwaysAsId = true)
private final List<Agent> subscribers = new ArrayList<>();
}
#JsonManagedReference and #JsonBackReference are designed to handle this two-way linkage between fields, one for Parent role, the other for Child role.
For avoiding the problem, linkage is handled such that the property
annotated with #JsonManagedReference annotation is handled normally
(serialized normally, no special handling for deserialization) and the
property annotated with #JsonBackReference annotation is not
serialized; and during deserialization, its value is set to instance
that has the "managed" (forward) link.
Related
I have a ManyToMany relationship between Profile and ProfileExperience that is mapped as follows:
#ManyToMany
#JoinTable(name = "profile_experience_relations",
joinColumns = {
#JoinColumn(name = "profile_id")
},
inverseJoinColumns = {
#JoinColumn(name = "profile_experience_id")
})
private List<ProfileExperience> experiences;
I have added localization support inside of ProfileExperience, following this guide like so:
ProfileExperience Class
#OneToMany(mappedBy = "profileExperience", cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH}, orphanRemoval = true)
#MapKey(name = "localizedProfileExperiencePk.locale")
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
private Map<String, LocalizedProfileExperience> localizations = new HashMap<>();
LocalizedProfileExperience Class
#Entity
#Getter
#Setter
#Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
public class LocalizedProfileExperience {
#EmbeddedId
private LocalizedProfileExperiencePk localizedProfileExperiencePk;
#ManyToOne
#MapsId("id")
#JoinColumn(name = "profileExperienceId")
private ProfileExperience profileExperience;
private String value;
}
Composite PK Class
#Embeddable
#Getter
#Setter
public class LocalizedProfileExperiencePk implements Serializable {
private static final long serialVersionUID = 1L;
private String profileExperienceId;
private String locale;
public LocalizedProfileExperiencePk() {
}
Before adding the localization, there was no duplicate entries in the responses, however - everything retrieved is now duplicated.
I can solve the issue by using a Set, however I'm curious as to why this happened. What is the explanation? Can I solve it without using a set? Am I overlooking something incredibly simple?
The problem is that you are probably using join fetch or an entity graph to fetch nested collections. Now, when you look at the JDBC result set, you will see that there are many duplicate result set rows. If you have a profile with 2 profile experiences, and each has 3 localizations, you will see that you have 6 (2 * 3) duplicate rows. Theoretically, Hibernate could try to retain the expected object graph cardinality, but this is not so easy, especially when multiple collections are involved. Also, for certain collection mappings it would simply not be possible to do.
So the short answer to your problem is, never use a List unless duplicity matters to you. In this case, you will have an order column though, so even then it would be safe to use a list.
Implement the equal method of your data class. Hibernate need it.
I have three database tables that make up a many to many relationship. The tables are named Disposition, Disposition_Filter, and Dispositions_Disposition_Filter. Having mentioned that, Disposition_Disposition_Filter is the "join table".
The class DispositionFilterEntity looks like this:
#Getter
#Setter
#ToString
#EqualsAndHashCode
#NoArgsConstructor
#AllArgsConstructor
#Entity(name = "disposition_filter")
public class DispositionFilterEntity {
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#GeneratedValue(generator = "uuid2")
#Id
private String id;
#Column private String name;
}
While the DispositionEntity class looks like this:
#Getter
#Setter
#ToString
#EqualsAndHashCode
#Entity(name = "dispositions")
public class DispositionEntity {
#GenericGenerator(name = "uuid2", strategy = "org.hibernate.id.UUIDGenerator")
#GeneratedValue(generator = "uuid2")
#Id
private String id;
/** #see Disposition */
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String description;
#Column(nullable = false)
private String category;
#Column private boolean active;
#Column private String label;
#Column(name = "hidden_if_agent_not_assigned")
private Boolean hidden;
#Transient
public Disposition getAttributeTypeEnum() {
return Disposition.from(name);
}
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "dispositions_disposition_filter",
joinColumns = {#JoinColumn(name = "filter_id")},
inverseJoinColumns = {#JoinColumn(name = "disposition_id")})
private List<DispositionFilterEntity> filters;
}
When I run my code in the debugger and make a request to retrieve all the disposition filter objects from the database, I can see that the filters member on the disposition object is coming back empty despite the fact that data actually DOES exist in that relationship. If anyone knows why that list is coming back empty and could point me in the correct direction, I would very much appreciate it.
As you realized in your second update, your first problem have to do with the way you configured the dispositions relationship in DispositionFilterEntity, you interchanged the name of the columns in the joinColumns and inverseJoinColumns attributes. Instead of this:
#ManyToMany
#JoinTable(
name = "dispositions_disposition_filter",
joinColumns = {#JoinColumn(name = "disposition_id")},
inverseJoinColumns = {#JoinColumn(name = "filter_id")})
private List<DispositionEntity> dispositions;
You need:
#ManyToMany
#JoinTable(
name = "dispositions_disposition_filter",
joinColumns = {#JoinColumn(name = "filter_id")},
inverseJoinColumns = {#JoinColumn(name = "disposition_id")})
private List<DispositionEntity> dispositions;
I think the fetch type is not relevant for the problem although it is always advisable to fetch collections lazily.
Now, you are facing a stack overflow error.
The cause of this error is that you are resolving both relationships at the same time, in a circular fashion.
I mean, say for instance, that you are fetching a DispositionFilterEntity from the database.
In your code you are resolving the relationship with dispositions, either explicitly, by invoking getDispositions in your code, or implicitly, by other means - we will see later that this is the case.
For every DispositionEntity fetched, you are resolving the relationship with filters, again, either explicitly, by invoking getFilters in your code, or implicitly, by other means.
As indicated in the different comments, the first stack overflow you got was caused by the implementation of the toString, and equals and hashCode of your entities. It is always a good practice to not include any relationship field in the implementation of these methods in an entity to avoid lazy initialization or other problems like the one that you are facing
As far as you are using Lombok, in order to prevent the error you need to annotate the dispositions field in DispositionFilterEntity with #ToString.Exclude and with #EqualsAndHashCode.Exclude.
In addition, you can also annotate in the same way the filters field in DispositionEntity.
Once this problem was resolved you faced a new stack overflow error. This time the error was caused by the logic you are using to convert your entities to DTOs.
You are using Mapstruct for that purpose.
First, the provided stack trace - although the source code you provided later does not include all the methods initially indicated - shows methods related with the conversion of different entity-DTOs pairs. I think it is always better to use one Mapper for every entity-DTO pair.
Back to the stack overflow error, one option you have to avoid it is to ignore one of the fields, either dispositions or filters, in the corresponding mapping method by providing the corresponding #Mapping annotation: which of them, it will depend on your actual use case.
It is important to annotate the right mapper method, in this case, the one that maps every entity to the corresponding DTO, not the contrary.
I initially advised you to take care of JSON serialization an apply #JsonIgnore or whatever you need prevent the error but probably, as you already have a DTO, it will be no longer required.
Of course, if you do not need the relationship to be bidirectional, one possible solution is to remove one side of the relation. The reason why the filters field is not providing you any result is because you again interchanged the values of the joinColumns and inverseJoinColumns. Instead of this:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "dispositions_disposition_filter",
joinColumns = {#JoinColumn(name = "filter_id")},
inverseJoinColumns = {#JoinColumn(name = "disposition_id")})
private List<DispositionFilterEntity> filters;
You need:
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(
name = "dispositions_disposition_filter",
joinColumns = {#JoinColumn(name = "disposition_id")},
inverseJoinColumns = {#JoinColumn(name = "filter_id")})
private List<DispositionFilterEntity> filters;
Please, always remember that the inverseJoinColumns references the column to join with the entity applicable in the other side of the collection.
I am developing rest web app with spring framework, Hibernate and JSON. Please Assume that I have two entities like below:
BaseEntity.java
#MappedSuperclass
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id" )
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
public long getId() {
return id;
}
}
University.java
public class University extends BaseEntity {
private String uniName;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,orphanRemoval = true)
#JoinColumn(name = "university_id")
private List<Student> students=new ArrayList<>();
// setter an getter
}
Student.java
public class Student extends BaseEntity{
private String stuName;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "university_id",updatable = false,insertable = false)
private University university;
// setter an getter
}
when I call my rest api to list University every things work fine as I expect, but when I call my rest api to list student eagerly my JSON response is
[
{
"id": 1,
"stuName": "st1",
"university": {
"id": 1,
"uniName": "uni1"
}
},
{
"id": 2,
"stuName": "st2",
"university": 1
}
]
but my desired response is:
[
{
"id": 1,
"stutName": "st1",
"university":
{
"id": 1,
"uniName": "uni1"
}
},
{
"id": 2,
"stutName": "st2",
"university":
{
"id": 1,
"uniName": "uni1"
}
}
Update 1: my hibernate annotation working fine I have JSON issue
Requirements :
I need both side fetch eagerly(the university side is Ok)
I need university object in student side for every student(when I fetching student eagerly)
What kind of serialization or JSON config I need to do that for matching my desired response?
Update 2:
by removing #JsonIdentityInfo and editing student side like below:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "university_id",updatable = false,insertable = false)
#JsonIgnoreProperties(value = "students", allowSetters = true)
private University university;
the json response still same
I need my desired response that is mentioned above.
thanks
Remove #JsonIdentityInfo from base class, this is causing university object to serialize only id.
Can you add #JoinColumn to Student entity as well
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = student_id")
Also check your University entity class's foreign key.The foreign key should be from other entity right?
#JoinColumn(name = "university_id",foreignKey = #ForeignKey(name = "student_id")) ??
Else alternatively you can use the "mappedBy" as well.
#JoinColumn(name = "university_id", mappedBy="university")
private List<Student> students=new ArrayList<>();
You can add this and check
University
public class University {
#Fetch(value = FetchMode.SELECT)
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "university_id")
#JsonIgnore
private List<Student> students;
}
Student
public class Student{
#ManyToOne
#JoinColumn(name = "university_id", insertable = true, updatable = true, nullable = true)
private University university;
}
I understand you do not want to include University.students in your JSON.
Remove #JsonIdentityInfo
#MappedSuperclass
//#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,property = "id" )
public abstract class BaseEntity implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
public long getId() {
return id;
}
}
Add #JsonIgnore to students to avoid circle
public class University extends BaseEntity {
private String uniName;
#JsonIgnore
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,orphanRemoval = true)
#JoinColumn(name = "university_id",foreignKey = #ForeignKey(name = "university_id"))
private List<Student> students=new ArrayList<>();
// setter an getter
}
If you need University.students to be serialized in other contexts give http://www.baeldung.com/jackson-bidirectional-relationships-and-infinite-recursion a read. Other options to deal with bidirectional relationships are explained there.
Add #jsonignore for getter method
and add #jsonProperty to the field
like
#JsonProperty(access = Access.READ_ONLY)
private String password;
Recently added some feature to jackson like Readonly and writeonly
you can refer this:
http://fasterxml.github.io/jackson-annotations/javadoc/2.6/com/fasterxml/jackson/annotation/JsonProperty.Access.html
You might want to try using #JsonRawValue as an annotation for your university property. The behavior you're encountering is due to reference collapsing - since it's the same University twice, the serializer tries to be smart and just return a reference the second time it's encountered.
EDIT: The toString():
#Override
public String toString() {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsString(this);
}
The way you map your relationsip, even if it is "working fine", does not comply with jpa spec for bi-directional relationship. See this question and related answers.
To summarize usually, the owner of the relationship is the many-to-one side and the one-to-many side is annotated with mappedBy. Your mapping solution, with the one-to-many side owning the relationship is not usual / recommended (as described in the answers above) but technically possible. (#manyToOne side misses some attributes like "updatable=false" in your example)
Then, with JPA and recent Hibernate version, the lazy loading policy is the following:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
So I would suggest you to use this default lazy loading policy, and to change the owner of your manyToOne relationship as it does not seem like a good idea to get all the students via a single University resource request. (Have you heard about pagination?)
Doing so, and also excluding students collection from Marshalling, using for example #JsonIgnore, should do the trick.
I had the same problem. Hibernate (or eclipselink) are not the problem.
The only constraint in JPA is the FetchType.EAGER .
In the BaseEntity I have added a standard method
public String getLabel(){
return "id:"+this.getId();
}
this method would be abstract, but I had a lot of class and i didn't want to change it all so I added a default value.
In parent entity, in this case University, override the method
#Override
public String getLabel{
return this.uniName;
}
For each parent class, use a particular field as a label for your entity
Define a MyStandardSerializer:
public class StandardJsonSerializer extends JsonSerializer<EntityInterface> {
#Override
public void serializeWithType(EntityInterface value, JsonGenerator jgen, SerializerProvider provider, TypeSerializer typeSer) throws IOException, JsonProcessingException {
serialize(value, jgen, provider);
}
#Override
public void serialize(EntityInterface value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("id", (long) value.getId());
jgen.writeStringField("label", value.getLabel());
jgen.writeEndObject();
}
}
In the student class, on univrsity add:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "university_id",updatable = false,insertable = false)
#JsonSerialize(using=StandardJsonSerializer.class)
private University university;
Now you have resolved circularity.
When you need a label, override the method in the parent entity.
When you need some particular fields, create a specific Serializer.
I have the following code:
public interface JSONInvoiceView {
public interface JSONInvoiceBasicView {
}
public interface JSONInvoiceWithLinesView extends JSONInvoiceBasicView {
}
}
#PersistenceUnit(unitName="ERP_PU")
#Entity
#Table(name="INVOICE")
public class Invoice extends FrameworkEntity {
#Id
#SequenceGenerator(name = "PK_INVOICE_GEN", sequenceName = "PK_INVOICE_GEN", allocationSize=1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PK_INVOICE_GEN")
#Column(name = "ID")
#JsonView(JSONInvoiceView.JSONInvoiceBasicView.class)
private Long id;
#OneToMany(mappedBy="invoiceLine", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonView(JSONInvoiceView.JSONInvoiceWithLinesView.class)
#JsonManagedReference
private List<InvoiceLine> lines = new ArrayList<InvoiceLine>();
#Temporal(TemporalType.DATE)
#Column(name = "DATE")
#JsonView(JSONInvoiceView.JSONInvoiceBasicView.class)
private Date startDate;
//...
}
#PersistenceUnit(unitName="ERP_PU")
#Entity
#Table(name="INVOICE_LINE")
public class InvoiceLine extends FrameworkEntity {
#Id
#Column(name = "ID")
#JsonView(JSONInvoiceView.JSONInvoiceWithLinesView.class)
private Long id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name="FK_INVOICE")
#JsonBackReference
private Invoice invoice;
#Column(name = "AMOUNT")
#JsonView(JSONInvoiceView.JSONInvoiceWithLinesView.class)
private BigDecimal amount;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name="FK_GOOD")
private Good good;
//...
}
#PersistenceUnit(unitName="ERP_PU")
#Entity
#Table(name="GOOD")
public class Good extends FrameworkEntity {
#Id
#Column(name = "ID")
private Long id;
#Column(name = "DESCRIPTION", length=200)
private String description;
//...
}
So – one Invoice can have multiple InvoiceLines and each line has reference to Good. I need to get two JSON views: Inovice-only view and Invoice+InvoiceLine-only view. My domain is far richer than these 3 classes – the whole entity graph involves tens of classes and I need careful control how much of this graph I am loading in my entities. But I need to control also how much of loaded graph the JSON serialization facility should try to serialize. And I have the problem with this second control.
entityList is list of Invoices which has loaded InvoiceLines (with touch, e.g. invoiceLines.size();) but InvoiceLines have not further loaded Goods (invoiceLine.good is not touched during lazy load). So, entityList if Invoice+InvoiceLines.
I use the following code for Invoice-only view and this code works:
jsonString = objectMapper.writerWithView(JSONInvoiceView.JSONInvoiceBasicView.class).writeValueAsString(entityList);
Code for retrieving JSON view with Invoice+InvoiceLine-only data:
jsonString = objectMapper.writerWithView(JSONInvoiceView.JSONInvoiceWithLinesView.class).writeValueAsString(entityList);
And this code does not work, it raises error message:
org.codehaus.jackson.map.JsonMappingException: could not initialize proxy - no Session (through reference chain: java.util.ArrayList[0]->mycom.entities.Invoice["invoiceLines"]->org.hibernate.collection.internal.PersistentBag[0]-> mycom.entities.Good["good"]-> mycom.entities.Good_$$_jvst4f9_c["id"])
at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:218)
at org.codehaus.jackson.map.JsonMappingException.wrapWithPath(JsonMappingException.java:183)
at org.codehaus.jackson.map.ser.std.SerializerBase.wrapAndThrow(SerializerBase.java:140)
at org.codehaus.jackson.map.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:158)
at org.codehaus.jackson.map.ser.BeanSerializer.serialize(BeanSerializer.java:112)
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:286)
So, the question is – what Jackson views/annotations should I apply to serialized Invoice+InvoiceLine only parts of entity graph which has loaded only Invoice+InvoiceLine data? How should I indicate that Jackson should not try to go further along association chain and Jackson should not try to serialize 3rd, 4th and so order associations, Jackson should not try to serialize good entities?
p.s. Ignore annotations (or any similar global annotation on entities) is not applicable in my case, because there will be cases when I need only Invoice data and then there will be cases when I will need Invoice+InvoiceLine+Good data and further I will need data Invoice+InvoiceLine+Good+GoodSupplier, etc.
I have found solution - Jackson perceives fields without #JsonView annotation as the fields belonging to every view. Therefor I should introduce additional view:
public interface JSONInvoiceView {
public interface JSONInvoiceBasicView {
}
public interface JSONInvoiceWithLinesView extends JSONInvoiceBasicView {
}
public interface JSONInvoiceWithLinesViewExt extends JSONInvoiceWithLinesView {
}
}
And apply new interace to the Good field:
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinColumn(name="FK_GOOD")
#JsonView(JSONInvoiceView.JSONInvoiceWithLinesExtView.class)
private Good good;
So - I should define new JSON view interfeice for each level of associations for my entities. After appling #JsonView all works like a charm.
Can somebody please give me an example of a unidirectional #OneToOne primary-key mapping in Hibernate ? I've tried numerous combinations, and so far the best thing I've gotten is this :
#Entity
#Table(name = "paper_cheque_stop_metadata")
#org.hibernate.annotations.Entity(mutable = false)
public class PaperChequeStopMetadata implements Serializable, SecurityEventAware {
private static final long serialVersionUID = 1L;
#Id
#JoinColumn(name = "paper_cheque_id")
#OneToOne(cascade = {}, fetch = FetchType.EAGER, optional = false, targetEntity = PaperCheque.class)
private PaperCheque paperCheque;
}
Whenever Hibernate tries to automatically generate the schema for the above mapping, it tries to create the primary key as a blob, instead of as a long, which is the id type of PaperCheque. Can somebody please help me ? If I can't get an exact solution, something close would do, but I'd appreciate any response.
I saved this discussion when I implemented a couple of #OneToOne mappings, I hope it can be of use to you too, but we don't let Hibernate create the database for us.
Note the GenericGenerator annotation.
Anyway, I have this code working:
#Entity
#Table(name = "message")
public class Message implements java.io.Serializable
{
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn(name = "id", referencedColumnName = "message_id")
public MessageContent getMessageContent()
{
return messageContent;
}
}
#Entity
#Table(name = "message_content")
#GenericGenerator(name = "MessageContent", strategy = "foreign",
parameters =
{
#org.hibernate.annotations.Parameter
(
name = "property", value = "message"
)
}
)
public class MessageContent implements java.io.Serializable
{
#Id
#Column(name = "message_id", unique = true, nullable = false)
// See http://forum.hibernate.org/viewtopic.php?p=2381079
#GeneratedValue(generator = "MessageContent")
public Integer getMessageId()
{
return this.messageId;
}
}
Your intention is to have a 1-1 relationship between PaperChequeStopMetaData and PaperCheque? If that's so, you can't define the PaperCheque instance as the #Id of PaperChequeStopMetaData, you have to define a separate #Id column in PaperChequeStopMetaData.
Thank you both for your answers. I kept experimenting, and here's what I got working :
#Entity
#Table(name = "paper_cheque_stop_metadata")
#org.hibernate.annotations.Entity(mutable = false)
public class PaperChequeStopMetadata implements Serializable, SecurityEventAware {
private static final long serialVersionUID = 1L;
#SuppressWarnings("unused")
#Id
#Column(name = "paper_cheque_id")
#AccessType("property")
private long id;
#OneToOne(cascade = {}, fetch = FetchType.EAGER, optional = false, targetEntity = PaperCheque.class)
#PrimaryKeyJoinColumn(name = "paper_cheque_id")
#JoinColumn(name = "paper_cheque_id", insertable = true)
#NotNull
private PaperCheque paperCheque;
#XmlAttribute(namespace = XMLNS, name = "paper-cheque-id", required = true)
public final long getId() {
return this.paperCheque.getId();
}
public final void setId(long id) {
//this.id = id;
//NOOP, this is essentially a pseudo-property
}
}
This is, by all means, a disgusting hack, but it gets me everything I wanted. The paperCheque property accessors are as normal (not shown). I've run into this kind of unidirectional OneToOne mapping problem before and settled for much worse solutions, but this time I decided I was going to figure out out, so I kept hacking away at it. Once again, thank you both for your answers, it's much appreciated.
Just updating this question for future views.
When this question was made i think there wasn't a proper solution for this problem. But since JPA 2.0 you can use #MapsId to solve this problem.
Reference with proper explanation: https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
You should stay away from hibernate's OneToOne mapping, it is very dangerous. see http://opensource.atlassian.com/projects/hibernate/browse/HHH-2128
you are better off using ManyToOne mappings.