I am getting a ConstraintViolationException when I try to saveOrUpdate using hibernate. When I insert a brand new object for a user, the save works perfectly but when I try to update it fails.
In the database table I have a unique not null primary key and a unique not null foreign key named userid
My pojo declaration is as follows;
#Id #GeneratedValue
#Column(name = "id")
private int id;
#Column(name="userid")
private int userid;
#Column(name = "homephonenumber")
protected String homeContactNumber;
#Column(name = "mobilephonenumber")
protected String mobileContactNumber;
#Column(name = "photo")
private byte[] optionalImage;
#Column(name = "address")
private String address;
My insert statement looks as follows;
public boolean addCardForUser(String userid, Card card) {
if(StringUtilities.stringEmptyOrNull(userid)){
throw new IllegalArgumentException("Cannot add card for null or empty user id");
}
if(card == null){
throw new IllegalArgumentException("Cannot null card to the database for user " + userid);
}
SessionFactory sf = null;
Session session = null;
try{
sf = HibernateUtil.getSessionFactory();
session = sf.openSession();
session.beginTransaction();
session.saveOrUpdate(card);
session.getTransaction().commit();
return true;
}catch(Exception e){
logger.error("Unable to add Card to the database for user " + userid );
}finally{
DatabaseUtilities.closeSessionFactory(sf);
DatabaseUtilities.closeSession(session);
}
return false;
}
The exception I get says
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:
Duplicate entry '16' for key 'userid_UNIQUE'
And the database looks like this
What am I doing wrong, should the database entry not be updated?
For update, you should load the Card instance first, after some fields updated, then, invoke addCardForUser to update it, if the Card instance is not loaded via hibernate, hibernate will recogize it as a new record, if this card has the the userId with other card records in database, the unique constrain is violated!
You should not put a unique constraint on your foreign key. The foreign key models a one-to-many relation ship. While your parent-objects may only have one user, the same user may be related to many different parent objects. Thus, there will not necessarily be only one row in your parent object table for one userId.
Related
I need to find a sensor by its ID and its user ID, which it belongs to.
Repository:
#Query("SELECT * from sensors LEFT JOIN users_sensors us on sensors.sensor_id = us.sensor_id " +
"WHERE sensors.sensor_id = :sensorId AND us.user_id = :userId")
Optional<Sensor> findBySensorIdAndUsersId(#Param("sensorId") Long sensorId, #Param("userId") String userId);
Populated data before calling a method:
INSERT INTO users (id) VALUES('user1');
INSERT INTO sensors (fk_sensor_type) VALUES(0);
INSERT INTO users_sensors (user_id, sensor_id) VALUES('user1', 1);
Sensor class contains:
#Id
#Column(name = "sensor_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long sensorId;
private int sensorType;
#ManyToMany(mappedBy = "sensors")
private List<User> users = new ArrayList<>();
User class:
#Entity
#Table(name = "users")
public class User {
#Id
#NotBlank
#Column(name = "id")
private String id;
#ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(
name = "users_sensors",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "sensor_id")
)
final List<Sensor> sensors = new ArrayList<>();
// Constructors
// Getters and setters
}
users_sensors scheme:
create table users_sensors
(
id bigint primary key not null generated by default as identity,
user_id text not null,
sensor_id bigint not null,
foreign key (sensor_id) references sensors (sensor_id)
match simple on update no action on delete no action,
foreign key (user_id) references users (id)
match simple on update no action on delete no action
);
Method:
private static final Sensor sensor = new Sensor();
public void shouldReturnUserSensor() {
String userId = "user1";
// user is PRESENT
Optional<User> user = userRepository.findById(userId);
// inserts & returns 2
sensor.setUsers(List.of(user.get()));
sensor.setSensorType(0);
Long newSensorId = sensorRepository.save(sensor).getSensorId();
// expected sensor is NULL
Optional<Sensor> expectedSensor = sensorRepository.findBySensorIdAndUsersId(newSensorId, userId);
}
My expectedSensor is NULL and not found. When I run exact query provided above in the post under Repository in Query Console, it returns correct values, but in the app it does not. How to solve this issue?
New relations in the database should be added on the owning side, according to the answer:
Using mappedBy, If we only call person.getDocuments().add(document),
the foreign key in ID_DOCUMENTS will NOT be linked to the new
document, because this is not the owning /tracked side of the
relation!
To link the document to the new person, you need to explicitly call
document.setPerson(person), because that is the owning side of the
relation.
When using mappedBy, it is the responsibility of the developer to know
what is the owning side, and update the correct side of the relation
in order to trigger the persistence of the new relation in the
database.
This means that a sensor should be added to a user on the user's side, or the sensor should be switched to be owning side.
You should also consider declaring query method as shown below, which doesn't require writing JPQL or native SQL.
public Optional<Sensor> findByIdAndUsers_Id(Long sensorId, String userId);
Here is my entity and table structure as below
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq")
#SequenceGenerator(name = "seq", sequenceName = "user_data_seq")
private Integer id;
#Column(name = "FName")
private String fname;
#Column(name = "LName")
Private String lname;
CREATE TABLE user
(
id integer NOT NULL DEFAULT nextval('user_data_seq'::regclass),
fname character varying(32) COLLATE pg_catalog."default" NOT NULL,,
lname character varying(32) COLLATE pg_catalog."default" NOT NULL
CONSTRAINT pk_user PRIMARY KEY (id),
CONSTRAINT user_fname_unique UNIQUE (fname)
}
I have made one manual entry in USER through pgAdmin, its inserted with the latest sequence value.
INSERT INTO user(fname, lname) VALUES('AAA', 'BBB');
Now application is using and trying to update the LName for the existing record. But looks like JPA is considering this as new entry and trying re-insert, due to that unique constraint violation error is occurring.
public User updateLName(final User user) {
User userToUpdate = userRepository.findById(user.getId());
userToUpdate.setLName("CCC");
userToUpdate = userRepository.save(userToUpdate);
return userToUpdate;
}
The below code (if condition) from Spring JPA, is still returning as true and make the record to insert as NEW :
org.springframework.data.jpa.repository.support.SimpleJpaRepository
#Transactional
public <S extends T> S save(S entity) {
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
But this wont be a problem for the records inserted through application already and again able to update.
The identity of entities is defined by their primary keys. So you have to specify which data you want to update. To update existing using save() method you can fetch the existing data by id.
User userToUpdate = userRepository.getOne(id);
The update its data
userToUpdate.setFname(userDto.getFName());
And finally, call the save method it update the data in database
userRepository.save(userToUpdate);
I am trying to make an update in the database row. I am having this exception
Caused by: org.hibernate.TransientObjectException: The given object has a null identifier: com.models.User
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.getUpdateId(DefaultSaveOrUpdateEventListener.java:270)
this is my controller code for the submit action from the jsp file
// create new user object
User user = new User();
user.setName(name);
user.setEmail(email);
user.setActive(false);
_userDao.update(user);
this is my dao that defines the update with hibernate session factory utility
public void update(User user) {
getSession().update(user);
}
//EDITTED: this is my mapping for user entity class
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "userId")
private Integer id;
#Column(nullable = false)
private String name;
#Column(unique = false, nullable = false)
private String email;
#Column(nullable = true)
private boolean active;
I am not able to update the user record where the email address is equal to the one entered in the jsp input form. Kindly assist, this is my first attempt in updating a field with hibernate sessionfactory.
The problem is that you are using update method on not existing entity. If you want to save newly created entity you have to use save or saveOrUpdate.
update method works only if entity already exists in DB.
I had the slimier situation at my work. As #ByeBye said the problem was trying to update an entity which was not persisted before. See following example
SalesOrder salesOrder = consignment.getSalesOrder();
if (salesOrder != null) {
// some code
}
else{
//
salesOrder = new SalesOrder();
consignment.setSalesOrder(salesOrder);
}
salesOrderRepository.update(salesOrder); // hibernate update
return salesOrder;
}
Here when the execution of code comes to else part. It try to create a new sales order object (where the object id is null ) and try to update it. And this cause the mentioned error.
So my fix was simply changing the update to saveOrUpdate . see below
.......
salesOrder = new SalesOrder();
consignment.setSalesOrder(salesOrder);
}
salesOrderRepository.saveOrUpdate(salesOrder);
return salesOrder;
}
So then it commands hibernate to first persist the non-existing objects and then do all the updates upon that (if required ).
Hope this scenario will help to all
Hi i am new to hibernate.
I have a java web project and i am using spring mvc with hibernate in this project and for database i am using my sql.
The issue i am facing is that :
i have a table in db as user and in java project as user.java
and i have another table references and in java project i have referances.java
and i have created a mapping table as user_referances_mapping(user_id, referance_id).
and i have one to many relationship between user and referances table.
Now when i try to get the user data it gives me user table coloumns data but not the referances data and returns only null list for referances table.
On the other hand i also have similar mapping tables with user table like role, address with one to many mapping only and data is getting retrieved from those tables.
So can anyone help me getting the solution, that why i am not getting the data of the referances table using the one to many relationship and what should be the solution to it.
mapping of referances table in user table:
#OneToMany
#Basic(optional = true)
#BatchSize(size = 5)
#Cascade(CascadeType.ALL)
#Cache(region = IAppConstants.CACHE_REFERANCES, usage = CacheConcurrencyStrategy.READ_WRITE)
#JoinTable(name = "user_referances_mapping", joinColumns = { #JoinColumn(name = "user_id") }, inverseJoinColumns = { #JoinColumn(name = "referance_id") })
private List<Referances> referances = new ArrayList<Referances>();
public List<Referances> getReferances() {
return referances;
}
public void setReferances(List<Referances> referances) {
this.referances = referances;
}
UserDao class function :
public User getC2SUserByContactNoOrEmail(final String value) throws ApplicationException {
try{
#SuppressWarnings("unchecked")
Query query = currentSession().createQuery(
IQueryConstants.FETCH_USER_BY_CONTACTNO_OR_EMAIL);
query.setParameter("contactNo", value);
query.setParameter("email", value);
return (User) query.uniqueResult();
}catch(Exception e){
throw new ApplicationException(
"Issue occurred while fetching user by: " + value, e);
}
//return null;
}
FETCH_USER_BY_CONTACTNO_OR_EMAIL = "FROM User WHERE contactNo=:contactNo or email=:email";
If I'm right, the OneToMany relations are defined as "lazy" by default, which means you need to explicitly state that you want to fetch the related records.
Try modifying the query like this:
FETCH_USER_BY_CONTACTNO_OR_EMAIL = "FROM User u LEFT JOIN FETCH u.referances WHERE u.contactNo=:contactNo or u.email=:email";
I am trying to learn JPA and I have a problem that I stucked in since 2 days.
I have a table named "User" includes id,email,password,username and status.
As you guess email and username columns are unique.
I also have a Class called User something like that :
#Entity
#Table(name = "user", uniqueConstraints = #UniqueConstraint(columnNames = {"username", "email"}))
public class User {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
#Column(name="email", nullable=false)
private String email;
#Column(name="password", nullable=false)
private String password;
#Column(name="username", nullable=false)
private String username;
#Column(name="status", nullable=false)
private String status;
Rest of this class is getters and setters.
I am trying to insert a value by using JPA with Hibernate.
try {
em = jpaResourceBean.getEMF().createEntityManager();
em.getTransaction().begin();
user.setStatus(UserStatus.PENDING.toString());
em.persist(user);
em.getTransaction().commit();
logger.info("User " + user.getUsername() + " has been registered");
// Attention starts
} catch (XXXXXX) {
if (XXXXX.getYYY().equals("ZZZZZ")) logger.info("User name is already exist");
if (XXXXX.getMMM().equals("LLLLL")) logger.info("Email is already exist");
}
// Attention end
All I want to know : How can I understand is the problem with the username constraint or the email unique constraint? While you can check my Attention start and end block, I am sure that you get my point : )
Thanks in advance.
You should not use auto-generated schemas in production, so why not simply set the contraint name in your schema. Then when the exception is generated, you will get the failing constraint name.
create table users (
user varchar2(20) not null,
email varchar2(20) not null,
constraint user_uq unique(user),
constraint email_uq unique(email)
)
You might want to have a look in the database before you insert to see if they are already there.. that way you most likely wont get an exception anyway...
Try this:
catch(javax.validation.ConstraintViolationException exception) {
Set<ConstraintViolation<?>> constraintViolations = exception.getConstraintViolations();
...
}