Hibernate putting restrictions on collection elements conflicts with fetch mode - java

I am using Hibernate 4.3.8 as ORM tool for our MySql database. I have a class to be mapped which is annotated as follows:
#Entity
#DynamicUpdate
#Table(name = "myclass")
public class MyClass {
#Id
#Column(name = "myClassId")
private String id;
#Column(name = "status")
private String status;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "myclass_children", joinColumns = #JoinColumn(name = "myClassId"))
#Column(name = "child")
#Fetch(FetchMode.JOIN)
#BatchSize(size = 100)
#Cascade(value = CascadeType.ALL)
private Set<String> children;
}
To perform read queries via Hibernate, I am asked to use Criteria API. I should mention at the beginning that using HQL or SQL are not options.
Using the following code performs exactly what I want to do: Performs a second select query to retrieve collection elements and returns exactly 20 MyClass objects.
public List<MyClass> listEntities() {
Session session = sessionFactory.openSession();
try {
Criteria criteria = session.createCriteria(MyClass.class);
criteria.setFetchMode("children", FetchMode.SELECT);
criteria.add(Restrictions.eq("status", "open"));
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
criteria.setMaxResults(20);
}
}
Here are the queries generated:
select
this.myClassId as myClassId_1_0_0,
this.status as status_2_0_0
from
myclass this
where
status = ?
limit ?
select
children0.myClassId as myClassId1_0_0,
children0.child as child2_0_0
from
myclass_children as children0_
where
children0_.myClassId in (
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
)
However, when I try to put a restriction on collection elements, hibernate performs a single join query. When number of rows (not distinct root entities) in the result set of this single query reaches to the limit, Hibernate returns the existing MyClass objects as result. If each MyClass objects as 2 children, 10 MyClass objects are returned.
public List<MyClass> listEntities() {
Session session = sessionFactory.openSession();
try {
Criteria criteria = session.createCriteria(MyClass.class);
criteria.setFetchMode("children", FetchMode.SELECT);
criteria.createCriteria("children", "ch", JoinType.LEFT_OUTER_JOIN);
criteria.add(Restrictions.eq("status", "open"));
criteria.add(Restrictions.in("ch.elements", Arrays.asList("child1", "child2"));
criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
criteria.setMaxResults(20);
}
}
Here is the generated query:
select
this.id as id1_0_0,
this.status as status2_0_0,
ch.child as child1_0_2
from
myclass this
left outer join
myclass_children ch1_
on this.myClassId = ch1_.myClassId
where
this.status = ? limit ?
What can I do to obtain 20 MyClass objects while adding restrictions on collection elements? Any suggestions & answers are welcome!
NOTE: #Fetch(FetchMode.JOIN) annotation is used for other code base (like selecting by id, etc.). It should does not have any effect on my question since I am setting FetchMode.SELECT for criteria object separately.

Related

Can't fetch join lazy initialized collections

I am unable to perform a simple fetchjoin because of MultipleBagFetchException.
#Entity
public class Person {
#OneToMany(mappedBy="person",fetch=FetchType.LAZY)
private List<Auto> autos;
}
#Entity
public class Auto {
#ManyToOne
#JoinColumn(name = "person_id", nullable = false)
private Person person;
#OneToMany(mappedBy="auto",fetch=FetchType.LAZY)
private List<Tool> tools;
}
#Entity
#Table(name="tool")
public class Tool {
#ManyToOne
#JoinColumn(name = "auto_id", nullable = false)
private Auto auto;
}
As you can see all of my associactions uses default fetchtype.
#Query("SELECT p FROM Person p JOIN FETCH p.autos a JOIN FETCH a.tools")
List<Person>findAll();
result:
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags: [com.example.entities.Person.autos, com.example.entities.Auto.tools]
I have read about this exceptions, but in those cases the reason for this exception was the usage of EAGER fetch type for collections. What about this? This the most simple Entity relation.
And on the top of that lets suppose we are not allowed to touch the Entities.
How to solve this only on the query side?
There is one way to avoid n+1 queries without touching the entities, only changing the query for findAll. We can write a wrapper function which will first load persons with autos and them fetch all tools in a single select.
PersonRepository
#Query("SELECT distinct p FROM Person p JOIN FETCH p.autos a")
List<Person> findAll();
Wrapper code
List<Person> persons = personRepository.findAll();
Session session = (Session) entityManager.getDelegate();
List<Auto> autos = new ArrayList<>();
for (Person person : persons) {
if(!CollectionUtils.isEmpty(person.getAutos())) {
autos.addAll(person.getAutos());
}
}
try{
autos = session.createQuery("select distinct a from Auto a Join fetch a.tools " +
" where a in :autos", Auto.class)
.setParameter("autos", autos)
.setHint(QueryHints.PASS_DISTINCT_THROUGH, false)
.getResultList();
} catch (Exception ex) {
ex.printStackTrace();
}
The first query will be:
SELECT DISTINCT
person0_.id AS id1_6_0_,
autos1_.id AS id1_0_1_,
person0_.name AS name2_6_0_,
autos1_.name AS name2_0_1_,
autos1_.person_id AS person_i3_0_1_,
autos1_.person_id AS person_i3_0_0__,
autos1_.id AS id1_0_0__
FROM
Person person0_
INNER JOIN
Auto autos1_
ON
person0_.id=autos1_.person_id
The second query generated will be :
SELECT
auto0_.id AS id1_0_0_,
tools1_.id AS id1_8_1_,
auto0_.name AS name2_0_0_,
auto0_.person_id AS person_i3_0_0_,
tools1_.auto_id AS auto_id3_8_1_,
tools1_.name AS name2_8_1_,
tools1_.auto_id AS auto_id3_8_0__,
tools1_.id AS id1_8_0__
FROM
Auto auto0_
INNER JOIN
Tool tools1_
ON
auto0_.id=tools1_.auto_id
WHERE
auto0_.id IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Other than this I believe our options are limited, we will have to change Tool entity FetchMode or add BatchSize for default FetchMode.SELECT in order to get Tools in a separate query.
#OneToMany(mappedBy = "auto", fetch = FetchType.LAZY)
#Fetch(FetchMode.SUBSELECT)
private List<Tool> tools;
The query will be
SELECT
tools0_.auto_id AS auto_id3_8_1_
, tools0_.id AS id1_8_1_
, tools0_.id AS id1_8_0_
, tools0_.auto_id AS auto_id3_8_0_
, tools0_.name AS name2_8_0_
FROM
Tool tools0_
WHERE
tools0_.auto_id IN
(
SELECT
autos1_.id
FROM
Person person0_
INNER JOIN
Auto autos1_
ON
person0_.id=autos1_.person_id
)

JPA ManyToMany persist

I have a NOTIFICATION table who contains one ManyToMany association :
#Entity
#Table(name="NOTIFICATION")
#NamedQuery(name="Notification.findAll", query="SELECT f FROM Notification f")
public class Notification {
/** SOME COLUMN DEFINITION NOT IMPORTANT FOR MY CASE
COD, DATE, ID_THEME, ID_TYP, IC_ARCH, ID_CLIENT, INFOS, NAME, TITRE_NOT, ID_NOT
**/
#ManyToMany
#JoinTable(
name="PJ_PAR_NOTIF"
, joinColumns={
#JoinColumn(name="ID_NOTIF")
}
, inverseJoinColumns={
#JoinColumn(name="ID_PJ_GEN")
}
)
private List<PiecesJointesGen> piecesJointesGens;
}
So, I have an association table called PJ_PAR_NOTIF.
I try to persist a Notification entity. Here is piecesJointesGens initialisation, from a Value Object :
#PersistenceContext(unitName="pu/middle")
private EntityManager entityMgr;
FoaNotification lFoaNotification = new FoaNotification();
for(PieceJointeGenVO lPJGenVO : pNotificationVO.getPiecesJointes()){
PiecesJointesGen lPiecesJointesGen = new PiecesJointesGen();
lPiecesJointesGen.setLienPjGen(lPJGenVO.getLienPieceJointeGen());
lPiecesJointesGen.setIdPjGen(lPJGenVO.getIdPieceJointeGen());
lNotification.getFoaPiecesJointesGens().add(lFoaPiecesJointesGen);
}
entityMgr.persist(pNotification);
The persist doesn't work. JPA generate a first insert for my Notification object, that is ok :
insert
into
NOTIFICATION
(COD, DATE, ID_THEME, ID_TYP, IC_ARCH, ID_CLIENT, INFOS, NAME, TITRE_NOT, ID_NOT)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
Then, JPA try to insert values in my association table, but piecesJointesGen doesn't exists for the moment :
insert
into
PJ_PAR_NOTIF
(ID_NOTIF, ID_PJ_GEN)
values
(?, ?)
So, I have this error :
GRAVE: EJB Exception: : java.lang.IllegalStateException: org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.entities.PiecesJointesGen
Is there a way to tell JPA to insert piecesJointesGen before the PJ_PAR_NOTIF insert ?
Modify piecesJointesGens mapping to #ManyToMany(cascade = CascadeType.PERSIST).

No matter what, I can't batch MySQL INSERT statements in Hibernate

I'm currently facing the well-known and common Hibernate insert batch problem.
I need to save batches 5 millions of rows long. I'm first trying with a much lighter payload. Since I have to insert entities of only 2 types (first all records of type A, then all records of type B, all pointing to common type C ManyToOne parent), I would like to take the most advantage from JDBC batch insert.
I have already read lots of documentation, but none that I have tried worked.
I know that in order to use batch inserts I must not use an entity generator. So I removed the AUTO_INCREMENT ID and I'm setting the ID with a trick: SELECT MAX(ID) FROM ENTITIES and increment every time.
I know that I must flush the session regularly. I'll post code ahead, but anyway I perform a transaction every 500 elements.
I know that I have to set hibernate.jdbc.batch_size consistent with my application's bulk size, so I set it in the LocalSessionFactoryBean (Spring ORM integration)
I know I have to enable rewriting batched statements in connection URL.
Here are my entities
Common parent entity. This gets inserted first in a single transaction. I don't care about auto increment column here. Only one record per batch job
#Entity
#Table(...)
#SequenceGenerator(...)
public class Deal
{
#Id
#Column(
name = "DEAL_ID",
nullable = false)
#GeneratedValue(
strategy = GenerationType.AUTO)
protected Long id;
................
}
One of the children (let's say 2.5M records per batch)
#Entity
#Table(
name = "TA_LOANS")
public class Loan
{
#Id
#Column(
name = "LOAN_ID",
nullable = false)
protected Long id;
#ManyToOne(
optional = false,
targetEntity = Deal.class,
fetch = FetchType.LAZY)
#JoinColumn(
name = "DEAL_ID",
nullable = false)
protected Deal deal;
.............
}
The other children type. Let's say the other 2.5M records
#Entity
#Table(
name = "TA_BONDS")
public class Bond
{
#Id
#Column(
name = "BOND_ID")
#ManyToOne(
fetch = FetchType.LAZY,
optional = false,
targetEntity = Deal.class)
#JoinColumn(
name = "DEAL_ID",
nullable = false,
updatable = false)
protected Deal deal;
}
Simplified code that inserts records
long loanIdCounter = loanDao.getMaxId(), bondIdCounter = bondDao.getMaxId(); //Perform SELECT MAX(ID)
Deal deal = null;
List<Bond> bondList = new ArrayList<Bond>(COMMIT_BATCH_SIZE); //500 constant value
List<Loan> loanList = new ArrayList<Loan>(COMMIT_BATCH_SIZE);
for (String msg: inputStreamReader)
{
log.debug(msg.toString());
if (this is a deal)
{
Deal deal = parseDeal(msg.getMessage());
deal = dealManager.persist(holder.deal); //Called in a separate transaction using Spring annotation #Transaction(REQUIRES_NEW)
}
else if (this is a loan)
{
Loan loan = parseLoan(msg.getMessage());
loan.setId(++loanIdCounter);
loan.setDeal(deal);
loanList.add(loan);
if (loanList.size() == COMMIT_BATCH_SIZE)
{
loanManager.bulkInsert(loanList); //Perform a bulk insert in a single transaction, not annotated but handled manually this time
loanList.clear();
}
}
else if (this is a bond)
{
Bond bond = parseBond(msg.getMessage());
bond.setId(++bondIdCounter);
bond.setDeal(deal);
bondList.add(bond);
if (bondList.size() == COMMIT_BATCH_SIZE) //As above
{
bondManager.bulkInsert(bondList);
bondList.clear();
}
}
}
if (!bondList.isEmpty())
bondManager.bulkInsert(bondList);
if (!loanList.isEmpty())
loanManager.bulkInsert(loanList);
//Flush remaining items, not important
Implementation of bulkInsert:
#Override
public void bulkInsert(Collection<Bond> bonds)
{
// StatelessSession session = sessionFactory.openStatelessSession();
Session session = sessionFactory.openSession();
try
{
Transaction t = session.beginTransaction();
try
{
for (Bond bond : bonds)
// session.persist(bond);
// session.insert(bond);
session.save(bond);
}
catch (RuntimeException ex)
{
t.rollback();
}
finally
{
t.commit();
}
}
finally
{
session.close();
}
}
As you can see from comments, I have tried several combinations of stateful/stateless session. None worked.
My dataSource is a ComboPooledDataSource with following URL
<b:property name="jdbcUrl" value="jdbc:mysql://server:3306/db?autoReconnect=true&rewriteBatchedStatements=true" />
My SessionFactory
<b:bean id="sessionFactory" class="class.that.extends.org.springframework.orm.hibernate3.LocalSessionFactoryBean" lazy-init="false" depends-on="dataSource">
<b:property name="dataSource" ref="phoenixDataSource" />
<b:property name="hibernateProperties">
<b:props>
<b:prop key="hibernate.dialect">${hibernate.dialect}</b:prop> <!-- MySQL5InnoDb-->
<b:prop key="hibernate.show_sql">${hibernate.showSQL}</b:prop>
<b:prop key="hibernate.jdbc.batch_size">500</b:prop>
<b:prop key="hibernate.jdbc.use_scrollable_resultset">false</b:prop>
<b:prop key="hibernate.cache.use_second_level_cache">false</b:prop>
<b:prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</b:prop>
<b:prop key="hibernate.cache.use_query_cache">false</b:prop>
<b:prop key="hibernate.validator.apply_to_ddl">false</b:prop>
<b:prop key="hibernate.validator.autoregister_listeners">false</b:prop>
<b:prop key="hibernate.order_inserts">true</b:prop>
<b:prop key="hibernate.order_updates">true</b:prop>
</b:props>
</b:property>
</b:bean>
Even if my project-wide class extends LocalSessionFactoryBean, it does not override its methods (only adds few project-wide methods)
I'm getting mad since a few days. I read a few articles and none helped me enable batch inserts. I run all of my code from JUnit tests instrumented with Spring context (so I can #Autowire my classes). All of my attempts only produce a lots of separate INSERT statements
https://stackoverflow.com/questions/12011343/how-do-you-enable-batch-inserts-in-hibernate
https://stackoverflow.com/questions/3469364/faster-way-to-batch-saves-with-hibernate
https://forum.hibernate.org/viewtopic.php?p=2374413
https://stackoverflow.com/questions/3026968/high-performance-hibernate-insert
What am I missing?
It's likely your queries are being rewritten but you wouldn't know if by looking at the Hibernate SQL logs. Hibernate does not rewrite the insert statements - the MySQL driver rewrites them. In other words, Hibernate will send multiple insert statements to the driver, and then the driver will rewrite them. So the Hibernate logs only show you what SQL Hibernate sent to the driver, not what SQL the driver sent to the database.
You can verify this by enabling MySQL's profileSQL parameter in connection url:
<b:property name="jdbcUrl" value="jdbc:mysql://server:3306/db?autoReconnect=true&rewriteBatchedStatements=true&profileSQL=true" />
Using an example similar to yours, this is what my output looks like:
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
insert into Person (firstName, lastName, id) values (?, ?, ?)
Wed Feb 05 13:29:52 MST 2014 INFO: Profiler Event: [QUERY] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) duration: 1 ms, connection-id: 81, statement-id: 33, resultset-id: 0, message: insert into Person (firstName, lastName, id) values ('person1', 'Name', 1),('person2', 'Name', 2),('person3', 'Name', 3),('person4', 'Name', 4),('person5', 'Name', 5),('person6', 'Name', 6),('person7', 'Name', 7),('person8', 'Name', 8),('person9', 'Name', 9),('person10', 'Name', 10)
The first 10 lines are being logged by Hibernate though this not what is actually being sent to MySQL database. The last line is coming from MySQL driver and it clearly shows a single batch insert with multiple values and that is what is actually being sent to the MySQL database.

JPA One to One entities - cannot insert

I have written an application that parses an xml document using jaxb and then inserts into a database using JPA.
I have three JPA entities.
1.ItemEntity
2.PromotionEntity
3.SellPriceEntity
The item entity has a one to one relationship with the PromotionEntity and a one to one relationship with the SellPrice Entitiy.
When try to insert into the db using only the ItemEntity my application works and the item record is inserted into the db. However when I try to insert into the db using the ItemEntity, PromotionEntity and the SellPriceEntity I start to get errors.
org.apache.openjpa.persistence.PersistenceException: Incorrect integer value: '\xAC\xED\x00\x05sr\x00,org.apache.camel.example.etl.PromotionEntity$\x0C\xF5\xF1\x08\x0B\xA2\x81\x02\x00\x05L\x00\x02idt\x00\x10' for column 'PROMOTION_ID' at row 1 {prepstmnt 1554452939 INSERT INTO item (id, ATTRIBUTE_1, ATTRIBUTE_3, ATTRIBUTE_2, BRAND_LOGO_FILE_NAME, BRAND_NAME, CLASS_NO, DEFAULT_MARGIN, DESCRIPTION, EXTENDED_DESCRIPTION, EXTENDED_DESCRIPTION_2, GST_CODE, IMAGE_FILE_NAME, ITEM_NO, OUT_OF_STOCK_IND, PACK_QTY, PROMOTION_ID, SELL_PRICE_ID, SELLING_UNIT, SIZE_APPLICABLE, STOCK_AVAILABLE, SPPLR_NO, VOLUME, WEB_AGE_GROUP, WEB_COLOR_DESCRIPTION, WEB_DESCRIPTION, WEB_SIZE_DESCRIPTION, WEIGHT) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)} [code=1366, state=HY000]
FailedObject: org.apache.camel.example.etl.ItemEntity#333f8b4c
Here is how I have set my entity beans up...
#Entity(name = "item")
public class ItemEntity implements java.io.Serializable {
private static final long serialVersionUID = -9063279672222036437L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "ITEM_NO")
private String itemNo;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="PROMOTION_ID")
private PromotionEntity promotion;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="SELL_PRICE_ID")
private SellPriceEntity sellPrice;
gets and sets...
Promotion Entity
#Entity(name = "promotion")
public class PromotionEntity implements java.io.Serializable {
private static final long serialVersionUID = 2597721500656837249L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "PROMOTION_ID")
private String promotionId;
#Column(name = "PROMOTION_PRICE")
private String promotionPrice;
gets and sets...
Sell Price Entity
#Entity(name = "sell_price")
public class SellPriceEntity implements java.io.Serializable {
private static final long serialVersionUID = -205334787672950850L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "SELL_PRICE_EFFECTIVE_DATE_1")
private String sellPriceEffectiveDateOne;
#Column(name = "SELL_PRICE_1")
private String sellPriceOne;
gets and sets...
I believe that I have defined the relationship fields properly so not sure what is going wrong. On trying to debug through the JDBCStoreManager class I can see that the sql being executed is....
com.mysql.jdbc.JDBC4PreparedStatement#6d66cc49:
INSERT INTO item (
id,
ATTRIBUTE_1,
ATTRIBUTE_3,
ATTRIBUTE_2,
BRAND_LOGO_FILE_NAME,
BRAND_NAME,
CLASS_NO,
DEFAULT_MARGIN,
DESCRIPTION,
EXTENDED_DESCRIPTION,
EXTENDED_DESCRIPTION_2,
GST_CODE, I
MAGE_FILE_NAME,
ITEM_NO,
OUT_OF_STOCK_IND,
PACK_QTY,
PROMOTION_ID,
SELL_PRICE_ID,
SELLING_UNIT,
SIZE_APPLICABLE,
STOCK_AVAILABLE,
SPPLR_NO,
VOLUME,
WEB_AGE_GROUP,
WEB_COLOR_DESCRIPTION,
WEB_DESCRIPTION,
WEB_SIZE_DESCRIPTION,
WEIGHT)
VALUES (701, '', '', '', '', '', '350', '.00', 'KHOMBU APFOOTA KOKO HIGH', '', '', '1', '', '93501250080', 'Y', '0', ** STREAM DATA **, ** STREAM DATA **, 'Each', 'Y', '0', 'KHOMBU', '.0000', '', 'Black', '', '8', '.00')
Where I should be inserting promotion_id and sell_price_id it is telling me ** STREAM DATA **. Is that right? Maybe this is why I'm getting a data type error.
thanks
It is not clear what you are after from the description, which also conflicts with the model you posted. A joincolumn is used to specify the foreign key field name as well as the primary key it references in the target entity. But JPA requires this to be the primary key in the referenced entity, so your mappings end up using "ITEM"."PROMOTION_ID" to reference "PROMOTION"."ID". You get the exception because on of the "PROMOTION_ID" fields doesn't exist on one of the tables - either the item or promotion tables.
Decide which table will have the foreign key to the parents table and go from there. I'm not sure it makes sense to have a single auto generated pk value reused for the 3 entities involved in the model shown, as it means the other two entitities can never exist with out the one who gets the id set. For example, there would be no way to create a new promotion and keep the old one around. That said, it would be possible in JPA 2.0 to set a relationship as the #ID. So you could remove the id from item like this:
#Column(name = "ITEM_NO")
private String itemNo;
#Id
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="PROMOTION_ID")
private PromotionEntity promotion;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name="SELL_PRICE_ID")
private SellPriceEntity sellPrice;
This would have item use the generated long id value from promotion as its id as well, and store it in the "PROMOTION_Id" field. You could do something similar to SellPriceEntity so it has a relationship to item or promotion that it could use to get its id set.

hibernate and oracle sequence GenericGenerator creates gap

I've mapped my class as follow (omitted other fields as only ID matters):
#Entity
#Table(name = "MODEL_GROUP")
#Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class SettlementModelGroup implements Serializable
{
#Id
#GeneratedValue(generator = "MODEL_GROUP_SEQ", strategy = GenerationType.SEQUENCE)
#GenericGenerator(name = "MODEL_GROUP_SEQ",
strategy = "sequence",
parameters = #Parameter(name = "sequence", value = "SEQ_MODEL_GROUP_MODEL_GROUP_ID"))
#Column(name = "MODEL_GROUP_ID", nullable = false)
private Integer modelId;
}
when I'm saving new object:
Integer modelGroupId = sessionFactory.getCurrentSession().save( modelGroup );
System.out.println( modelGroupId );
ID is set as for example 23, but when I look at the database it is actually 24. This is leading to many problems, as I'm using this ID later on. Any idea why it is making this gap?
SQL logs show that everything is fine (I thinks so):
Hibernate:
select
SEQ_MODEL_GROUP_MODEL_GROUP_ID.nextval
from
dual
Hibernate:
insert
into
MODEL_GROUP
(DOMAIN_ID, DESCRIPTION, NAME, PERIOD_TYPE_ID, MODEL_GROUP_TYPE_ID, STATUS_ID, OWNER_ID, MODEL_GROUP_ID)
values
(?, ?, ?, ?, ?, ?, ?, ?)
Trigger and Sequence:
CREATE SEQUENCE "SEQ_MODEL_GROUP_MODEL_GROUP_ID"
INCREMENT BY 1
START WITH 1
NOMAXVALUE
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER
;
CREATE OR REPLACE TRIGGER "TRG_MODEL_GROUP_MODEL_GROUP_ID"
BEFORE INSERT
ON "MODEL_GROUP"
FOR EACH ROW
WHEN (NEW."MODEL_GROUP_ID" is NULL)
BEGIN
SELECT "SEQ_MODEL_GROUP_MODEL_GROUP_ID".NEXTVAL
INTO :NEW."MODEL_GROUP_ID"
FROM DUAL;
END;
Apparently, when Hibernate ask your database for nextValue of ID, it fires also Trigger. So when I ask for ID, I've got number 23 but when actually saving to database by commiting transaction, it is increased again so I've got 24. Solution is described here:
HIbernate issue with Oracle Trigger for generating id from a sequence
To make it work correctly, I changed Trigger:
CREATE OR REPLACE TRIGGER "TRG_MODEL_GROUP_MODEL_GROUP_ID"
BEFORE INSERT
ON "MODEL_GROUP"
FOR EACH ROW
WHEN (NEW."MODEL_GROUP_ID" is NULL)
BEGIN
SELECT "SEQ_MODEL_GROUP_MODEL_GROUP_ID".NEXTVAL
INTO :NEW."MODEL_GROUP_ID"
FROM DUAL;
END;

Categories

Resources