-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 54 posts ]  Go to page Previous  1, 2, 3, 4  Next
Author Message
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Mon Oct 05, 2009 7:16 am 
Beginner
Beginner

Joined: Thu Dec 23, 2004 5:24 am
Posts: 40
Location: Luxembourg
juvegitau wrote:
hi
am trying to use the TriggerAssignedIdentityGenerator using anotation and am gettting the error below
plis help
[...] ... 86 more


I don't think the error is to do with the identity generator, because I can't find anything related to it in your stack trace. However, I don't have enough info here to be really sure.
So, please, can you provide:
- The code of the class com.nx.nxpos.model.AuditTrail (or its concrete implementation if it's an interface)
- The table definition for the storage of the AuditTrail entities
- The code of the trigger

Please, make sure the trigger is activated too.


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Thu Dec 17, 2009 5:08 am 
Newbie

Joined: Thu Dec 17, 2009 5:01 am
Posts: 2
Thank u ....

I have just modified the code to work with Before Insert Trigger and ID generator for BigDecimal.

Is the Right way of using it in Production.

Code:
package org.hibernate.id;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import oracle.jdbc.OraclePreparedStatement;
import oracle.jdbc.OracleTypes;

import org.apache.commons.lang.StringUtils;
import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.SequenceIdentityGenerator.NoCommentsInsert;
import org.hibernate.id.insert.AbstractReturningDelegate;
import org.hibernate.id.insert.IdentifierGeneratingInsert;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;

/**
* A generator with immediate retrieval through JDBC3
* {@link java.sql.Connection#prepareStatement(String)}. The value of the identity column must be
* set from a "before insert trigger" <p/> This generator only known to work with newer Oracle
* drivers compiled for JDK 1.4 (JDBC3). The minimum version is 10.2.0.1 <p/> Note: Due to a bug in
* Oracle drivers, sql comments on these insert statements are completely disabled.
*
* This class dos not use the method
* {@link java.sql.Connection#prepareStatement(String, String[]) getGeneratedKeys} because with this
* method the driver wants get the return type form the database meta. If the application user is
* not the shemaowner, this is not possible. This is the reason why the ReturnParameter is hard set
* to Long.
*
* @author Jean-Pol Landrain
* @author Beat Sager
*/
public class TriggerAssignedIdentityGenerator extends AbstractPostInsertGenerator {
    private static final String RETURN_PREFIX = " RETURNING ";
    private static final String RETURN_SUFFIX = " INTO ?";
    private static final String Q_MARK = "?";

    public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
            PostInsertIdentityPersister persister, Dialect dialect,
            boolean isGetGeneratedKeysEnabled) throws HibernateException {
        return new Delegate(persister, dialect);
    }

    public static class Delegate extends AbstractReturningDelegate {
        private final Dialect dialect;
        private final String sqlSuffix;

        public Delegate(PostInsertIdentityPersister persister, Dialect dialect) {
            super(persister);
            this.dialect = dialect;
            String[] keyColumns = getPersister().getRootTableKeyColumnNames();
            if (keyColumns.length > 1) {
                throw new HibernateException(
                        "trigger assigned identity generator cannot be used with multi-column keys");
            }
            sqlSuffix = new StringBuffer(26).append(RETURN_PREFIX).append(keyColumns[0]).append(
                    RETURN_SUFFIX).toString();
        }

        public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
            return new NoCommentsInsert(dialect);
        }

        protected PreparedStatement prepare(String insertSQL, SessionImplementor session)
                throws SQLException {
            insertSQL += sqlSuffix;
            OraclePreparedStatement stmt = (OraclePreparedStatement) session.getBatcher()
                    .prepareStatement(insertSQL);
            stmt.registerReturnParameter(StringUtils.countMatches(insertSQL, Q_MARK),
                    OracleTypes.NUMBER);
            return stmt;
        }

        protected Serializable executeAndExtract(PreparedStatement statement) throws SQLException {
     
            if (insert.executeUpdate() > 0) {
                ResultSet  resultSet = insert.getGeneratedKeys();
                if (resultSet.next()) {
                      return resultSet.getBigDecimal(1);
                }
            }
            throw new SQLException("Returned no id in resultset");
        }
    }
}


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Thu Dec 17, 2009 7:43 am 
Beginner
Beginner

Joined: Thu Dec 23, 2004 5:24 am
Posts: 40
Location: Luxembourg
devaraj wrote:
I have just modified the code to work with Before Insert Trigger and ID generator for BigDecimal.[...]

Devaraj, I can't tell this for sure because I'm not in the situation, but I'm not convinced the solution of Beat was the correct one for his schema owner issue (had he set the hibernate property "hibernate.default_schema" ?). I'm not an Oracle expert, but I can't see why he should be schema owner in order to access the resultset metadata ("if he can access, why can't he read the related metadata?"). Maybe it's the case, but really I don't get why it would be like that (but, once again, I'm not Oracle DBA).

I'm telling you this because if you were using the first solution instead, as it is a generic solution that is neither related to Oracle (it should work well with MySQL too), nor to the exact type of the returned column, you wouldn't have to modify the code for BigDecimal type.

So, did you give a try to the first solution (in the initial post) ? I would be glad to know.


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Thu Mar 25, 2010 9:17 am 
Newbie

Joined: Thu Mar 25, 2010 9:08 am
Posts: 1
I used the original TriggerAssignedIdentityGenerator posted in this thread. I ran into an ArrayOutOfBoundsException because of a bug in Oracle's JDBC drivers which is described here at StackOverflow

Code:
java.lang.ArrayIndexOutOfBoundsException: 15
at oracle.jdbc.driver.OracleSql.computeBasicInfo(OracleSql.java:950)
    at oracle.jdbc.driver.OracleSql.getSqlKind(OracleSql.java:623)
    at oracle.jdbc.driver.OraclePreparedStatement.(OraclePreparedStatement.java:1212)
    at oracle.jdbc.driver.T4CPreparedStatement.(T4CPreparedStatement.java:28)
    at oracle.jdbc.driver.T4CDriverExtension.allocatePreparedStatement(T4CDriverExtension.java:68)
    at oracle.jdbc.driver.PhysicalConnection.prepareStatement(PhysicalConnection.java:3059)
    at oracle.jdbc.driver.PhysicalConnection.prepareStatement(PhysicalConnection.java:2961)


Oracle has a patch available which will fix this problem. However, you will need access to Oracle's MetaLink to download the patch. (Patch #7112447) After applying the patch, the code worked great. Thanks!


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Thu Mar 25, 2010 10:09 am 
Beginner
Beginner

Joined: Thu Dec 23, 2004 5:24 am
Posts: 40
Location: Luxembourg
Hi Piepera.

I have checked on Metalink and this is fully relevant. For later reference, let me just add that:
- the problem only affects the queries that return an auto-generated key. Of course, it is the case with the TriggerAssignedIdentityGenerator.
- it affects versions 10.2.0.0 to 11.1.0.7 of Oracle. It is fixed in 11.2.x.x
- the patch is for the db server, not for the jdbc driver.

I have never encountered this problem but our applications are running on a fully patched db. However, thank you very much for sharing the info. I'm sure it can help other people.

[Once again I understand why Oracle has acquired Sun: they needed competent people to fix the defects in their jdbc implementation ;-))]


Top
 Profile  
 
 Post subject: Re:
PostPosted: Wed Jun 30, 2010 6:22 am 
Beginner
Beginner

Joined: Thu Dec 23, 2004 5:24 am
Posts: 40
Location: Luxembourg
landrain wrote:
[...]
I'm looking at a way to declare it globally now, so we don't have to repeat this specific annotation in every class where we want to use it:

Code:
@GenericGenerator(name="triggerAssigned",
  strategy = "jpl.hibernate.util.TriggerAssignedIdentityGenerator"
)


However I have found nothing so far. It seems it's just not possible. That was the same with plain hibernate however: you had to repeat the declaration of your custom id generator for every class. Global declaration of custom id generators would be a nice addition to Hibernate or JPA.


Now the @GenericGenerator and @GenericGenerators annotations can be used at package level.
See http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/#entity-hibspec-identifier for the details:

Quote:
2.4.2.1. Generators

@org.hibernate.annotations.GenericGenerator and @org.hibernate.annotations.GenericGenerators allows you to define an Hibernate specific id generator.

Code:
@Id @GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
public String getId() {

@Id @GeneratedValue(generator="hibseq")
@GenericGenerator(name="hibseq", strategy = "seqhilo",
    parameters = {
        @Parameter(name="max_lo", value = "5"),
        @Parameter(name="sequence", value="heybabyhey")
    }
)
public Integer getId() {
strategy is the short name of an Hibernate3 generator strategy or the fully qualified class name of an IdentifierGenerator implementation. You can add some parameters through the parameters attribute.

Contrary to their standard counterpart, @GenericGenerator and @GenericGenerators can be used in package level annotations, making them application level generators (just like if they were in a JPA XML file).

Code:
@GenericGenerators(
    {
    @GenericGenerator(
        name="hibseq",
        strategy = "seqhilo",
        parameters = {
            @Parameter(name="max_lo", value = "5"),
            @Parameter(name="sequence", value="heybabyhey")
        }
     ),
     @GenericGenerator(...)
     }
)
package org.hibernate.test.model


Last edited by landrain on Fri Jan 21, 2011 6:11 am, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Thu Jan 20, 2011 5:40 pm 
Newbie

Joined: Wed Aug 04, 2010 8:35 am
Posts: 5
Location: Washington DC
I'm trying to use this in my project. We are using Hibernate 3.6. When I try to compile landrain's TriggerAssignedIdentityGenerator class, it gives me compilation error that, it can't find org.hibernate.IdentifierGeneratorFactory and org.hibernate.util.NamedGeneratedKeysHelper. Looks like they have been removed from the 3.6 version of hibernate.

Does that mean hibernate itself has created a mechanism to refresh the ids generated by a trigger. If so can someone post an annotation sample how to do it. Also will it work if I try to persist a parent and child object with a single call. The ids for both parent & child are generated by triggers.

Appreciate response.


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Fri Jan 21, 2011 5:56 am 
Beginner
Beginner

Joined: Thu Dec 23, 2004 5:24 am
Posts: 40
Location: Luxembourg
tonyaugustine wrote:
I'm trying to use this in my project. We are using Hibernate 3.6. When I try to compile landrain's TriggerAssignedIdentityGenerator class, it gives me compilation error that, it can't find org.hibernate.IdentifierGeneratorFactory and org.hibernate.util.NamedGeneratedKeysHelper. Looks like they have been removed from the 3.6 version of hibernate.

Yes, the code must be updated for the newer versions of Hibernate (I believe since 3.5.0, but I haven't checked exactly).

Please, see hereafter for an update with hibernate 3.6.0.Final. One line has changed in the executeAndExtract method + some imports. The changes are marked with "del" and "add" tags:

Code:
package jpl.hibernate.util;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.hibernate.HibernateException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.AbstractPostInsertGenerator;
[del]import org.hibernate.id.IdentifierGeneratorFactory;[/del]
[add]import org.hibernate.id.IdentifierGeneratorHelper;[/add]
import org.hibernate.id.PostInsertIdentityPersister;
import org.hibernate.id.SequenceIdentityGenerator.NoCommentsInsert;
import org.hibernate.id.insert.AbstractReturningDelegate;
import org.hibernate.id.insert.IdentifierGeneratingInsert;
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
[del]import org.hibernate.util.NamedGeneratedKeysHelper;[/del]

/**
* A generator with immediate retrieval through JDBC3 {@link java.sql.Connection#prepareStatement(String, String[]) getGeneratedKeys}.
* The value of the identity column must be set from a "before insert trigger"
* <p/>
* This generator only known to work with newer Oracle drivers compiled for
* JDK 1.4 (JDBC3). The minimum version is 10.2.0.1
* <p/>
* Note: Due to a bug in Oracle drivers, sql comments on these insert statements
* are completely disabled.
*
* @author Jean-Pol Landrain
*/
public class TriggerAssignedIdentityGenerator extends AbstractPostInsertGenerator {

    public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(PostInsertIdentityPersister persister, Dialect dialect, boolean isGetGeneratedKeysEnabled) throws HibernateException {
        return new Delegate(persister, dialect);
    }

    public static class Delegate extends AbstractReturningDelegate {
        private final Dialect dialect;

        private final String[] keyColumns;

        public Delegate(PostInsertIdentityPersister persister, Dialect dialect) {
            super(persister);
            this.dialect = dialect;
            this.keyColumns = getPersister().getRootTableKeyColumnNames();
            if (keyColumns.length > 1) {
                throw new HibernateException("trigger assigned identity generator cannot be used with multi-column keys");
            }
        }

        public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
            NoCommentsInsert insert = new NoCommentsInsert(dialect);
            return insert;
        }

        protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException {
            return session.getBatcher().prepareStatement(insertSQL, keyColumns);
        }

        protected Serializable executeAndExtract(PreparedStatement insert) throws SQLException {
            insert.executeUpdate();
            [del]return IdentifierGeneratorFactory.getGeneratedIdentity(NamedGeneratedKeysHelper.getGeneratedKey(insert), getPersister().getIdentifierType());[/del]
            [add]return IdentifierGeneratorHelper.getGeneratedIdentity(insert.getGeneratedKeys(), getPersister().getIdentifierType());[/add]
        }
    }
}

tonyaugustine wrote:
Does that mean hibernate itself has created a mechanism to refresh the ids generated by a trigger. If so can someone post an annotation sample how to do it.

It exists a trigger-identity in NHibernate. But I am not aware of anything similar in Hibernate up to now.

tonyaugustine wrote:
Also will it work if I try to persist a parent and child object with a single call. The ids for both parent & child are generated by triggers.

It works if the two entities, parent and child, are declared with this identity generator (in case you're using annotations and you need this idgen on all or many of your entities, see two posts above in order to use the annotations at package level).


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Fri Jan 21, 2011 10:50 am 
Newbie

Joined: Wed Aug 04, 2010 8:35 am
Posts: 5
Location: Washington DC
Jean-pol

Thanks man. Works like a charm ! U da man.

Tony


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Tue Feb 08, 2011 3:50 pm 
Newbie

Joined: Wed Jan 05, 2011 3:22 pm
Posts: 13
This is probably what I am looking for for past 2 days. ! thanks for the post. But I have 2 questions

1) I was using HB 3.1.x version and it was working fine. I was using "identity" and it worked with SQL as well as Oracle (with trigger and seq. number ..same scenario as yours)
2) Since I have to support 2 Db (SQL, Oracle) ..do I have to change hbm file for each installation?


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Wed Feb 16, 2011 12:16 pm 
Beginner
Beginner

Joined: Thu Dec 23, 2004 5:24 am
Posts: 40
Location: Luxembourg
guru_i wrote:
This is probably what I am looking for for past 2 days. ! thanks for the post. But I have 2 questions

1) I was using HB 3.1.x version and it was working fine. I was using "identity" and it worked with SQL as well as Oracle (with trigger and seq. number ..same scenario as yours)
2) Since I have to support 2 Db (SQL, Oracle) ..do I have to change hbm file for each installation?


Sorry for replying so late. Could you be more precise in your 1st question:

> "I was using "identity" and it worked with SQL as well as Oracle"

Do you mean you were using the 'native' identity generator. When you say SQL, do you mean "MS SQL Server" ?

About your second question, it's really outside the scope of this thread, but there are known solutions:
- there are ways to externalize parts of xml files for different environments (unfortunately not in a portable way, because this is not in the JEE specificications). Can I suggest you have a look at external entities in XML?
- the hbm files don't need to be in your deployable artifacts (war, ear). There are ways to load them from directories on the server (both inside or outside the server classpath). As far as I know, Hibernate doesn't offer property placeholders like Spring does. So you can't modify the hbm files at runtime (you can however do it quite easily at build time if you use Ant, Maven, Graddle, ...).

I'm pretty sure it also exists other ways to do this. But I'm not a specialist on the subject.


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Thu Feb 17, 2011 10:01 am 
Newbie

Joined: Wed Jan 05, 2011 3:22 pm
Posts: 13
Thanks for replying

Yes I use IDENTITY columns for MS SQL 2005 and Triggers(before insert)/sequence for Oracle. And since I support 2 db we were happily using generator class "identity" and it all worked on both until I upgraded my Hibernate libraries to 3.5.6.

But now I have overpass that issue by using native class with parameter as sequence name. So Hibernate is smart enough to use sequence for Oracle using generator class parameter and use IDENTITY columns for SQL Server. :)

It took me a while to get the fact that I can pass sequence name to HB using native generator class.

Thanks for help though


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Thu Feb 17, 2011 10:37 am 
Beginner
Beginner

Joined: Thu Dec 23, 2004 5:24 am
Posts: 40
Location: Luxembourg
guru_i wrote:
Yes I use IDENTITY columns for MS SQL 2005 and Triggers(before insert)/sequence for Oracle. And since I support 2 db we were happily using generator class "identity"

I think I get it now: you mean the GenerationType.IDENTITY from JPA (?)

guru_i wrote:
It took me a while to get the fact that I can pass sequence name to HB using native generator class.

Does that mean you've solved your issue?


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Thu Feb 17, 2011 11:06 am 
Newbie

Joined: Wed Jan 05, 2011 3:22 pm
Posts: 13
Yes

What I have now in my HBM file is

<generator class="native">
<param name="sequence">seq_pcwcontact</param>
</generator>

which works for both of my DB (Oracle and MS SQL Server). Hibernate uses IDENTITY columns to generate the id in case of SQL Server and sequence number in Oracle. Sorry I didnt mention that I had to drop my triggers which do nothing but call the sequence

And my problem is resolved.


Top
 Profile  
 
 Post subject: Re: Before Insert Trigger and ID generator
PostPosted: Sat Jun 11, 2011 7:32 am 
Newbie

Joined: Sat Jun 11, 2011 7:28 am
Posts: 1
I add small fixes to quoted identifiers, because Oracle driver throws exception on quoted identifier with this generator.

Code:
-            return session.getBatcher().prepareStatement(insertSQL, keyColumns);
+            if (keyColumns.length != 1) {
+                throw new HibernateException("trigger assigned identity generator could't use entities with no-one key columns");
+            }
+            String verifiedId = keyColumns[0].replaceAll("\"", "");
+            return session.getBatcher().prepareStatement(insertSQL, new String[]{verifiedId});


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 54 posts ]  Go to page Previous  1, 2, 3, 4  Next

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.