#Id in hibernate - java

I have some code
#Entity
#Table(name = "USER")
public class User {
#Id
#Column(name = "id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
When I try to add user to table, I catch
org.h2.jdbc.JdbcSQLException: NULL not allowed for column "ID"; SQL statement:
insert into USER (id, birthday, email, first_name, last_name, login, password, id_role) values (null, ?, ?, ?, ?, ?, ?, ?) [23502-181]
This is my code to add user to db
stock2.setBirthday(new Date(46));
stock2.setEmail("sss");
stock2.setFirstName("oleg");
stock2.setId(506l);
stock2.setLastName("gubov");
stock2.setLogin("OP");
stock2.setPassword("1");
Role role2 = new Role();
role2.setName("poil");
role2.setId(7l);
stock2.setRole(role2);
HibernateUserDao dao = new HibernateUserDao();
System.out.println(stock2.getId() + " -----------before");
dao.create(stock2);
and code of create method:
public void create(User user) {
Session session = null;
try {
session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
session.save(user);
session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
throw e;
} finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}
In my opinion, id is 7,but not NULL.
And "java" thinks otherwise. Where can be the problem?

You call dao.create(), so your intention is to insert row into database. Why are you using GenerationType.IDENTITY while inserting your own id?
GenerationType.IDENTITY means that your JPA Provider will make use of table's IDENTITY column (so, the id will be assigned at databse side). Hibernate, knowing that, will not send your id in an SQL statement.
By the way, it is not the best option for generating ids. GenerationType.IDENTITY has some performance problems (it does not support preallocation and JPA Provider may make SELECT query after each inserted row, just to know what the generated id was).
To be able to insert your own values as ids you can simply remove #GeneratedValue (which de facto means that you want ids to be generated automatically) annotation.

User is a reserved keyword on most database engines, so change:
#Table(name = "USER")
to
#Table(name = "APP_USER")

Here is a link describing your issue and proposing possible solutions: http://en.wikibooks.org/wiki/Java_Persistence/Identity_and_Sequencing#Common_Problems_4
Excerpt:
null is inserted into the database, or error on insert.
[...] It may also be that you did not set your primary key column in your table to be an identity type.

Related

JPA many-to-one relationship CascadeType behavior

I have 2 tables: User and Loan. User have 3 fields: id (PK), first_name and last_name. Loan table have field user_id that is foreign key to User table:
The logic of my application: when I create new Loan object and set there a corresponding user it should create a new one for unique user or set a id of a user into user_id for existing user.
For that purpose my database have a unique index on first_name, last_name.
The Loan class uses User with #ManyToOne relationship:
public class Loan {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name = "user_id")
private User user;
... methods ...
When I add new user, everything is fine, it persists to db with new PK. But when I try to add an existing one I got and exception:
javax.servlet.ServletException: org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLIntegrityConstraintViolationException: The statement was aborted because it would have caused a duplicate key value in a unique or primary key constraint or unique index identified by 'FIRST_LAST_NAME' defined on 'USER'.
Error Code: 20000
Again when I put #ManyToOne(cascade = CascadeType.MERGE) I'm able only to enter existing users, as only I pass a new one that is not persisted in DB I got an exception:
javax.servlet.ServletException: org.springframework.dao.InvalidDataAccessApiUsageException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: User{id=null, firstName='a', lastName='a'}.;
#ManyToOne(cascade = CascadeType.ALL) didn't work as well.
UPDATE
the code for insertion new Loan is:
user = userService.findByName(firstName, lastName);
if (user == null) {
user = new User(firstName, lastName);
}
loan.setUser(user);
loanService.save(loan);
findByName() and save() methods are:
private EntityManager em;
public User findByName(String firstName, String lastName) {
TypedQuery<User> query = em.createQuery(
"SELECT u FROM User u WHERE u.firstName = :firstName " +
"AND u.lastName = :lastName", User.class)
.setParameter("firstName", firstName).setParameter("lastName", lastName);
List<User> users = query.getResultList();
if (!users.isEmpty()) {
return users.iterator().next();
} else {
return null;
}
}
public void save(User user) {
if (user.getId() == null) {
em.persist(user);
} else {
em.merge(user);
}
}
If you call Persist on Loan
a) with cascade.Persist set on the user relationship and the referenced user exists, set you will get an exception if the User instance was not read in from the same EntityManager instance/context - You are reading it through the userService and then saving the loan in the loanService, so the context would have to be container managed and within the same transaction to work.
b) If cascade.PERSIST (or cascade.ALL) is not set and the reference user is new, you will get the "new object was found" exception.
If you cannot perform the read of the user in the same EntityManager you are going to save the loan in, switching to using merge may help, as JPA should then check referenced entities and merge as appropriate. You will then need to have the cascade.MERGE option set on the relationships for the merge to be applied to the User instance.
Note that using merge is different from Persist in that what you passed in does not become managed by the context. That means after the transaction commits, the entities passed in will still not have any primary key values set. You may want to pass back the entity returned from Merge if you are going to continue to use the entities for any other operations.

not getting data from the mapping table using hibernate

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";

Hibernate (on postgres) dropping Schema from #JoinTable

I'm using Hibernate 4.3.10.Final (with SpringData JPA) running on a Postgres 4 database and have run into a very strange bug. Our app utilizes a database outside of the default "public" schema, and when we try to insert data Hibernate drops the correct schema.
Our model consists of an abstract "Log" class that uses single class inheritance to allow many different object types to insert a associated log message. See code below.
The schema already exists (hibernate doesn't create it) and booting validation runs fine, but when try to insert a new record we get the error relation "booking_log" does not exist -- which is missing the schema modifier (say customapp for our purposes). See the first line from the logs below to get an idea of what other insert statements look like.
I've dug through the mapping phase and verified Hibernate is indeed picking up the schema from the #JoinTable annotation, but not sure how we're losing it.
Any help debugging or possible solutions would be appreciated.
Thanks!
Log - Abstract super class
#MappedSuperclass
#Table(name="log", schema=Constants.DB_SCHEMA)
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name="log_type_id", discriminatorType = DiscriminatorType.INTEGER)
public abstract class Log {
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="log_seq_gen")
#SequenceGenerator(allocationSize = 1, name="log_seq_gen", sequenceName=Constants.DB_SCHEMA + ".log_id_seq")
private Long id;
// ...
}
BookingLog
#Entity
#DiscriminatorValue("2")
public class BookingLog extends Log implements TenantResource<Company,Long> {
#JoinTable(name="booking_log",
schema = Constants.DB_SCHEMA,
joinColumns = { #JoinColumn(name="log_id", referencedColumnName="id", nullable=false, updatable=false)},
inverseJoinColumns = { #JoinColumn(name="booking_id", referencedColumnName="id", nullable=false, updatable=false)})
#ManyToOne(cascade=CascadeType.ALL)
private Booking booking;
///...
}
** Logs **
2015-07-20_18:14:09.055 DEBUG org.hibernate.SQL - insert into customapp.booking_product (created_dt, created_by, modified_dt, modified_by, include_in_payroll, include_in_revenue, booking_id, description, payroll_percent, price, product_id, qty, id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
2015-07-20_18:14:09.072 DEBUG org.hibernate.SQL - insert into booking_log (log_date, details, log_time, user_id, booking_id, id) values (?, ?, ?, ?, ?, ?)
2015-07-20_18:14:09.176 DEBUG o.h.e.jdbc.spi.SqlExceptionHelper - could not execute statement [n/a]
org.postgresql.util.PSQLException: ERROR: relation "booking_log" does not exist
Based on your #JoinTable configuration and the insert statement that hibernate generates looks like the problem is the way you are triyng to add extra fields/data to the booking_log table.
I would need more details about your model to be sure but I think you are using a join-table and something else instead of create a class that models the join-table.
I mean, you have this
BookingLog (*) --------------------------------------> (1) Booking
but I think you really need this
BookingLog (1) ---> (1) BookingLogAssociation (*) ---> (1) Booking
Then the mapping will be like this,
#Entity
#DiscriminatorValue("2")
public class BookingLog extends Log implements TenantResource<Company,Long> {
#OneToOne(mappedBy="bookingLog")
private BookingLogAssociation booking;
}
Note the attributes of BookingLogAssociation, they are the extra field/data you want to add in booking_log table.
#Entity
#Table(name="booking_log")
#IdClass(BookingLogAssociationId.class)
public class BookingLogAssociation {
#Id
private long log_id;
#Id
private long booking_id;
#OneToOne
#PrimaryKeyJoinColumn(name="log_id", referencedColumnName="id")
private BookingLog bookingLog;
#ManyToOne
#PrimaryKeyJoinColumn(name="booking_id", referencedColumnName="id")
private Booking booking;
#Column(name="log_date")
#Temporal(TemporalType.DATE)
private Calendar logDate;
#Column(name="log_time")
#Temporal(TemporalType.TIME)
private Calendar logTime;
#ManyToOne
#JoinColumn(name="user_id")
private User user;
// Could be just an attribute too
//#Column(name="user_id")
//private long userId;
...
}
The BookingLogAssociationId class that represents the BookingLogAssociation's composite key.
public class BookingLogAssociationId implements Serializable {
private long log_id;
private long booking_id;
public int hashCode() {
return (int)(log_id + booking_id);
}
public boolean equals(Object object) {
if (object instanceof BookingLogAssociationId) {
BookingLogAssociationId otherId = (BookingLogAssociationId) object;
return (otherId.log_id == this.log_id) && (otherId.booking_id == this.booking_id);
}
return false;
}
You can read more about this option here

ConstraintViolationException thrown for Foreign Key

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.

Why hibernate runs delete and insert commands for embaddable objects

I am trying to create a simple example to understand how collection of basic and embaddable types works in Hibernate.
I have created a User entity with a set of nickNames and also a set of addresses. Here are my Java classes:
User.java
#Entity
#Table(name = "TB_User")
public class User {
#Id
#GeneratedValue
private int id;
private String name;
#ElementCollection
#CollectionTable(name = "Nicknames", joinColumns = #JoinColumn(name = "user_id"))
#Column(name = "nickname")
private Set<String> nickNames = new HashSet<String>();
#ElementCollection
#CollectionTable(name = "Addresses", joinColumns = #JoinColumn(name = "user_id"))
#AttributeOverrides({ #AttributeOverride(name = "street1", column = #Column(name = "fld_street")) })
public Set<Address> addresses = new HashSet<Address>();
public User() {
}
public User(String name, Address... addresses) {
this.name = name;
this.addresses.addAll(Arrays.asList(addresses));
}
public void addNickName(String... nickNames) {
this.nickNames.addAll(Arrays.asList(nickNames));
}
// Setters & Getters
}
Address.java
#Embeddable
public class Address {
private String street1;
public Address() {}
public Address(String street1) {
this.street1 = street1;
}
#Override
public String toString() {
return street1;
}
// Setters & Getters
}
Now I have created a simple program to create users in database and then show the list of users. Here is my code:
private static void saveUsers() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.getTransaction().begin();
User user = new User("User", new Address("abc"),
new Address("xyz"));
user1.addNickName("alpha", "beta");
session.save(user);
session.getTransaction().commit();
}
private static void showUsers() {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.getTransaction().begin();
List<User> users = session.createQuery("from User").list();
for (User user : users) {
System.out.println(user.getName() + " -- > " + user.getNickNames()
+ " --> " + user.getAddresses());
}
session.getTransaction().commit();
}
When I run this program, I observed that hibernate issues below set of commands:
Hibernate: select user0_.id as id1_2_, user0_.name as name2_2_ from TB_User user0_
Hibernate: select nicknames0_.user_id as user_id1_2_0_, nicknames0_.nickname as nickname2_1_0_ from Nicknames nicknames0_ where nicknames0_.user_id=?
Hibernate: select addresses0_.user_id as user_id1_2_0_, addresses0_.fld_street as fld_street2_0_0_ from Addresses addresses0_ where addresses0_.user_id=?
User -- > [alpha, beta] --> [xyz, abc]
Hibernate: delete from Addresses where user_id=?
Hibernate: insert into Addresses (user_id, fld_street) values (?, ?)
Hibernate: delete from Addresses where user_id=?
Hibernate: insert into Addresses (user_id, fld_street) values (?, ?)
If I try to get the list of addresses using user.getAddresses() with in session, then Hibernate deletes & re-inserts records in Addresses table.
Why hibernate tries to delete and re-create records in Addresses table, as this causes performance issue. Also why it is not applicable to basic types like nickNames in my example and not running update commands for the property nickNames?
This behaviour is related to the fact, that there is not bi-directional mapping. To understand that in depth - please read this article:
inverse = “true” example and explanation
And here is the way how to do that with annotation:
inverse=true in JPA annotations
Let me cite from the answer:
I found an answer to this. The mappedBy attribute of #OneToMany annotation behaves the same as inverse = true in the xml file.
Some summary:
Hibernate can issue SQL statements which we would expect. But only, if we do have bidirectional mapping in place.
Then, the other end (address) will be driving the persistence. It will/must know about its parent - and that's why some direct UPDATE statements could be issued.
So, if we want to avoid DELETE and INSERT, we have to use the inverse mapping. Hibernate will issue more "expectable" SQL.
This one is quite old, but might be relevant to sombody. Another tip that helped in my case: if you can separate reads from writes, consider using
#Transactional(readOnly = true)
That makes underlying JPA framework know that no modifications are expected in the current transaction. In practice, Hibernate no longer would run delete + insert when you merely want fetching some data.

Categories