How to update Child entity along with Parent entity in Spring Boot? - java

I am trying to update the parent entity and child entity (comes with parent) using JPA. I tried many ways but I couldn't do it. It gives me the following error.
javax.persistence.EntityExistsException: A different object with the
same identifier value was already associated with the session Contact(6)
Following is the entity,
Parent -> User.java
#Entity
public class User extends Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_generator")
#SequenceGenerator(name = "seq_generator", sequenceName = "seq_users", initialValue = 1, allocationSize = 1)
private Long id;
private String firstName;
private String lastName;
private String email;
private String password;
private String street;
private String city;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "userContact")
private Contact contact;
}
Child -> Contact.java
#Entity
public class Contact extends Serializable {
private static final long serialVersionUID = 1L;
#Getter(value = AccessLevel.NONE)
#Setter(value = AccessLevel.NONE)
#Id
#Column(insertable = false, updatable = false)
private Long id;
#MapsId
#OneToOne
#JoinColumn(name = "id", nullable = false)
private User userContact;
private String phone;
}
In my Service.java I am updating like this,
#Override
#Transactional
public User update(Long id, User user) {
Optional<User> curUser = userRepo.findById(id);
user.setId(curUser.get().getId());
user.setStreet(curUser.get().getStreet());
user.setCity(curUser.get().getCity());
Contact contact = user.getContact();
contact.setUserContact(user);
user.setContact(contact);
return userRepo.save(user);
}
And the update JSON payload is like this,
{
"firstName": "FirstName",
"lastName": "LastName",
"email": "Email",
"password": "Password",
"contact": {
"phone": "0123456789"
}
}
So why I can't update this at once? The insert is working fine but this update is not working. What I am doing wrong here? I really appreciate it if anybody can help me. Thanks in advance.
Here is the new way I tried,
#Override
#Transactional
public User update(Long id, User user) {
Optional<User> curUser = userRepo.findById(id);
curUser.setFirstName(user.getFirstName());
curUser.setlastName(user.getLastName());
curUser.setEmail(user.getEmail());
curUser.setPassword(user.getPassword());
Contact contact = user.getContact();
contact.setUserContact(curUser);
curUser.setContact(contact);
return userRepo.save(curUser);
}

Because you have the same user (i.e. same id) represented twice in the persistence context.
This line loads the user curUser in the persistence context.
Optional<User> curUser = userRepo.findById(id);
This line makes user the same user by id, but it is a different instant.
user.setId(curUser.get().getId());
And finally this line tries to add user to the persistence context.
userRepo.save(user);
This would result with the two instances being attached to the persistence context while representing the same user with the same id which would result in an inconsistent state since JPA wouldn't know which version to persist. Therefore this is forbidden and throws the exception you are seeing.
In your case changing
Optional<User> curUser = userRepo.findById(id);
user.setId(curUser.get().getId());
to
user.setId(id);
should do the trick.
Regarding your comments:
The same problem exists for referenced objects. You'll have to make sure that you: either load an entity and change it's attribute, or do NOT load the entity, not directly nor indirectly via a reference and save a detached entity, constructed from your request.
If you load an entity and then try to save a detached instance of it, you will get this kind of error.

Related

Jpa entity same record duplicated and making unique key violation

I am using Jpa, my website is shows a form in 3 steps, in every step i'm making save() in many steps :
First save() will create a record A with a primary key id and another unique key userId, data as firstname and lastname
Step 2 is where the user will enter his adress, then the same first save() is played
But I see an error :
ERROR: duplicate key value violates unique constraint "userId" XXXX already exists
A save() is supposed to make an update if the record exists, but I noted that my id increments in every save() how to prevent this incrementation ?
here is my entity :
#Table(name = "user", schema = "salesforce", uniqueConstraints = {
#UniqueConstraint(columnNames = { "userId" }) })
public class Beneficiary implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "userID")
private String userId;
#Column(name = "firstname")
private String firstname;
#Column(name = "lastname")
private String lastname;
The service :
void save(List<User> users, String idDocument); // it can be same users with same idDocument
public Beneficiary save(Beneficiary newBeneficiary);
The repository
#Override
public User save(User newUser) {
return userRepository.save(newUser);
}
#Override
#Transactional
public void save(List<User> users, String idDocument) {
users.forEach(newUser -> {
newUser.setDocument(new Document());
newUser.getDocument().setIDDocument(idDocument);
save(newUser);
});
}
In every step, the same save() is played but first i got the error saying that I'm trying to write a new ligne with the same UserId and it creates the duplication violation
This re-write is due to the fact that Id is incrementing ! why ?

Column 'brand_id' cannot be null

I create new Product using Spring Boot / Hibernate / JPA and get Column 'brand_id' cannot be null error. I don't know why this error happen. Does anyone can explain where I was wrong?
Product:
#Entity
public class Product {
#javax.persistence.Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
#NotBlank(message = "Product name is required")
private String name;
private String image;
private String description;
private double price;
private int countInStock;
private double rating;
private int numReviews;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "brand_id", updatable = false, nullable = false)
#JsonIgnore
private Brand brand;
#ManyToMany
#JoinTable(name = "product_category", joinColumns = #JoinColumn(name = "product_id"), inverseJoinColumns = #JoinColumn(name = "category_id"))
#JsonIgnore
private List<Category> categories;
Brand:
#Entity
public class Brand {
#javax.persistence.Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
#NotBlank(message = "Brand name is required")
private String name;
#OneToMany(cascade = CascadeType.REFRESH, fetch = FetchType.LAZY, mappedBy = "brand", orphanRemoval = true)
private List<Product> products;
Category:
#Entity
public class Category {
#javax.persistence.Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
#NotBlank(message = "Category name is required")
private String name;
#ManyToMany(mappedBy = "categories")
private List<Product> products;
And my Product Object that used to input create new Product:
{
"name": "test",
"image": "/images/test.jpg",
"description": "test",
"brand_id": 4,
"category_id": 1,
"price": 99.99,
"countInStock": 100,
"rating": 4.5,
"numReviews": 120
}
Wherever you are creating this Product object, you must provide it with a Brand object.
Currently, you are only providing it with an integer as a Brand.
What you need to do is fetch the brand object you are referring to from your DB and include it in the instantiation of your new Product.
Probably the best and simplest approach to this would be using the EntityManager to get a reference to that Brand.
Getting the EntityManager is very simple when using spring.
#PersistenceContext
private EntityManager entityManager;
Now, simply use it to get the reference to the target Brand.
Brand brand = entityManager.getReference(Brand.class , brand_id);
Use this brand to instantiate your new Product and insert it to the DB without any exceptions.
How to automate this logic into the unmarshalling process
If you are always going to want to use this logic when creating a Product, you can use this logic in the constructor. If you only want to use this method when unmarshalling, here is an example that is based of something I wrote recently. I am using an XmlAdapter, but there are also JSONAdapater classes you can look into and should work about the same way.
Create you adapter class. This class is going to be used to parse JSON to java object.
//Once again, I am using an XmlAdapter, but the idea should be similar with JSONAdapters
#Service
public class BrandIdXmlAdapter extends XmlAdapter<String, Brand> {
#PersistenceContext
private EntityManager entityManager;
//v is the String that is going to be unmarshalled.
//In our case, its going to be the brand_id String.
#Override
public Brand unmarshal(String v) {
Brand brand = entityManager.getReference(Brand.class, v);
return brand;
}
There is also a possibility to override the marshal() method for parsing from a POJO to XML/JSON.
The only problem here is that to be able to use use the PersistenceContext annotation, this class has to be an EJB.
We are going to workaround that by telling Spring this is a necessary service.
First step is to give the adapter class the Service annotation(Done in example above).
The next step is to go to where you would want to unmarshall the input into a POJO (either the controller if you receive it as a request or the service if you are going to request it from another service) and autowire the adapter
#Autowired
private BrandIdXmlAdapter xmlAdapter;
Next step is to create the unmarshaller that will use this adapter.
JAXBContext context = JAXBContext.newInstance(Product.class);
brandIdUnmarshaller = context.createUnmarshaller();
brandIdUnmarshaller.setAdapter(xmlAdapter);
Now when receiving the data, use the brandIdUnmarshaller.unmarhsall() method.
Last step is to annotate your Brand variable in Product to tell it to use the adapter when parsing this specific variable.
public class Product {
.
.
.
//again, find the right annotation according to your JSONAdapter
#XmlJavaTypeAdapter(BrandIdXmlAdapter.class)
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "brand_id", updatable = false, nullable = false)
#JsonIgnore
private Brand brand;
}
Now everytime you parse from JSON to Product, you will automatically get a Product that contains a valid Brand object.

Hibernate One-to-One DTO-Entity population

I have 2 entities in my DB with one-to-one one directional mapping:
User and PasswordResetToken. The idea behind this is to create new token each time user requests password reset and store only the latest one.
Below are my entities:
#Entity
#Table(name = "USERS")
#Getter #Setter
public class User implements Serializable {
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.AUTO, generator = "usersSeq")
#SequenceGenerator(name = "usersSeq", sequenceName = "SEQ_USERS", allocationSize = 1)
private long id;
#Column(name = "NAME")
private String name;
#Column(name = "PASSWORD")
private String password;
#Column(name = "EMAIL")
private String email;
#Column(name = "ROLE")
private Integer role;
}
///...
#Entity
#Table(name = "PASSWORD_RESET_TOKENS")
#Getter
#Setter
public class PasswordResetToken implements Serializable {
private static final int EXPIRATION = 24;
#Column(name = "TOKEN")
private String token;
#Id
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(nullable = false, name = "user_id")
private User user;
#Column(name = "EXPIRY_DATE")
private Instant expiryDate;
public PasswordResetToken() {
}
public void setExpiryDate(ZonedDateTime expiryDate) {
this.expiryDate = expiryDate.plus(EXPIRATION, ChronoUnit.HOURS).toInstant();
}
}
Also, I have DTOs created for both of them to pass them around my app.
Code snippets:
#Getter #Setter
public class PasswordResetTokenModel {
private String token;
private ZonedDateTime expiryDate;
private UserModel user;
}
UserModel is also used for Spring Security
#Getter
#Setter
public class UserModel extends User {
public UserModel(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}
private long id;
private String name;
public String getEmail() {
return this.getUsername();
}
}
For population I've created 2 populators:
#Component
public class UserPopulatorImpl implements UserPopulator {
#Autowired
UserDetailsService userDetailsService;
#Override
public UserModel populateToDTO(User user) {
UserModel userModel = new UserModel(user.getEmail(), user.getPassword(), userDetailsService.getAuthorities(user.getRole()));
userModel.setId(user.getId());
return userModel;
}
#Override
public User populateToDAO(UserModel userModel) {
User user = new User();
user.setEmail(userModel.getEmail());
user.setName(userModel.getName());
user.setPassword(userModel.getPassword());
//TODO: change it!
user.setRole(1);
return user;
}
}
//...
#Component
public class PasswordResetTokenPopulatorImpl implements PasswordResetTokenPopulator {
#Autowired
UserPopulator userPopulator;
#Override
public PasswordResetTokenModel populateToDTO(PasswordResetToken passwordResetToken) {
PasswordResetTokenModel passwordResetTokenModel = new PasswordResetTokenModel();
passwordResetTokenModel.setUser(userPopulator.populateToDTO(passwordResetToken.getUser()));
passwordResetTokenModel.setToken(passwordResetToken.getToken());
passwordResetTokenModel.setExpiryDate(ZonedDateTime.ofInstant(passwordResetToken.getExpiryDate(), ZoneId.systemDefault()));
return passwordResetTokenModel;
}
#Override
public PasswordResetToken populateToDAO(PasswordResetTokenModel passwordResetTokenModel) {
PasswordResetToken passwordResetToken = new PasswordResetToken();
passwordResetToken.setExpiryDate(passwordResetTokenModel.getExpiryDate());
passwordResetToken.setUser(userPopulator.populateToDAO(passwordResetTokenModel.getUser()));
passwordResetToken.setToken(passwordResetTokenModel.getToken());
return passwordResetToken;
}
}
I'm saving object using
sessionFactory.getCurrentSession().saveOrUpdate(token);
When I use this code, I'm getting following exception
object references an unsaved transient instance - save the transient instance before flushing: com.demo.megaevents.entities.User
There are currently 2 issues in this code:
Seems like Cascade.ALL in my OneToOne mapping is not working. If
I create separate primary key in Token class everything works almost
as expected but storing every created token in DB (more like
OneToMany relation), however I want to avoid it as I need to store
only one token per user in my DB
I don't like using new in populators, as it forces hibernate to create new object while flushing session. However, I also don't want to do another select to fetch this data from DB because just before mentioned populator I already do this query to fetch it and I think that it's an overhead.
Also, I really want to have DTOs and I don't want to remove DTO layer.
So, my questions:
What is the correct way to handle population between DTO and entities?
Are there any other improvements (probably architectural) to my solution?
Thanks a lot.
I'm not sure why you would let UserModel extend User, but I guess you did that because you didn't want to have to copy all properties from User into UserModel. Too bad, because that's what is going to be needed to have a clean separation between the entity model and data transfer model.
You get that exception because you try to persist a PasswordResetToken that has a reference to a User object with an id, but the User isn't associated with the current session. You don't have to query the user, but at least association it with the session like this:
PasswordResetToken token = // wherever you get that from
Session s = sessionFactory.getCurrentSession();
token.setUser(s.load(User.class, token.getUser().getId());
s.persist(token);
Cascading would cause the User to be created/inserted or updated via a SQL INSERT or UPDATE statement which is apparently not what you want.
You could do the Session.load() call in you populators if you want, but I'd not do that. Actually I would recommend not having populators at all, but instead create the entity objects in your service instead.
Normally you only have a few(mostly 1) ways of actually creating a new entity object, so the full extent of the transformation from DTO to entity will only be relevant in very few cases.
Most of the time you are going to do an update and for that, you should first select the existing entity and apply the fields that are allowed to be changed from the DTO on the entity object.
For providing the presentation layer with DTOs I would recommend using Blaze-Persistence Entity Views to avoid the manual mapping boilerplate and also improve performance of select queries.

Not-null property references a transient value - transient instance must be saved before current operation

I have 2 domain models and one Spring REST Controller like below:
#Entity
public class Customer{
#Id
private Long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;
// other stuff with getters/setters
}
#Entity
public class Country{
#Id
#Column(name="COUNTRY_ID")
private Integer id;
// other stuff with getters/setters
}
Spring REST Controller:
#Controller
#RequestMapping("/shop/services/customers")
public class CustomerRESTController {
/**
* Create new customer
*/
#RequestMapping( method=RequestMethod.POST)
#ResponseStatus(HttpStatus.CREATED)
#ResponseBody
public com.salesmanager.web.entity.customer.Customer createCustomer(#Valid #RequestBody Customer customer, Model model, HttpServletRequest request, HttpServletResponse response) throws Exception {
customerService.saveOrUpdate(customer);
return customer;
}
// other stuff
}
I am trying to call above REST service with below JSON as body:
{
"firstname": "Tapas",
"lastname": "Jena",
"city": "Hyderabad",
"country": "1"
}
Where country code 1 is already there in Country table. The problem is when I am calling this service getting below error:
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.test.model.Customer.country -> com.test.model.Country; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: Not-null property references a transient value - transient instance must be saved before current operation: com.test.model.Customer.country -> com.test.model.Country
Any help will be appreciated!
Try putting CascadeType.ALL
#OneToOne(fetch = FetchType.EAGER,cascade=CascadeType.ALL)
#JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;
I had a similar problem. Two entities: Document and Status.
Document had a relationship OneToMany with Status, that represented the history of Status the Document had.
So, there was a #NotNull #ManyToOne reference of Document inside Status.
Also, I needed to know the actual Status of Document. So, I needed another relationship, this time #OneToOne, also #NotNull, inside Document.
The problem was: how can I persist both entities the first time if both had a #NotNull reference to the other?
The solution was: remove #NotNull reference from actualStatus reference. This way, it was able to persist both entities.
Just to add an additional scenario that led me to this exact same error:
Make sure that any backward references that may exist are not null.
Specifically in my case, I was using Mapstruct to update some fields of the entity, e.g.
MyClass newInstance = //...
MyClass dbInstance = repository.findByField(someField);
MyClassMapper.MAPPER.update(dbInstance, newInstance);
repository.save(dbInstance);
And my poor implementation of MyClassMapper led the backward references of dbInstance fields to be set to null when they should be pointing back to dbInstance.
I got same error and this is how I solved it:
1st Entity:
#Entity
public class Person implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int personId;
private String name;
private String email;
private long phoneNumber;
private String password;
private String userType;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "personCustomer", cascade
= CascadeType.ALL)
private Customer customer;
2nd Entity:
#Entity
public class Customer implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int customerId;
#OneToOne(fetch = FetchType.LAZY, optional = false, cascade =
CascadeType.ALL)
#JoinColumn(name = "person_customer")
#JsonIgnore
private Person personCustomer;
My Controller:
#PostMapping("/customer/registration")
public PersonCustomer addCustomer(#RequestBody Person person)
{
Customer customer = new Customer(person);
person.setCustomer(customer);
Customer cust = customerRepo.save(customer);
logger.info("{}", cust);
Optional<Person> person_Cust =
personRepo.findById(cust.getPersonCustomer().getPersonId());
Person personNew = person_Cust.get();
PersonCustomer personCust = new PersonCustomer();
if(cust.equals(null))
{
personCust.setStatus("FAIL");
personCust.setMessage("Registration failed");
personCust.setTimestamp(personCust.timeStamp());
}
personCust.setStatus("OK");
personCust.setMessage("Registration OK");
personCust.setTimestamp(personCust.timeStamp());
personCust.setPerson(personNew);
return personCust;
}
The problem got solved when I added "person.setCustomer(customer);".
As both POJO classes has each others reference, so we have to "set" each others reference before using the JPA repository method(customerRepo.save(customer));
I had the exact same problem. The solution seems to be to send the JSON like this:
{
"firstname": "Tapas",
"lastname": "Jena",
"city": "Hyderabad",
"country": {"id":"1"}
}
I guess #RequestBody tries to map an entity not a single field since the Customer instance is referencing a Country instance.
(I have similarly two entities, joined. In the DB, records for the referenced entity (Country in your case) were already created but the entity creation (Customer in your case) with a json, provided the same error message. For me CascadeType.ALL not helped but the above written change in the JSON solved the problem. For further config of course CascadeType can be considered.)
you should change :
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;
to :
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name="COUNTRY_ID")
private Country country;
just delete nullable setting.

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