JPA (Hibernate) issue saving ManyToOne relationship - java

I'm experiencing a problem: I have an Entity Definition, that has a OneToMany relationship with DefinitionInfo Entities and when I try to load an existing Definition, add a DefinitionInfo to it's infoList collection, saving says it can not find the entity of the DefinitionInfo I just added to the Definition. To this I say 'DUH!' I am trying to add a new DefintionInfo to the Definition so of course you won't find it!
Here's the code:
#Entity(name = "CohortDefinition")
#Table(name="cohort_definition")
#NamedEntityGraph(
name = "CohortDefinition.withDetail",
attributeNodes = { #NamedAttributeNode(value = "details", subgraph = "detailsGraph") },
subgraphs = {#NamedSubgraph(name = "detailsGraph", type = CohortDefinitionDetails.class, attributeNodes = { #NamedAttributeNode(value="expression")})}
)
public class CohortDefinition implements Serializable{
private static final long serialVersionUID = 1L;
...
#OneToMany(mappedBy="cohortDefinition", fetch= FetchType.LAZY)
private Set<CohortGenerationInfo> generationInfoList;
...
+ Getters and Setters
And the target/dependent object:
Entity(name = "CohortGenerationInfo")
#Table(name="cohort_generation_info")
public class CohortGenerationInfo implements Serializable {
private static final long serialVersionUID = 1L;
public CohortGenerationInfo()
{
}
public CohortGenerationInfo(Integer cohortDefinitionId, Integer sourceId)
{
this.id = new CohortGenerationInfoPK(cohortDefinitionId, sourceId);
}
#EmbeddedId
private CohortGenerationInfoPK id;
#ManyToOne
#MapsId("cohortDefinitionId")
#JoinColumn(name="id")
private CohortDefinition cohortDefinition;
Here is the EmbeddedID class (The Id for the Info consists of the definition's ID + a ID of another entity that represents differetnt types of info.
#Embeddable
public class CohortGenerationInfoPK implements Serializable {
private static final long serialVersionUID = 1L;
public CohortGenerationInfoPK() {
}
public CohortGenerationInfoPK(Integer cohortDefinitionId, Integer sourceId) {
this.cohortDefinitionId = cohortDefinitionId;
this.sourceId = sourceId;
}
#Column(name = "id")
private Integer cohortDefinitionId;
#Column(name = "source_id")
private Integer sourceId;
+ getter/setter
Here is what I am doing. This should be so simple, but I just don't understand what is going on:
Source source = this.getSourceRepository().findBySourceKey(sourceKey);
CohortDefinition currentDefinition = this.cohortDefinitionRepository.findOne(id);
CohortGenerationInfo info = findBySourceId(currentDefinition.getGenerationInfoList(), source.getSourceId());
if (info == null)
{
info = new CohortGenerationInfo(currentDefinition.getId(), source.getSourceId());
currentDefinition.getGenerationInfoList().add(info);
}
info.setStatus(GenerationStatus.PENDING)
.setStartTime(Calendar.getInstance().getTime());
this.cohortDefinitionRepository.save(currentDefinition);
Here's the Hibernate select just before it blows up:
Hibernate: select cohortgene0_.id as id1_4_0_, cohortgene0_.source_id as source_i2_4_0_, cohortgene0_.execution_duration as executio3_4_0_, cohortgene0_.is_valid as is_valid4_4_0_, cohortgene0_.start_time as start_ti5_4_0_, cohortgene0_.status as status6_4_0_, cohortdefi1_.id as id1_2_1_, cohortdefi1_.created_by as created_2_2_1_, cohortdefi1_.created_date as created_3_2_1_, cohortdefi1_.description as descript4_2_1_, cohortdefi1_.expression_type as expressi5_2_1_, cohortdefi1_.modified_by as modified6_2_1_, cohortdefi1_.modified_date as modified7_2_1_, cohortdefi1_.name as name8_2_1_ from cohort_generation_info cohortgene0_ inner join cohort_definition cohortdefi1_ on cohortgene0_.id=cohortdefi1_.id where cohortgene0_.id=? and cohortgene0_.source_id=?
Here is the stack trace:
javax.persistence.EntityNotFoundException: Unable to find org.ohdsi.webapi.cohortdefinition.CohortGenerationInfo with id org.ohdsi.webapi.cohortdefinition.CohortGenerationInfoPK#3
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate.handleEntityNotFound(EntityManagerFactoryBuilderImpl.java:183)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:219)
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:275)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:151)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1070)
at org.hibernate.internal.SessionImpl.internalLoad(SessionImpl.java:989)
at org.hibernate.type.EntityType.resolveIdentifier(EntityType.java:716)
at org.hibernate.type.EntityType.resolve(EntityType.java:502)
at org.hibernate.type.EntityType.replace(EntityType.java:366)
at org.hibernate.type.CollectionType.replaceElements(CollectionType.java:549)
at org.hibernate.type.CollectionType.replace(CollectionType.java:690)
at org.hibernate.type.TypeHelper.replace(TypeHelper.java:177)
at org.hibernate.event.internal.DefaultMergeEventListener.copyValues(DefaultMergeEventListener.java:407)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:219)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:192)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:876)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:858)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:863)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.merge(AbstractEntityManagerImpl.java:1196)
I stoppped here at the merge part of the stack trace. It appears to be fetchign the existing entity from the DB and mereg it. The Definition DOES EXIST, it's just a new DefinitionInfo being added.
Does anyone have any thoughts? I have some limitations on how to structure the database, but I'm hoping this isn't a database structure problem, rather just somethign annoying with JPA.

Couple of apparent issues with the code.
Since you are expecting to do transitive persistence, i.e., saving CohortDefinition should also save CohortGenerationInfo, you will need to set the cascade property on the OneToMany annotation. For example,
#OneToMany(mappedBy="cohortDefinition", fetch= FetchType.LAZY, cascade= CascadeType.ALL)
You have set a bidirectional relationship between CohortDefinition and CohortGenerationInfo. As a best practice, it is better to set the values on both sides of the relationship explicitly rather than relying on the ORM to do it. So the code should do the following (Better to put it in a helper method).
currentDefinition.getGenerationInfoList().add(info);
info.setCohortDefinition(currentDefinition);

Related

JpaObjectRetrievalFailureException when saving entity with one-to-many and client-assigned ids

In a simple Spring Boot Application, I'm facing with a JpaObjectRetrievalFailureException when I'm trying to save an entity with one-to-many association and client-assigned ids.
Please take a look on these entities and on this simple repository:
#Entity
#Table(name = "cart")
public class Cart {
#Id
#Column(name = "id")
private UUID id;
#Column(name = "name")
private String name;
#OneToMany
#JoinColumn(name = "cart_id")
private List<Item> items;
// constructors, getters, setters, equals and hashCode ommitted
}
#Entity
#Table(name = "item")
public class Item {
#Id
#Column(name = "id")
private UUID id;
#Column(name = "name")
private String name;
// constructors, getters, setters, equals and hashCode ommitted
}
public interface CartRepository extends JpaRepository<Cart, UUID> {
}
I wrote this test:
#DataJpaTest
class CartRepositoryTest {
#Autowired
private CartRepository cartRepository;
#Test
void should_save_cart() {
// GIVEN
final var cart = new Cart(UUID.randomUUID(), "cart");
final var item = new Item(UUID.randomUUID(), "item");
cart.setItems(List.of(item));
// WHEN
final var saved = cartRepository.save(cart);
// THEN
final var fetched = cartRepository.findById(saved.id());
assertThat(fetched).isPresent();
}
}
When I run the test, call to cartRepository.save(cart) fails with:
Unable to find com.example.testjpaonetomany.domain.Item with id f5658508-f3d0-4d9b-a1f0-17b614753356; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.example.testjpaonetomany.domain.Item with id f5658508-f3d0-4d9b-a1f0-17b614753356
org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find com.example.testjpaonetomany.domain.Item with id f5658508-f3d0-4d9b-a1f0-17b614753356; nested exception is javax.persistence.EntityNotFoundException: Unable to find com.example.testjpaonetomany.domain.Item with id f5658508-f3d0-4d9b-a1f0-17b614753356
at app//org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:379)
at app//org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:235)
at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
at app//org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at app//org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at app//org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at app//org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at app//org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at app//org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at app//org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
at app/jdk.proxy3/jdk.proxy3.$Proxy105.save(Unknown Source)
at app//com.example.testjpaonetomany.repository.CartRepositoryTest.should_save_cart(CartRepositoryTest.java:28)
If I modify my entities by adding #GeneratedValue for ids, and in my test, I replace UUID.randomUUID() by null to delegate to Hibernate the ID generation, the test passes.
How to deal with client-generated ids?
The cause is that you save the parent object only (which is absolutely correct and fine) but still need to explain JPA that the operation should be propagated i.e.
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "cart_id")
private List<Item> items;
As minor improvements I would suggest to put the UUID generation into constructors and establish the relation via the dedicated method i.e.
final var cart = new Cart("cart");
cart.addItem(new Item("item"));
and probably consider using em.persist() instead of repository.save() as it makes a select request first in case of using uuids as #Augusto mentioned

How to avoid LazyInitializationException with nested collections in JPA-Hibernate?

Mandatory background info:
As part of my studies to learn Spring, I built my usual app - a little tool that saves questions and later creates randomized quizzes using them.
Each subject can have any number of topics, which in turn may have any number of questions, which once again in turn may have any number of answers.
Now, the problem proper:
I keep getting LazyInitializationExceptions.
What I tried last:
I changed almost each and every collection type used to Sets.
Also felt tempted to set the enable_lazy_load_no_trans property to true, but I've consistently read this is an antipattern to avoid.
The entities proper: (only fields shown to avoid wall of code-induced fatigue)
Subject:
#Entity
#Table(name = Resources.TABLE_SUBJECTS)
public class Subject implements DomainObject
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = Resources.ID_SUBJECT)
private int subjectId;
#Column(name="subject_name", nullable = false)
private String name;
#OneToMany(
mappedBy = Resources.ENTITY_SUBJECT,
fetch = FetchType.EAGER
)
private Set<Topic> topics;
}
Topic:
#Entity
#Table(name = Resources.TABLE_TOPICS)
public class Topic implements DomainObject
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "topic_id")
private int topicId;
#Column(name = "name")
private String name;
#OneToMany(
mappedBy = Resources.ENTITY_TOPIC,
orphanRemoval = true,
cascade = CascadeType.MERGE,
fetch = FetchType.EAGER
)
private Set<Question> questions;
#ManyToOne(
fetch = FetchType.LAZY
)
private Subject subject;
}
Question:
#Entity
#Table(name = Resources.TABLE_QUESTIONS)
public class Question implements DomainObject
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = Resources.ID_QUESTION)
private int questionId;
#Column(name = "statement")
private String statement;
#OneToMany(
mappedBy = Resources.ENTITY_QUESTION,
orphanRemoval = true,
cascade = CascadeType.MERGE
)
private Set<Answer> answers;
#ManyToOne(
fetch = FetchType.LAZY
)
private Topic topic;
}
Answer:
#Entity
#Table(name = Resources.TABLE_ANSWERS)
public class Answer implements DomainObject
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = Resources.ID_ANSWER)
private int answerId;
#Column(name = "answer_text", nullable = false)
private String text;
#Column(name = "is_correct", nullable = false)
private Boolean isCorrect;
#ManyToOne(
fetch = FetchType.LAZY
)
private Question question;
}
I'm using interfaces extending JpaRepository to perform CRUD operations. I tried this to fetch stuff, without luck:
public interface SubjectRepository extends JpaRepository<Subject, Integer>
{
#Query
Optional<Subject> findByName(String name);
#Query(value = "SELECT DISTINCT s FROM Subject s " +
"LEFT JOIN FETCH s.topics AS t " +
"JOIN FETCH t.questions AS q " +
"JOIN FETCH q.answers as a")
List<Subject> getSubjects();
}
Now, the big chunk of text Spring Boot deigns to throw at me - the stack trace:
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy [org.callisto.quizmaker.domain.Subject#1] - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:176) ~[hibernate-core-5.6.4.Final.jar:5.6.4.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:322) ~[hibernate-core-5.6.4.Final.jar:5.6.4.Final]
at org.hibernate.proxy.pojo.bytebuddy.ByteBuddyInterceptor.intercept(ByteBuddyInterceptor.java:45) ~[hibernate-core-5.6.4.Final.jar:5.6.4.Final]
at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95) ~[hibernate-core-5.6.4.Final.jar:5.6.4.Final]
at org.callisto.quizmaker.domain.Subject$HibernateProxy$B8rwBfBD.getTopics(Unknown Source) ~[main/:na]
at org.callisto.quizmaker.service.QuizMakerService.activeSubjectHasTopics(QuizMakerService.java:122) ~[main/:na]
at org.callisto.quizmaker.QuizMaker.checkIfActiveSubjectHasTopics(QuizMaker.java:307) ~[main/:na]
at org.callisto.quizmaker.QuizMaker.createNewQuestion(QuizMaker.java:117) ~[main/:na]
at org.callisto.quizmaker.QuizMaker.prepareMainMenu(QuizMaker.java:88) ~[main/:na]
at org.callisto.quizmaker.QuizMaker.run(QuizMaker.java:65) ~[main/:na]
at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:769) ~[spring-boot-2.6.3.jar:2.6.3]
This exception happens when I call this line of code:
boolean output = service.activeSubjectHasTopics();
Which, in turn, calls this method on a service class:
public boolean activeSubjectHasTopics()
{
if (activeSubject == null)
{
throw new NullPointerException(Resources.EXCEPTION_SUBJECT_NULL);
}
return !activeSubject.getTopics().isEmpty();
}
The activeSubjectHasTopics method gets called in this context:
private void createNewQuestion(View view, QuizMakerService service)
{
int subjectId = chooseOrAddSubject(view, service);
service.setActiveSubject(subjectId);
if (checkIfActiveSubjectHasTopics(view, service))
{
chooseOrAddTopic(view, service, subjectId);
}
do
{
createQuestion(view, service);
createAnswers(view, service);
}
while(view.askToCreateAnotherQuestion());
service.saveDataToFile();
prepareMainMenu(view, service);
}
private boolean checkIfActiveSubjectHasTopics(View view, QuizMakerService service)
{
boolean output = service.activeSubjectHasTopics();
if (!output)
{
view.printNoTopicsWarning(service.getActiveSubjectName());
String topicName = readTopicName(view);
createNewTopic(service, topicName);
}
return output;
}
Not helpful if you change your structure to set. If you need to get the entities you need to explicitly include FETCH clause in your hql queries. You'll work your way by checking out the Hibernate documentation:
https://docs.jboss.org/hibernate/orm/3.3/reference/en/html/performance.html#performance-fetching
I was able to track down the cause of the issue thanks to a comment from Christian Beikov - to quote:
Where do you get this activeSubject object from? If you don't load it
as part of the transaction within activeSubjectHasTopics, then this
won't work as the object is already detached at this point, since it
was loaded through a different transaction.
The activeSubject object was defined as part of the service class containing the activeSubjectHasTopics method, and was initialized by a different transaction as he pointed out.
I was able to fix the problem by annotating that service class as #Transactional and storing the IDs of the objects I need instead of the objects themselves.

How to stop Hibernate from eagerly fetching a relationship when it is mapped using a column (referencedColumnName) different than the primary key?

I'm mapping a relationship that does not use the entity's primary key. Using "referencedColumnName" with a column different than the primary key causes hibernate to eagerly fetch the association, by issuing an extra select, even when it's tagged with FetchType.LAZY.
My goal is to make it behave like a regular mapping, meaning it wouldn't issue an extra query every time I need to query the main entity.
I have already tried using #LazyToOne(LazyToOneOption.NO_PROXY), which sorts out the problem, but it does not operate well with Jackson's (JSON parsing library) module "jackson-datatype-hibernate5", which skips hibernate lazy proxies when serializing the results.
Here is a scenario almost like the one I have that causes the problem:
Entities:
#Entity(name = "Book")
#Table(name = "book")
public class Book
implements Serializable {
#Id
#GeneratedValue
private Long id;
private String title;
private String author;
#NaturalId
private String isbn;
//Getters and setters omitted for brevity
}
#Entity(name = "Publication")
#Table(name = "publication")
public class Publication {
#Id
#GeneratedValue
private Long id;
private String publisher;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(
name = "isbn",
referencedColumnName = "isbn"
)
private Book book;
#Column(
name = "price_in_cents",
nullable = false
)
private Integer priceCents;
private String currency;
//Getters and setters omitted for brevity
}
Repository (Spring-Data, but you could try directly with the EntityManager):
#Repository
public interface PublicationRepository extends JpaReadRepository <Publication, Long>
{
#Query ("SELECT d FROM Publication d WHERE d.publisher = ?1 ")
Optional <Publication> findByPublisher (String isbn);
}
Thanks
The only way to achieve what you are looking for is by moving the annotatation #Id to the isbn property.
You can leave the #GeneratedValue on the autoincrement property.
Notes:
1 - Make sure that your equals/hc are following the OID(Object ID) on your domain case the "NaturalId" ISBN.
2 - It will be good to ensure if possible on DB level that your natural ID has unique contraint on it.

One-To-Many relation in JPA DELETE operation is not working

The entity model and repository is given below.
Channel.java
public class Channel extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "channel_name")
private String channelName;
#Column(name = "channel_type")
private Integer channelType;
#Column(name = "seq_id")
private Integer seqId;
#Column(name = "channel_device_key")
private String channeldeviceKey;
}
UserRoomChannel.java
public class UserRoomChannel extends BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#OneToOne
#JoinColumn(name = "user_house_id")
private UserHouse userHouse;
#OneToOne
#JoinColumn(name = "room_id")
private Room room;
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<Channel> channels;
}
UserRoomChannelReposirtory.java
public interface UserRoomChannelRepository extends JpaRepository<UserRoomChannel, Long> {
#Query(value = "DELETE FROM user_room_channel_channels WHERE channels_id=?1", nativeQuery = true)
void deleteUserRoomChannelChannels(Long id);
}
I can save the data successfully. When data is saved through this a third table named user_room_channel_channels is created.
EX:
user_room_channel_id channels_id
1 1
1 2
But When I tried to delete with channels_id it give me the error
A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance:.....
The native query what I write it execute from the command line.
But using JPA I can't do that.
Any help or any suggestion for resolving this issue?
A collection with cascade="all-delete-orphan" was no longer referenced
by the owning entity instance
is because before you delete your channels(and its children), you somehow do the following:
you load your UserRoomChannel along with its Channels children in the collection from the Database.
somewhere in your code you change the reference of the children collection : myUserRoomChannel.setChannels(newChannelCollections) or myUserRoomChannel.channels =new ChannelCollections();
and you try to delete the user with your repositorisory.
Hibernate who remembered having set the children collection with reference A to the User can find the collection anymore, because User.channels is now User.channels == B (with B being a new reference to your collection).
How to fix it:
just find the place where you are replacing your children collections and instead of:
myUserRoomChannel.setChannels(newChannelCollections), or
myUserRoomChannel.channels =new ChannelCollections(),
just do
myUserRoomChannel.getChannels().add/delete/clearYourChannels()
I just have done the below step and it works perfectly.
Reomve:
myUserRoomChannel.setChannels(channels)
Add
myUserRoomChannel.getChannels().removeAll(channels) and then
userRoomChannelRepository.save(myUserRoomChannel)

Need an example of a primary-key #OneToOne mapping in Hibernate

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.

Categories

Resources