The following is my code for batch inserting to MySQL remote database.
Session session = db.setSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<e.getContents().size(); i++ ) {
Content content = e.getContents().get(i);
session.save(content);
if ( i % 40 == 0 ) {
session.flush();
session.clear();
}
}
tx.commit();
The mapping is defined in the following way:
class name="Database.Content" table="..." schema="" catalog="...">
<id name="id">
<column name="id" sql-type="int" not-null="true"/>
<generator class="identity"/>
</id>
<property name="week">
<column name="week" sql-type="int"/>
</property>
<property name="type">
<column name="type" sql-type="int" not-null="true"/>
</property>
<many-to-one name="group" class="Database.Group">
<column name="`group`"/>
</many-to-one>
<many-to-one name="table" class="Database.Table">
<column name="`table`" not-null="true"/>
</many-to-one>
</class>
I also set up some properties in hibernate.cfg.xml:
<property name="hibernate.jdbc.batch_size">40</property>
<property name="hibernate.cache.use_second_level_cache">false</property>
Unfortunately, the insertion of 150 rows takes about 30 seconds, which is very slow. I have read that setting generator=identity may disable batch inserting completely. However, if I remove the generator line, I end up with getting Duplicate key error. I wonder if I could send null as my id, so MySQL will do the job.
What is the best way to optimise the query? Thanks.
As answered in this SO question identity will indeed not work for batch. For MySQL you end up either generating ids in an application (f.e. with generator assigned or uuid - Hibernate-specific) or employing JPA-compliant table generator.
Example of using table generator:
<table-generator name="EMP_GEN"
table="GENERATOR_TABLE"
pk-column-name="key"
value-column-name="hi"
pk-column-value="EMP"
allocation-size="20"/>
See details on http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#mapping-declaration-id. There is also a description of what parameter mean on Java EE page.
Related
I have 2 POJO, Event and OrganizerProfile. Their relationship are many to one.
If I retrieve the Event from database, the OrganizerProfile is showing as empty from the debugger instead of what it should be. Also, I have to leave the hibernate session open to call the event.getOrganizerProfile.
If I get the Event, close the hibernate session, then the OrganizerProfile in the Event cannot be retrieved.
new EventDTO(this.getEvtByDateAddress(_event.getDate(), _event.getAddress(), /*dont close sess*/false));
Can you please explain this?
Thanks
<hibernate-mapping package="com.example.client.serializable">
<class name="Event" table="event">
<id name="oid" type="long" column="oid">
<generator class="increment">
<param name="initial_value">1</param>
</generator>
</id>
<property name="evtName">
<column name="evtName"/>
</property>
<property name="address">
<column name="address"/>
</property>
<property name="date" type="date">
<column name="date"/>
</property>
<many-to-one name="organizerProfile" cascade="all"></many-to-one>
</class>
</hibernate-mapping>
<hibernate-mapping package="com.example.client.serializable">
<class name="OrganizerProfile" table="organizerprofile">
<id column="oid" name="oid" type="long">
<generator class="increment">
<param name="initial_value">1</param>
</generator>
</id>
<property generated="never" lazy="false" name="acctOid">
<column name="acctOid"/>
</property>
<property generated="never" lazy="false" name="email">
<column name="email"/>
</property>
<property generated="never" lazy="false" name="name">
<column name="name"/>
</property>
<property generated="never" lazy="false" name="contact">
<column length="5120" name="contact"/>
</property>
<property name="profilePicName">
<column name="profilePicName"/>
</property>
</class>
</hibernate-mapping>
public Event getEvtByDateAddress(Date _date, String _address, boolean _closeSess)
{
try
{
if(!session.isOpen())
{
session = HibernateUtil.getSessionFactory().openSession();
}
session.beginTransaction();
Criteria criteria = session.createCriteria(Event.class);
criteria.add(Restrictions.eq("date", _date));
criteria.add(Restrictions.eq("address", _address));
Event evt = (Event)criteria.uniqueResult();
if(_closeSess)
{
session.close();
}
if (evt==null)
{
LogUtils.logInfo("The event does not exist: " + _date + " " + _address);
return null;
}
else
{
return evt;
}
}
catch(Exception e)
{
LogUtils.logInfo(e.toString());
if(_closeSess)
{
session.close();
}
return null;
}
}
public EventDTO(Event _event)
{
this.oid=_event.getOid();
this.evtName=_event.getEvtName();
this.address=_event.getAddress();
this.date=_event.getDate();
this.evtPicName=_event.getEvtPicName();
this.organizerProfile=new OrganizerProfileDTO(_event.getOrganizerProfile());
}
<many-to-one name="organizerProfile" cascade="all"></many-to-one>
Since you did not specify the property lazy in your many-to-one mapping, the associated entities will be proxied (see hibernate mapping documentation point 12) thus your related entity is not fetched and cannot be accessed outside of the session, inside of the session you can access it, because hibernate will automatically fetch it when you try to access it.
If you want to access the related entity outside of the session you have to fetch it manually, let hibernate initialize it - both within the session - or you can set your fetch type to eager ( <many-to-one name="organizerProfile" cascade="all" lazy="false"></many-to-one>) which I would NOT recommend (N+1 selects Problem).
Example of manual fetching with your code:
Criteria criteria = session.createCriteria(Event.class);
criteria.setFetchMode("organizerProfile", FetchMode.JOIN);
criteria.add(Restrictions.eq("date", _date));
criteria.add(Restrictions.eq("address", _address));
Also possibly helpful to read: Hibernate 4.3 docs #20: Performance fetching
everyone. I'm brand new using Hibernate.
So here I face a question, I have an entity like below:
<class name="cn.edu.scau.librarica.dao.MessageSession" table="msg_session">
<id name="msid" type="long" unsaved-value="null">
<generator class="identity"/>
</id>
<list name="msgs" cascade="all">
<key column="msid"
update="false" unique="true" not-null="true"/>
<list-index column="list_index"/>
<one-to-many class="Message" />
</list>
</class>
And now what I want to achieve is:
select Message m where msid=# and m.t<## and m.t>###
How can I represent it with Criteria?
Thank you for your attentions and advices, in advance.
UPDATE
As one of the answer guide, I have got the point that composite-element is not queryable, so I made a both-sid one-to-many map(changed are above)
And now I can query but now I found another problem:
How can I mapped the composite-id with foreign-key
Message are map like this:
<class name="Message">
<composite-id>
<generator class="foreign">
<!-- What here??? -->
</generator>
</composite-id>
</class>
As instructed, one-to-many may use set rather than list, so I can hardly find sample meets my need(due to Message should be ordered).
Any advice? I am still searching for that.
Thanks for help.
You can't directly select the Message as it is a component and not an entity. Components don't have an independent life cycle. They cannot be queried, created or deleted on their own, they always have to be accessed via the entity in which they are embedded (MessageSession in your case).
To make it into an Entity the table should have it's own primary key. In your case that doesn't seem to be so. You will need to change the schema so that the table corresponding to Message has a primary key and change the mapping to use one-to-many instead of composite-element.
If you can't do that you will have to query the MessageSession and get message out of it.
P.S: Hibernate is quite complex, I haven't see too many people being able to just pick it up along the way just trying things out. You are more likely to succeed if you spend some time studying the underlying concepts (which are more important than just mapping & querying).
here is the author of question.
After several days of research, I found the way to satisfied my require somehow.
Maping Message
<class name="cn.edu.scau.librarica.dao.Message" table="message">
<id name="id" type="long">
<generator class="identity" />
</id>
<many-to-one name="ms" class="cn.edu.scau.librarica.dao.MessageSession"
column="msid" not-null="true" insert="false" update="false" />
<property name="s" type="long" />
<property name="t" type="timestamp" />
<property name="m" type="string" />
</class>
Mapping MessageSession
<class name="cn.edu.scau.librarica.dao.MessageSession" table="msg_session">
<id name="msid" type="long" unsaved-value="null">
<generator class="identity"/>
</id>
<property name="latest" type="timestamp" />
<list name="msgs" table="msg_session_msgs" cascade="all">
<key column="msid"
update="false" not-null="true"/>
<list-index column="list_index"/>
<one-to-many class="cn.edu.scau.librarica.dao.Message" />
</list>
</class>
Among them, the unimportant are neglected.
Things go quite well while I can find the "one" with restrictions of "many" or inversely. just like this:
DetachedCriteria dc = DetachedCriteria.forClass(Message.class)
.createCriteria("ms")
.add(Restrictions.eq("msid", msid));
if (after != null)
dc.add(Restrictions.gt("t", after));
if (before != null)
dc.add(Restrictions.lt("t", before));
...can search out Message during the specified period from specified MessageSession.
So the trick is objects can't be returned by Criteria unless match them as entity.
On the underlaying database, it will be a little redundant, since Message can be uniquely identified by (msid,list_index), so I am finding a way to match <-this as the primary key for Message.
I have an hbm file which is as below:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping auto-import="true" default-lazy="false">
<class name="com.saman.entity.hibernate.EmployeeEntity"
table="Employee" optimistic-lock="version">
<id name="id">
<column name="Id" sql-type="bigint"/>
<generator class="native"/>
</id>
<timestamp name="version" source="db"/>
<property name="firstName">
<column name="FirstName" sql-type="nvarchar(300)"/>
</property>
<property name="lastName">
<column name="LastName" sql-type="nvarchar(300)"/>
</property>
<property name="employeeType">
<column name="EmployeeType" sql-type="nvarchar(300)"/>
</property>
<set name="shifts" table="Shifts" inverse="true" lazy="true" fetch="select">
<key>
<column name="Id" not-null="true"/>
</key>
<one-to-many class="com.saman.entity.hibernate.ShiftEntity"/>
</set>
</class>
</hibernate-mapping>
now I wanted if I add an employee and persist it, if then I add another employee with the previous information, my system raises an exception and tell me that I have another employee in the database with that informations.
does hibernate give me this option?
Well just add this to your mapping
<properties name="firstName_lastName_unique" unique="true">
<property name="firstName"/>
<property name="lastName"/>
</properties>
I think I understand what you want to achieve. But I don't know if you search your problem in stackoverflow first. This might be your answer How to do multiple column UniqueConstraint in hbm?
Have you set an auto increment on the ID column in your database?
You already have a generator for the id value. This should generate a unique id, but it only does so if these two conditions are true:
The column either is defined as autoincrement (p. ex. MySQL) or has a sequence (p. ex. Oracle)
When saving a new row, the member variable id is set to 0.
I can imagine, when you save a new value with previous information, the variable id still has a value != 0, and in this case the database uses the given value instead of generating a new unique one, which will fail due to the unique constraint.
This error also can appear if there is a second unique index on the table.
I've got an optional many-to-one relationship between two classes. Hibernate translates the property to be optional by setting the foreign keys to null.
My db-schema does not allow the columns to be null. The property to be optional is represented by the default-value of these columns.
<class name="sth.Alpha" ...>
....
<many-to-one name="beta" not-found="ignore" class="sth.Beta" insert="true" update="true">
<column name="a1/>
<column name="a2/>
</many-to-one>
</class>
<class name="sth.Alpha" ...>
<composite-id>
<key-property name="b1" type="int">
<column name="b1" precision="8" scale="0"/>
</key-property>
<key-property name="b2" type="int">
<column name="b2" precision="8" scale="0"/>
</key-property>
</composite-id>
</class>
selecting data is no problem because of not-found="ignore" in the may-to-one-tag it will result in a null-beta-object. But if I want to insert an Alpha? with beta set to null. I get an Exception, that it is not possible to insert null to a1 and a2.
I get rid of that problem if I set insert and update to false. But this results in not saving the relationship if it is set.
Database-Schema cannot be changed and Hibernate-version is fixed to 3.5
I would also be happy if you tell me, that it is not possible
how to use 0 instead of null in conjunction with <id unsavedvalue="whatever"> might help
or
other solution
My Hibernate hbm file looks something like this with a mysql DB:
<hibernate-mapping>
<class name="com.company.common.bo.position.Parent" table="Parents"
abstract="true">
<id name="id">
<generator class="increment" />
</id>
<property name="date" not-null="true" />
<property name="milliseconds" not-null="true" />
<property name="shares">
<column name="shares" precision="19" scale="6" not-null="true" />
</property>
<many-to-one name="ticker" column="tickerID" not-null="true" index="_tickerID_date_milliseconds_idx" />
<many-to-one name="auditTrail" column="auditTrailID"
not-null="false" cascade="save-update" lazy="false" fetch="select" />
<union-subclass name="com.company.common.bo.position.SubclassA"
table="SubclassAs">
<many-to-one name="account" column="accountID" not-null="true" foreign-key="SubclassA_accountID_fk" />
<many-to-one name="portfolio">
<column name="portfolioID" not-null="true"/>
</many-to-one>
<many-to-one name="individualTrade">
<column name="individualTradeID" not-null="false"/>
</many-to-one>
<many-to-one name="positionTransfer" column="positionTransferID"
cascade="save-update" not-null="false"/>
</union-subclass>
<union-subclass
name="com.company.common.bo.position.SubclassB" table="SubclassBs">
<many-to-one name="individualTrade">
<column name="individualTradeID" not-null="false" />
</many-to-one>
<many-to-one name="account" column="accountID" not-null="true" foreign-key="SubclassBs_accountID_fk"/>
<many-to-one name="internalExecution" column="executionID"
cascade="save-update" not-null="false" />
</union-subclass>
<union-subclass name="com.company.common.bo.position.SubclassC"
table="SubclassCs">
</union-subclass>
</class>
So basically i have an abstract class Parent and 3 subclasses (SubclassA, B, C) that extend it. In the database there are 3 tables (for the 3 subclasses). The id generator is "increment" because the union subclass mapping doesn't allow me to use native. So it looks like with increment, the ID is unique among the 3 tables. When I look at the hibernate sql, it basically finds the max ID from all 3 tables, and uses that as the next ID. But the query it uses seems very inefficient. This is what I see it doing:
select max(ids_.id) from ( select id from SubclassAs union select id from SubclassBs union select id from SubclassCs ) ids_
Which takes over 12 seconds to run. Each of those tables has more than a million records each. It's unioning every single ID together and then selecting the max out of that.
If i do something like this:
select max(ids_.id) from ( select max(id) as id from SubclassAs union select max(id) as id from SubclassBs union select max(id) as id from SubclassCs ) ids_
It is much faster, less than one millisecond, because the inner union only gets the max from each table, and then i select just the max out of those 3 records.
Is there a way to tell hibernate to do this instead, or is there a better way of using a generator for the ID across these 3 tables?
Thanks
If increment doesn't satisfy you, you can use some other generator strategy, and, since MySQL doesn't support sequences, the next suitable option is a hilo strategy.