It's my first time using Hibernate and when I insert multiple rows in a loop hibernate prints out the following for each row being inserted
Hibernate: select nextval ('hibernate_sequence')
after it does that for all the data in the loop it starts to insert the data
Hibernate: insert into user_data (age, location, sent_count, user_id, username, id) values (?, ?, ?, ?, ?, ?)
Is this how hibernate always operates? would it not be possible for the database to take care of the sequence? I feel like this really slows down the process of inserting rows. I am using PostgreSQL for my database.
Here is my relevant code user_data
@Entity
@Table(name = "user_data")
public class UserData
{
@Id @GeneratedValue
@Column(name = "id")
private int id;
@Column(name = "user_id")
private String userid;
@Column(name = "username")
private String username;
@Column(name = "age")
private int age;
@Column(name = "location")
private int location;
@Column(name = "sent_count")
private int sentCount;
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getUserid()
{
return userid;
}
public void setUserid(String userid)
{
this.userid = userid;
}
public String getUsername()
{
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public int getLocation()
{
return location;
}
public void setLocation(int location)
{
this.location = location;
}
public int getSentCount()
{
return sentCount;
}
public void setSentCount(int sentCount)
{
this.sentCount = sentCount;
}
}
My Hibernate helper class
public class HibernateUtil
{
static SessionFactory sessionFactory;
static ServiceRegistry serviceRegistry;
static
{
try
{
Configuration configuration = new Configuration();
configuration.setProperty("hibernate.temp.use_jdbc_metadata_defaults", "false");
configuration.configure();
serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
}
catch (Throwable ex)
{
// Make sure you log the exception, as it might be swallowed
System.err.println("Initial SessionFactory creation failed." + ex);
closeSessionFactory();
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory()
{
return sessionFactory;
}
public static void closeSessionFactory()
{
sessionFactory.close();
}
}
and the relevant section which inserts the rows
Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
for (String memberId : memberIds)
{
System.out.println(memberId);
UserData user = new UserData();
user.setUserid(memberId);
session.save(user);
}
session.getTransaction().commit();
session.close();
HibernateUtil.closeSessionFactory();
When you call session.save()
, you aren't inserting anything to the database yet. You are just enrolling your object to the session cache, where Hibernate keeps track of all the objects that will eventually need to be inserted/updated in the database. But what Hibernate needs right away is the new object's identifier, so it can wire other objects which refer to it by foreign key.
By default Hibernate will use a simple-minded ID generator backed by a native DB sequence, obtaining an ID from that sequence for each saved object. This will work alright as long as you don't do batch inserts (insert a lot of stuff in the same transaction). Your for-loop does make it look like you're doing precisely that, though.
If you have a need for good performance while doing batch inserts, you'll need to take care of a number of issues (search around for it), but here I'll present the solution to your immediate question: optimized sequence generators. This is what I use:
@GenericGenerator(name = "optimized-sequence", strategy = "enhanced-sequence", parameters = {
@Parameter(name = "prefer_sequence_per_entity", value = "true"),
@Parameter(name = "optimizer", value = "hilo"),
@Parameter(name = "increment_size", value = "50") })
package org.example.myproject;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
The above must be saved in a file named package-info.java
. What it achieves is define a package-wide custom ID generator under the name optimized-sequence
. You can use it like this:
@Id @GeneratedValue(generator = "optimized-sequence") public long id;
and what this will buy you is a native sequence-backed ID generator, but one which needs to bump the DB sequence only once for every 50 (configurable) saved objects. It changes the semantics of the underlying sequence such that, for example, currval = 1
means that the current Hibernate session has reserved the ID range 1-50 for itself. Yes, this will create "holes" in your ID space, but with 264 to go around, you shouldn't start worrying about that very soon.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With