Hibernate Lazy Loading Using SpringMVC - java

I'm having a big problem for which I can't find an answer.
I have a OneToOne bidirectional Lazy fetched relation between two entities, but when I query one of them, it eagerly fetch the other, even though I'm explicitly saying that it's Lazy.
According to what I've found, almost all from 2010 and back, says that that is the way hibernate behaves on OneToOne relations. This is my first time using Hibernate ever but I highly doubt that is true, and if it was that it still is true. But i can't find any recent information about it. Post1, Post2, Post3 as you can see, they are not really recent. So I wanted to know if that's still the case or maybe guide me to where I can find the answer, examples, anything would help really.
Here's what I'm doing, just in case I'm doing something wrong.
Note the classes has fewer attributes than the original, but all of them are columns of the entity, no FK, PK, or something like that.
User Entity
#Entity
#Table(name = "\"User\"")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Id", unique = true, nullable = false)
private long id;
#Column(name = "Username", nullable = false, unique = true, length = 100)
private String username;
#Valid
#OneToOne(fetch = FetchType.LAZY, mappedBy = "user", cascade = CascadeType.ALL)
#JsonBackReference
private UserProfile userProfile;
//Getters and Setters
}
UserProfile Entity
#Entity
#Table(name = "UserProfile")
public class UserProfile {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#NotBlank
#Size(max = 100)
#Column(name = "FirstName", nullable = false, unique = false, length = 100)
private String firstName;
#NotBlank
#Size(max = 100)
#Column(name = "LastName", nullable = false, unique = false, length = 100)
private String lastName;
#Valid
#OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "UserId")
#JsonManagedReference
private User user;
//Getters and Setters
}
Test controller method
#RequestMapping(value = "/test", method = RequestMethod.GET)
public ResponseEntity<Void> test(){
User user = userService.findUserByUsername("admin");
if(user.getUserProfile() == null){
return new ResponseEntity<Void>(HttpStatus.OK);
}
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}
And the UserService simply calls the UserDao method, which is
#Override
public User findByUsername(String username) {
Criteria criteria = createEntityCriteria();
criteria.add(Restrictions.eq("username", username));
return (User) criteria.uniqueResult();
}
The result of that service is always Conflict. Even though I'm calling the UserService (Which is Transactional) and the direct relation is in UserProfile. The controller is not transactional.
The log:
2016-02-29 18:50:58 DEBUG SQL::logStatement:92 -
select
this_.Id as Id1_2_0_,
this_.Username as Username5_2_0_
from
Public.[User] this_
where
this_.Username=?
Hibernate:
select
this_.Id as Id1_2_0_,
this_.Username as Username5_2_0_
from
Public.[User] this_
where
this_.Username=?
2016-02-29 18:50:58 DEBUG Loader::debugf:384 - Result set row: 0
2016-02-29 18:50:58 DEBUG Loader::getRow:1514 - Result row: EntityKey[com.portal.model.user.User#1]
2016-02-29 18:50:58 DEBUG TwoPhaseLoad::doInitializeEntity:144 - Resolving associations for [com.portal.model.user.User#1]
2016-02-29 18:50:58 DEBUG Loader::loadEntity:2186 - Loading entity: [com.portal.model.user.UserProfile#1]
2016-02-29 18:50:58 DEBUG SQL::logStatement:92 -
select
userprofil0_.id as id1_18_0_,
userprofil0_.FirstName as FirstNam5_18_0_,
userprofil0_.LastName as LastName7_18_0_,
userprofil0_.UserId as UserId10_18_0_
from
Public.UserProfile userprofil0_
where
userprofil0_.UserId=?
Hibernate:
select
userprofil0_.id as id1_18_0_,
userprofil0_.FirstName as FirstNam5_18_0_,
userprofil0_.LastName as LastName7_18_0_,
userprofil0_.UserId as UserId10_18_0_
from
Public.UserProfile userprofil0_
where
userprofil0_.UserId=?
Is there something wrong? Am I doing something wrong?
Thanks for any help you can provide! Let me know if you need more info!

Related

Why jpa manyToOne lazy fetch is unavailable, it still lead to useless Association query

#JsonIgnoreProperties({"bmsPost"})
#ManyToOne(optional = true,fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = false)
private UmsUser umsUser;
#JsonIgnoreProperties({"umsUser"})
#Column(name = "create_time", nullable = false)
private Date createTime;
#OneToMany(mappedBy = "umsUser",cascade=CascadeType.ALL,fetch=FetchType.LAZY)
// #JoinColumn(name="user_id",referencedColumnName="id")
private List<BmsPost> bmsPost;
I just want to select BmsPost, but it selects BmsPost and UmsUser
List<BmsPost> findAllByTitle(String title);
Result:
Hibernate: select bmspost0_.id as id1_1_, bmspost0_.collects as collects2_1_, bmspost0_.comments as comments3_1_, bmspost0_.content as content4_1_, bmspost0_.create_time as create_t5_1_, bmspost0_.essence as essence6_1_, bmspost0_.modify_time as modify_t7_1_, bmspost0_.section_id as section_8_1_, bmspost0_.title as title9_1_, bmspost0_.top as top10_1_, bmspost0_.user_id as user_id11_1_, bmspost0_.view as view12_1_ from bms_post bmspost0_ where bmspost0_.title=?
Hibernate: select umsuser0_.id as id1_3_0_, umsuser0_.active as active2_3_0_, umsuser0_.alias as alias3_3_0_, umsuser0_.avatar as avatar4_3_0_, umsuser0_.bio as bio5_3_0_, umsuser0_.create_time as create_t6_3_0_, umsuser0_.email as email7_3_0_, umsuser0_.mobile as mobile8_3_0_, umsuser0_.modify_time as modify_t9_3_0_, umsuser0_.password as passwor10_3_0_, umsuser0_.role_id as role_id11_3_0_, umsuser0_.score as score12_3_0_, umsuser0_.status as status13_3_0_, umsuser0_.token as token14_3_0_, umsuser0_.username as usernam15_3_0_ from ums_user umsuser0_ where umsuser0_.id=?
[BmsPost{id='1408691924814835713', title='ds', content='dsd', userId='1349618748226658305', comments=0, collects=0, view=0,top=false, essence=false, sectionId=0, createTime=2021-06-26 15:41:42.0,modifyTime=null, umsUser=com.example.demo.Entity.UmsUser#38d115a4}]
How to avoid it?
Do you define List bmsPost in your DTO?
if you define that property in your DTO when you get your Data from DB Hibernate create a query for them to get data it needs

Hibernate OneToOne between PK's with lazy behaviour

I'm trying to achieve to have an entity called MyEntity along with another entity called MyEntityInfo using Hibernate 5.3.13.Final with annotations under Wildfly 18.
The idea is to have MyEntity store some commonly requested fields, and MyEntityInfo store some rarely requested fields. Both share the same primary key called SID (Long), and there is a FK from Info's SID to Entity's SID. There can be entities without info.
Normally you will not require the additional info. For example, I don't want the info entity to be fetched when I query my entity like this:
MyEntityImpl entity = em.find(MyEntityImpl.class, 1L);
However, when I run this code, I find that there's a second query, fetching the Info entity along the main one, as in an EAGER behaviour.
I'm mapping the relationship using #OneToOne. I've tried several combinations of FetchType, optional and #LazyToOne, but so far without success.
Here is the code for both MyEntity and MyEntityInfo classes (additional getters and setters removed):
MyEntity (ID generator is a custom sequence generator):
#Entity
#Table(name = MyEntityImpl.TABLE_NAME)
public class MyEntityImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY";
#Id
#GeneratedValue(strategy = GenerationType.TABLE, generator = "GEN_" +
TABLE_NAME)
#GenericGenerator(name = "GEN_" +
TABLE_NAME, strategy = CoreIdGenerator.ID_GENERATOR, parameters = {
#Parameter(name = "tableName", value = TABLE_NAME) })
#Column(name = "sid", nullable = false, unique = true)
private Long sid;
#OneToOne(mappedBy = "myEntity", cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = true)
#LazyToOne(LazyToOneOption.NO_PROXY)
private MyEntityInfoImpl info;
#Column
private String field;
MyEntityInfo:
#Entity
#Table(name = MyEntityInfoImpl.TABLE_NAME)
public class MyEntityInfoImpl {
public static final String TABLE_NAME = "TMP_MY_ENTITY_INFO";
#Id
#Column(name = "SID", nullable = false, unique = true)
private Long sid;
#OneToOne(fetch = FetchType.EAGER, optional = false)
#JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;
#Column(name = "INFO_FIELD")
private String infoField;
I've tried this solution, but as I said, it didn't work for me:
Hibernate lazy loading for reverse one to one workaround - how does this work?
I've managed to do something somewhat similar using #OneToMany and managing data manually, but that's not what I'd like to do. However, another alternatives and information on whether this can be achieved or not using #OneToOne, or the right design pattern to do this are also welcome.
PS: Database tables creation for SQL Server, in case you want to try it:
create table TMP_MY_ENTITY (SID NUMERIC(19,0) NOT NULL, FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY ADD CONSTRAINT PK_TMP_MY_ENTITY PRIMARY KEY CLUSTERED (SID);
go
create table TMP_MY_ENTITY_INFO (SID NUMERIC(19,0) NOT NULL, INFO_FIELD VARCHAR(100));
go
ALTER TABLE TMP_MY_ENTITY_INFO ADD CONSTRAINT PK_TMP_MY_ENTITY_INFO PRIMARY KEY CLUSTERED (SID);
go
CREATE SEQUENCE SEQ_TMP_MY_ENTITY START WITH 1 INCREMENT BY 1 MINVALUE 1 CACHE 20;
alter table TMP_MY_ENTITY_INFO add constraint FK_TMP_MY_ENT_INFO_MY_ENT FOREIGN KEY (SID) references TMP_MY_ENTITY(SID);
go
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 1');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 1');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 2');
insert into TMP_MY_ENTITY_INFO(SID, INFO_FIELD) VALUES ((SELECT MAX(SID) FROM TMP_MY_ENTITY), 'Info 2');
insert into TMP_MY_ENTITY(SID, FIELD) VALUES (NEXT VALUE FOR SEQ_TMP_MY_ENTITY, 'Field 3 no info');
-- DELETE ALL
drop table TMP_MY_ENTITY_INFO;
drop table TMP_MY_ENTITY;
drop sequence SEQ_TMP_MY_ENTITY;
After following #SternK link, and upgrading to Wildfly 19 and Hibernate 5.4.14, it finally worked by using #MapsId.
The right mapping to use is this:
MyEntity:
public class MyEntityImpl {
#OneToOne(mappedBy = "myEntity", cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "SID")
private MyEntityInfoImpl info;
MyEntityInfo:
public class MyEntityInfoImpl {
#OneToOne(fetch = FetchType.EAGER, optional = false)
#MapsId
#JoinColumn(name = "SID", referencedColumnName = "SID", insertable = false, updatable = false, nullable = false)
private MyEntityImpl myEntity;

Hibernate reloading entities in a fetch join

I am having a problem with Hibernate reloading the entities in a query even though they are being fetched as part of the main query.
The entities are as follows (simplified)
class Data {
#Id
String guid;
#ManyToOne(fetch = FetchType.LAZY)
#NotFound(action = NotFoundAction.IGNORE)
DataContents contents;
}
class DataClosure {
#Id
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "ancestor_id", nullable = false)
private Data ancestor;
#Id
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "descendant_id", nullable = false)
private Data descendant;
private int length;
}
This is modelling a closure table of parent / child relationships.
I have set up some criteria as follows
final Criteria criteria = getSession()
.createCriteria(DataClosure.class, "dc");
criteria.createAlias("dc", "a");
criteria.createAlias("dc.descendant", "d");
criteria.setFetchMode("a", FetchMode.JOIN);
criteria.setFetchMode("d", FetchMode.JOIN);
criteria.add(Restrictions.eq("d.metadataGuid",guidParameter));
criteria.add(Restrictions.ne("a.metadataGuid",guidParameter));
This results in the following SQL query
select
this_.descendant_id as descenda2_21_2_,
this_.ancestor_id as ancestor3_21_2_,
this_.length as length1_21_2_,
d2_.guid as metadata1_20_0_,
d2_.name as name5_20_0_,
a1_.guid as metadata1_20_1_,
a1_.name as name6_20_1_
from
data_closure this_
inner join
data d2_
on this_.descendant_id=d2_.metadata_guid
inner join
data a1_
on this_.ancestor_id=a1_.metadata_guid
where
d2_.guid=?
and a1_.guid<>?
which looks like it is correctly implementing the join fetch. However when I execute
List list = criteria.list();
I see one of these entries in the SQL log per row in the result set
Result set row: 0
DEBUG Loader - Loading entity: [Data#testGuid19]
DEBUG SQL -
select
data0_.guid as guid1_20_0_,
data0_.title as title5_20_0_,
from
data data0_
where
data0_.guid=?
Hibernate:
(omitted)
DEBUG Loader - Result set row: 0
DEBUG Loader - Result row: EntityKey[Data#testGuid19]
DEBUG TwoPhaseLoad - Resolving associations for [Data#testGuid19]
DEBUG Loader - Loading entity: [DataContents#7F1134F890A446BBB47F3EB64C1CF668]
DEBUG SQL -
select
dataContents0_.guid as guid_12_0_,
dataContents0_.isoCreationDate as isoCreat2_12_0_,
from
dataContents dataContents0_
where
dataContents0_.guid=?
Hibernate:
(omitted)
It is looks like even though the DataContents is marked as lazily loaded, it's being loaded eagerly.
So I either want some way in my query to fetch join DataClosure and Data and lazily fetch DataContents, or to fetch join the DataContents if that is not possible.
Edit:
Modelling the closure table like this
class DataClosure {
#Id
#Column(name = "ancestor_id", nullable = false, length =36 )
private String ancestorId;
#Id
#Column(name = "descendant_id", nullable = false, length =36 )
private String descendantId;
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "ancestor_id", nullable = false)
private Data ancestor;
#ManyToOne(fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JoinColumn(name = "descendant_id", nullable = false)
private Data descendant;
private int length;
}
fixed the problem. In other words, having #Id annotation on entities from other tables seemed to cause the issue, even though there was nothing wrong with the queries generated.
I think your problem here might be this
#NotFound(action = NotFoundAction.IGNORE)
There are plenty of google results where using that causes the lazy loading to become eager. I think it is a bug in Hibernate.
Adding this to the list of annotations should fix the problem
#LazyToOne(value=LazyToOneOption.NO_PROXY)
Since that informs Hibernate that you will not try to use that property later on so no proxy is required.

Hibernate One to Many Mapping is trying to update mapping column to null

I am using below mapping
#Entity
#Table(name = "test")
public class TestOrder implements Serializable {
#Id
#Column(name = "orderid", updatable = false)
protected Long orderId;
#OneToMany(cascade = {CascadeType.ALL})
#JoinColumn(name = "order_id_fk")
private List<TestDetails> details;
//getters and setters
}
#Entity
#Table(name="test_details")
public class TestDetails implements Serializable {
#Id
//Generator
#Column(name = "id", updatable = false, insertable = false)
protected Long id;
#Column(name="order_id_fk", updatable = false)
private Long orderId;
//getters and setters
}
When I update/insert the data, it's trying to update the order_id_fk to null
SQL [update test_details set order_id_fk'='null where order_id_fk'='? and id'='?]; constraint [null];
Any help is much appreciated.
Updating/Inserting using Spring Integration
<int-jpa:updating-outbound-gateway entity-class="com.aaaa.TestOrder" entity-manager-factory="myEntityManagerFactory" persist-mode="MERGE">
<int-jpa:transactional propagation="REQUIRED" transaction-manager="myTransactionManager" />
</int-jpa:updating-outbound-gateway>
You need to fetch values of TestDetails entity eagerly.
Just modify in your annotation with,
#OneToMany(fetch = FetchType.EAGER, mappedBy="testOrder", cascade=CascadeType.ALL)
Hope this will work.
I ran your code and it works correctly (when added #GeneratedValue(strategy = GenerationType.AUTO) to both IDs).
em.getTransaction().begin();
TestOrder to = new TestOrder();
TestDetails td1 = new TestDetails();
TestDetails td2 = new TestDetails();
TestDetails td3 = new TestDetails();
to.setDetails(Arrays.asList(new TestDetails[] {td1, td2, td3}));
em.persist(to);
em.getTransaction().commit();
what results in following sqls:
[03/07/14 10:03:30] INFO jdbc.sqlonly: insert into test (orderid) values (1)
[03/07/14 10:03:30] INFO jdbc.sqlonly: insert into test_details (order_id_fk, id) values (NULL, 2)
[03/07/14 10:03:30] INFO jdbc.sqlonly: insert into test_details (order_id_fk, id) values (NULL, 3)
[03/07/14 10:03:30] INFO jdbc.sqlonly: insert into test_details (order_id_fk, id) values (NULL, 4)
[03/07/14 10:03:30] INFO jdbc.sqlonly: update test_details set order_id_fk=1 where id=2
[03/07/14 10:03:30] INFO jdbc.sqlonly: update test_details set order_id_fk=1 where id=3
[03/07/14 10:03:30] INFO jdbc.sqlonly: update test_details set order_id_fk=1 where id=4
So, if the code you posted is the code which you compile then TestOrder entity is not able to generate its ID (lack of #GeneratedValue annotation).
In case of database generating IDs (i.e. using autonumbering) for this entity, you should set #GeneratedValue to IDENTITY to let your jpa provider know that it must reread inserted row afterwards. If jpa provider fails to reread id, it updates test_details order_id_fk column with null.
PS. Why didn't you set many-to-one relationship at TestDetails side?
You may have to change the entity like below. As far as I remember, if you are trying to set the foreign key of a column this works best.
#Entity
#Table(name="test_details")
public class TestDetails implements Serializable {
#Id
//Generator
#Column(name = "id", updatable = false, insertable = false)
protected Long id;
#Column(name="order_id_fk", updatable = false, insertable = false)
private TestOrder orderId;
//getters and setters
}

OneToOne between two tables with shared primary key

I'm trying to set up the following tables using JPA/Hibernate:
User:
userid - PK
name
Validation:
userid - PK, FK(user)
code
There may be many users and every user may have max one validation code or none.
Here's my classes:
public class User
{
#Id
#Column(name = "userid")
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long userId;
#Column(name = "name", length = 50, unique = true, nullable = false)
protected String name;
...
}
public class Validation
{
#Id
#Column(name = "userid")
protected Long userId;
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn(name = "userid", referencedColumnName = "userid")
protected User user;
#Column(name = "code", length = 10, unique = true, nullable = false)
protected String code;
...
public void setUser(User user)
{
this.user = user;
this.userId = user.getUserId();
}
...
}
I create a user and then try to add a validation code using the following code:
public void addValidationCode(Long userId)
{
EntityManager em = createEntityManager();
EntityTransaction tx = em.getTransaction();
try
{
tx.begin();
// Fetch the user
User user = retrieveUserByID(userId);
Validation validation = new Validation();
validation.setUser(user);
em.persist(validation);
tx.commit();
}
...
}
When I try to run it I get a org.hibernate.PersistentObjectException: detached entity passed to persist: User
I have also tried to use the following code in my Validation class:
public void setUserId(Long userId)
{
this.userId = userId;
}
and when I create a validation code I simply do:
Validation validation = new Validation();
validation.setUserId(userId);
em.persist(validation);
tx.commit();
But then since User is null I get org.hibernate.PropertyValueException: not-null property references a null or transient value: User.code
Would appreciate any help regarding how to best solve this issue!
I have been able to solve this problem of "OneToOne between two tables with shared primary key" in pure JPA 2.0 way(Thanks to many existing threads on SOF). In fact there are two ways in JPA to handle this. I have used eclipselink as JPA provider and MySql as database. To highlight once again no proprietary eclipselink classes have been used here.
First approach is to use AUTO generation type strategy on the Parent Entity's Identifier field.
Parent Entity must contain the Child Entity Type member in OneToOne relationship(cascade type PERSIST and mappedBy = Parent Entity Type member of Child Entity)
#Entity
#Table(name = "USER_LOGIN")
public class UserLogin implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="USER_ID")
private Integer userId;
#OneToOne(cascade = CascadeType.PERSIST, mappedBy = "userLogin")
private UserDetail userDetail;
// getters & setters
}
Child Entity must not contain an identifier field. It must contain a member of Parent Entity Type with Id, OneToOne and JoinColumn annotations. JoinColumn must specify the ID field name of the DB table.
#Entity
#Table(name = "USER_DETAIL")
public class UserDetail implements Serializable {
#Id
#OneToOne
#JoinColumn(name="USER_ID")
private UserLogin userLogin;
// getters & setters
}
Above approach internally uses a default DB table named SEQUENCE for assigning the values to the identifier field. If not already present, This table needs to be created as below.
DROP TABLE TEST.SEQUENCE ;
CREATE TABLE TEST.SEQUENCE (SEQ_NAME VARCHAR(50), SEQ_COUNT DECIMAL(15));
INSERT INTO TEST.SEQUENCE(SEQ_NAME, SEQ_COUNT) values ('SEQ_GEN', 0);
Second approach is to use customized TABLE generation type strategy and TableGenerator annotation on the Parent Entity's Identifier field.
Except above change in identifier field everything else remains unchanged in Parent Entity.
#Entity
#Table(name = "USER_LOGIN")
public class UserLogin implements Serializable {
#Id
#TableGenerator(name="tablegenerator", table = "APP_SEQ_STORE", pkColumnName = "APP_SEQ_NAME", pkColumnValue = "USER_LOGIN.USER_ID", valueColumnName = "APP_SEQ_VALUE", initialValue = 1, allocationSize = 1 )
#GeneratedValue(strategy = GenerationType.TABLE, generator = "tablegenerator")
#Column(name="USER_ID")
private Integer userId;
#OneToOne(cascade = CascadeType.PERSIST, mappedBy = "userLogin")
private UserDetail userDetail;
// getters & setters
}
There is no change in Child Entity. It remains same as in the first approach.
This table generator approach internally uses a DB table APP_SEQ_STORE for assigning the values to the identifier field. This table needs to be created as below.
DROP TABLE TEST.APP_SEQ_STORE;
CREATE TABLE TEST.APP_SEQ_STORE
(
APP_SEQ_NAME VARCHAR(255) NOT NULL,
APP_SEQ_VALUE BIGINT NOT NULL,
PRIMARY KEY(APP_SEQ_NAME)
);
INSERT INTO TEST.APP_SEQ_STORE VALUES ('USER_LOGIN.USER_ID', 0);
If you use Hibernate you can also use
public class Validation {
private Long validationId;
private User user;
#Id
#GeneratedValue(generator="SharedPrimaryKeyGenerator")
#GenericGenerator(name="SharedPrimaryKeyGenerator",strategy="foreign",parameters = #Parameter(name="property", value="user"))
#Column(name = "VALIDATION_ID", unique = true, nullable = false)
public Long getValidationId(){
return validationId;
}
#OneToOne
#PrimaryKeyJoinColumn
public User getUser() {
return user;
}
}
Hibernate will make sure that the ID of Validation will be the same as the ID of the User entity set.
Are you using JPA or JPA 2.0 ?
If Validation PK is a FK to User, then you do not need the Long userId attribute in validation class, but instead do the #Id annotation alone. It would be:
Public class Validation
{
#Id
#OneToOne(cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn(name = "userid", referencedColumnName = "userid")
protected User user;
#Column(name = "code", length = 10, unique = true, nullable = false)
protected String code;
...
public void setUser(User user)
{
this.user = user;
this.userId = user.getUserId();
}
...
}
Try with it and tell us your results.
You need to set both userId and user.
If you set just the user, then the id for Validation is 0 and is deemed detached. If you set just the userId, then you need to make the user property nullable, which doesn't make sense here.
To be safe, you can probably set them both in one method call:
#Transient
public void setUserAndId(User user){
this.userId = user.getId();
this.user = user;
}
I marked the method #Transient so that Hibernate will ignore it. Also, so you can still have setUser and setUserId work as expected with out any "side effects."

Categories

Resources