1 2 Previous Next 22 Replies Latest reply on Apr 14, 2012 11:13 AM by xzeus

    Discrimator values in versioned entities causing problems

    talios

      Hey all, Adam,

      After realizing that we were still running our system with a slightly pre 1.0 SVN build I decided to update ourselves to 1.0, and also check out the new 1.1 beta release.

      However, I seem to have run into a few issues, one which looks to be my setup (need to modify our versions schema for collection versions, or disable that (as I current am)) and one which looks like its an actual bug.

      The problem I'm seeing is manifesting itself against an subclassed entity with a secondary table, and a discriminator column.

      The error seem is:

      02.10.2008 11:35:14.218 *ERROR* [btpool1-105] org.hibernate.util.JDBCExceptionReporter The column index is out of range: 5, number of columns: 4.
      


      Tracing this down to the SQL generated/used by hibernate I see:

      insert into smx3.contact_mech (info_string, contact_mech_type_id, contact_mech_id) values (?, 3001, ?)
      insert into smx3.postal_address (address1, address2, attn_name, city, country_geo_id, directions, postal_code, postal_code_geo_id, state_province_geo_id, to_name, contact_mech_id) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
      insert into smx3.contact_mech_versions (_revision_type, info_string, contact_mech_type_id, contact_mech_id, _revision) values (?, ?, 3001, ?, ?)
      


      Looking at the insert for contact_mech_versions, there's 5 columns, 4 variables, and one fixed value (the discriminator). When trying to trace back what envers was doing, I got confused and lost as the stacktrace I see:

      02.10.2008 11:35:14.219 *ERROR* [btpool1-105] org.hibernate.event.def.AbstractFlushingEventListener Could not synchronize database state with session org.hibernate.exception.DataException: could not insert: [smx3.schema3.PostalAddress_versions]
       at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:77)
       at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
       at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2272)
       at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2665)
       at org.hibernate.action.EntityInsertAction.execute(EntityInsertAction.java:60)
       at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
       at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
       at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:167)
       at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
       at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
       at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
       at org.jboss.envers.synchronization.VersionsSync.beforeCompletion(VersionsSync.java:147)
       at com.atomikos.icatch.jta.Sync2Sync.beforeCompletion(Sync2Sync.java:58)
       at com.atomikos.icatch.imp.TransactionStateHandler.commit(TransactionStateHandler.java:279)
       at com.atomikos.icatch.imp.CompositeTransactionImp.doCommit(CompositeTransactionImp.java:320)
      


      points to VersionsSync.beforeCompletion, line 147, but according to envers-1.0.0.ga's src file, VersionsSync is only 145 lines long (the version in trunk however matches up with the stacktrace).

      The parent class is defined as:

      @Entity
      @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
      @DiscriminatorColumn(name = "contact_mech_type_id", discriminatorType = DiscriminatorType.INTEGER)
      @Table(schema = "smx3", name = "contact_mech")
      @Versioned
      @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
      public abstract class ContactMech implements Identity<Integer> {
      


      with the child class:

      @Entity
      @DiscriminatorValue("3001")
      @SecondaryTable(schema = "smx3", name = "postal_address", pkJoinColumns = @PrimaryKeyJoinColumn(name = "contact_mech_id"))
      @Versioned
      @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
      public class PostalAddress extends ContactMech {
      


      Is envers properly attaching the @DiscriminatorColumn definition to its generated version entity? Does anyone have any pointers as to where/why/how/what could be causing this, or how I should go about further diagnosing it?

      Cheers,
      Mark

        • 1. Re: Discrimator values in versioned entities causing problem
          adamw

          Hello,

          so the 1.0.0 binaries don't match the sources?

          And this worked pre-1.0, but doesn't work now?

          I checked your mapping and it works for me. What kind of properties does PostalAddress contain? Any relations, components, UserTypes? Well, basically, did anything change except switching to a newer Envers version?

          The SQL looks fine ... there are 5 columns in the table, as you write, yes?

          Which DB are you using?

          Adam

          • 2. Re: Discrimator values in versioned entities causing problem
            talios

            Finally following up on this, the full PostalAddress class is:

            @Entity
            @DiscriminatorValue("3001")
            @SecondaryTable(schema = "smx3", name = "postal_address", pkJoinColumns = @PrimaryKeyJoinColumn(name = "contact_mech_id"))
            @Versioned
            @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
            public class PostalAddress extends ContactMech {
             private String toName;
            
             @Basic
             @Column(table = "postal_address", name = "to_name", nullable = true, insertable = true, updatable = true, length = 100)
             public String getToName() {
             return toName;
             }
            
             public void setToName(String toName) {
             this.toName = toName;
             }
            
             private String attnName;
            
             @Basic
             @Column(table = "postal_address", name = "attn_name", nullable = true, insertable = true, updatable = true, length = 100)
             public String getAttnName() {
             return attnName;
             }
            
             public void setAttnName(String attnName) {
             this.attnName = attnName;
             }
            
             private String address1;
            
             @Basic
             @Column(table = "postal_address", name = "address1", nullable = true, insertable = true, updatable = true)
             @NotNull(message = "{address1.required}")
             public String getAddress1() {
             return address1;
             }
            
             public void setAddress1(String address1) {
             this.address1 = address1;
             }
            
             private String address2;
            
             @Basic
             @Column(table = "postal_address", name = "address2", nullable = true, insertable = true, updatable = true)
             public String getAddress2() {
             return address2;
             }
            
             public void setAddress2(String address2) {
             this.address2 = address2;
             }
            
             private String directions;
            
             @Basic
             @Column(table = "postal_address", name = "directions", nullable = true, insertable = true, updatable = true)
             public String getDirections() {
             return directions;
             }
            
             public void setDirections(String directions) {
             this.directions = directions;
             }
            
             private String city;
            
             @Basic
             @Column(table = "postal_address", name = "city", nullable = true, insertable = true, updatable = true, length = 100)
             @NotNull(message = "{city.required}")
             public String getCity() {
             return city;
             }
            
             public void setCity(String city) {
             this.city = city;
             }
            
             private String postalCode;
            
             @Basic
             @Column(table = "postal_address", name = "postal_code", nullable = true, insertable = true, updatable = true, length = 60)
             public String getPostalCode() {
             return postalCode;
             }
            
             public void setPostalCode(String postalCode) {
             this.postalCode = postalCode;
             }
            
             @Override
             public boolean equals(Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
            
             PostalAddress that = (PostalAddress) o;
            
             if (address1 != null ? !address1.equals(that.address1) : that.address1 != null) return false;
             if (address2 != null ? !address2.equals(that.address2) : that.address2 != null) return false;
             if (attnName != null ? !attnName.equals(that.attnName) : that.attnName != null) return false;
             if (city != null ? !city.equals(that.city) : that.city != null) return false;
             if (directions != null ? !directions.equals(that.directions) : that.directions != null) return false;
             if (postalCode != null ? !postalCode.equals(that.postalCode) : that.postalCode != null) return false;
             if (toName != null ? !toName.equals(that.toName) : that.toName != null) return false;
            
             return true;
             }
            
             @Override
             public int hashCode() {
             int result;
             result = /*31 * result + */(toName != null ? toName.hashCode() : 0);
             result = 31 * result + (attnName != null ? attnName.hashCode() : 0);
             result = 31 * result + (address1 != null ? address1.hashCode() : 0);
             result = 31 * result + (address2 != null ? address2.hashCode() : 0);
             result = 31 * result + (directions != null ? directions.hashCode() : 0);
             result = 31 * result + (city != null ? city.hashCode() : 0);
             result = 31 * result + (postalCode != null ? postalCode.hashCode() : 0);
             return result;
             }
            
             private StateGeo regionGeo;
            
             @ManyToOne
             @JoinColumn(table = "postal_address", name = "state_province_geo_id", referencedColumnName = "geo_id", nullable = true, insertable = true, updatable = true)
             public StateGeo getRegionGeo() {
             return regionGeo;
             }
            
             public void setRegionGeo(StateGeo regionGeo) {
             this.regionGeo = regionGeo;
             }
            
             private PostalCodeGeo postalCodeGeo;
            
             @ManyToOne
             @JoinColumn(table = "postal_address", name = "postal_code_geo_id", referencedColumnName = "geo_id", nullable = true, insertable = true, updatable = true)
             public PostalCodeGeo getPostalCodeGeo() {
             return postalCodeGeo;
             }
            
             public void setPostalCodeGeo(PostalCodeGeo postalCodeGeo) {
             this.postalCodeGeo = postalCodeGeo;
             }
            
             private CountryGeo countryGeo;
            
             @ManyToOne
             @JoinColumn(table = "postal_address", name = "country_geo_id", referencedColumnName = "geo_id", nullable = true, insertable = true, updatable = true)
             public CountryGeo getCountryGeo() {
             return countryGeo;
             }
            
             public void setCountryGeo(CountryGeo countryGeo) {
             this.countryGeo = countryGeo;
             }
            }
            


            and its super class is:

            @Entity
            @Inheritance(strategy = InheritanceType.SINGLE_TABLE)
            @DiscriminatorColumn(name = "contact_mech_type_id", discriminatorType = DiscriminatorType.INTEGER)
            @Table(schema = "smx3", name = "contact_mech")
            @Versioned
            @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
            public abstract class ContactMech implements Identity<Integer> {
             private Integer id;
            
             @SequenceGenerator(
             name = "CONTACT_MECH_ID_SEQ",
             sequenceName = "smx3.contact_mech_id_seq",
             allocationSize = 20
             )
             @Id
             @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CONTACT_MECH_ID_SEQ")
             @Column(name = "contact_mech_id", nullable = false, insertable = true, updatable = true, length = 10)
             public Integer getId() {
             return id;
             }
            
             public void setId(Integer id) {
             this.id = id;
             }
            
             private String infoString;
            
             @Basic
             @Column(name = "info_string", nullable = true, insertable = true, updatable = true)
             public String getInfoString() {
             return infoString;
             }
            
             protected void setInfoString(String infoString) {
             this.infoString = infoString;
             }
            
             @Override
             public boolean equals(Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
            
             ContactMech that = (ContactMech) o;
            
             if (id != null ? !id.equals(that.id) : that.id != null)
             return false;
             if (infoString != null ? !infoString.equals(that.infoString) : that.infoString != null) return false;
            
             return true;
             }
            
             @Override
             public int hashCode() {
             int result;
             result = (id != null ? id.hashCode() : 0);
             result = 31 * result + (infoString != null ? infoString.hashCode() : 0);
             return result;
             }
            
            
             private ContactMechType contactMechType;
            
             @ManyToOne(fetch = FetchType.LAZY)
             @JoinColumn(name = "contact_mech_type_id", referencedColumnName = "contact_mech_type_id", nullable = false, insertable = false, updatable = false)
             public ContactMechType getContactMechType() {
             return contactMechType;
             }
            
             public void setContactMechType(ContactMechType contactMechType) {
             this.contactMechType = contactMechType;
             }
            
             private List<ContactMechAttribute> contactMechAttributes;
            
             @OneToMany(mappedBy = "contactMechByContactMechId")
             @Cache(usage = READ_WRITE)
             public List<ContactMechAttribute> getContactMechAttributes() {
             return contactMechAttributes;
             }
            
             public void setContactMechAttributes(List<ContactMechAttribute> contactMechAttributes) {
             this.contactMechAttributes = contactMechAttributes;
             }
            
             private List<ContactMechRelationship> relationshipsTo = new ArrayList<ContactMechRelationship>();
            
             @OneToMany(mappedBy = "to", cascade = {CascadeType.ALL})
             @Filter(name = "limitToCurrent")
             @Cache(usage = READ_WRITE)
             public List<ContactMechRelationship> getRelationshipsTo() {
             return relationshipsTo;
             }
            
             public void setRelationshipsTo(List<ContactMechRelationship> relationshipsTo) {
             this.relationshipsTo = relationshipsTo;
             }
            
             private List<ContactMechRelationship> relationshipsFrom = new ArrayList<ContactMechRelationship>();
            
             @OneToMany(mappedBy = "from", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
             @Filter(name = "limitToCurrent")
             @Cache(usage = READ_WRITE)
             public List<ContactMechRelationship> getRelationshipsFrom() {
             return relationshipsFrom;
             }
            
             public void setRelationshipsFrom(List<ContactMechRelationship> relationshipsFrom) {
             this.relationshipsFrom = relationshipsFrom;
             }
            
             private List<ContactMechStatus> contactMechStatuses;
            
             @OneToMany(mappedBy = "contactMech", fetch = FetchType.LAZY)
             @Cache(usage = READ_WRITE)
             public List<ContactMechStatus> getContactMechStatuses() {
             return contactMechStatuses;
             }
            
             public void setContactMechStatuses(List<ContactMechStatus> contactMechStatuses) {
             this.contactMechStatuses = contactMechStatuses;
             }
            
            
             @Transient
             public ContactMechStatus getCurrentStatus() {
             List<ContactMechStatus> status = getContactMechStatuses();
            
             return (status != null && status.size() > 0) ?
             status.get(status.size() - 1)
             : null;
             }
            
             private List<InvoiceContactMech> invoiceContactMechs;
            
             @OneToMany(mappedBy = "contactMechByContactMechId", fetch = FetchType.LAZY)
             @Cache(usage = READ_WRITE)
             public List<InvoiceContactMech> getInvoiceContactMechs() {
             return invoiceContactMechs;
             }
            
             public void setInvoiceContactMechs(List<InvoiceContactMech> invoiceContactMechs) {
             this.invoiceContactMechs = invoiceContactMechs;
             }
            
             private List<AgreementContactMech> agreementContactMechs = new ArrayList<AgreementContactMech>();
            
             @OneToMany(mappedBy = "contactMech", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
             @Cache(usage = READ_WRITE)
             public List<AgreementContactMech> getAgreementContactMechs() {
             return agreementContactMechs;
             }
            
             public void setAgreementContactMechs(List<AgreementContactMech> agreementContactMechs) {
             this.agreementContactMechs = agreementContactMechs;
             }
            
             private List<PartyContactMech> partyContactMechs;
            
             @OneToMany(mappedBy = "contactMech", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
             @Cache(usage = READ_WRITE)
             public List<PartyContactMech> getPartyContactMechs() {
             return partyContactMechs;
             }
            
             public void setPartyContactMechs(List<PartyContactMech> partyContactMechs) {
             this.partyContactMechs = partyContactMechs;
             }
            
             private List<PolicySetContactMech> policySetContactMechs;
            
             @OneToMany(mappedBy = "contactMech", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
             @Cache(usage = READ_WRITE)
             public List<PolicySetContactMech> getPolicySetContactMechs() {
             return policySetContactMechs;
             }
            
             public void setPolicySetContactMechs(List<PolicySetContactMech> policySetContactMechs) {
             this.policySetContactMechs = policySetContactMechs;
             }
            }
            


            The database is postgresql 8.3, the table DDL is:

            CREATE TABLE contact_mech (
             contact_mech_id integer NOT NULL,
             contact_mech_type_id integer NOT NULL,
             info_string character varying(255)
            );
            
            CREATE TABLE postal_address (
             contact_mech_id integer NOT NULL,
             to_name character varying(100),
             attn_name character varying(100),
             address1 character varying(255) NOT NULL,
             address2 character varying(255),
             directions character varying(255),
             city character varying(100),
             postal_code character varying(60),
             country_geo_id integer,
             state_province_geo_id integer,
             postal_code_geo_id integer
            );
            


            and my DDL for the relevant version tables is:

            CREATE TABLE smx3.revision_details (
             revision_id serial,
             timestamp bigint,
             rest_request_id integer,
             primary key (revision_id)
            );
            
            CREATE TABLE prototype_versions (
             _revision int8 not null,
             _revision_type integer
            );
            
            CREATE TABLE smx3.contact_mech_versions (
             LIKE prototype_versions,
             LIKE smx3.contact_mech,
             unique(contact_mech_id, _revision)
            );
            ALTER TABLE smx3.contact_mech_versions ALTER COLUMN contact_mech_id DROP NOT NULL;
            ALTER TABLE smx3.contact_mech_versions ALTER COLUMN contact_mech_type_id DROP NOT NULL;
            
            CREATE TABLE smx3.postal_address_versions (
             LIKE prototype_versions,
             LIKE smx3.postal_address,
             unique(contact_mech_id, _revision)
            );
            ALTER TABLE smx3.postal_address_versions ALTER COLUMN contact_mech_id DROP NOT NULL;
            
            


            Hopefully there's something in here that helps identify the problem, I'll try and build a simple test case/sample app that can reproduce the problem as well.


            • 3. Re: Discrimator values in versioned entities causing problem
              adamw

              I just tried persisting a single PostalAddress entity, using PostgreSQL 8.4.3, Hibernate 3.2.6, Envers 1.1.0.ga and didn't get any exceptions (of course I had to cut out all of the additional relations from the entities). Maybe the test case will clarify things :)

              --
              Adam

              • 4. Re: Discrimator values in versioned entities causing problem
                talios

                Ok, so I now have a test case which reproduces this. You can download my standalone app from:

                http://files.blog-city.com/files/aa/16275/b/enverstestcase.tar.gz

                The testcase configures a setup similar to our deployment (minus OSGi) including: Carol as a JNDI provider, Atomikos 3.4 as a JTA transaction manager/datasource.

                Inside the pom.xml theres a dependency declaration for 3 versions of envers (commented out) which are included in the ./lib directory - the original envers preview build we're using, 1.0.0 (which shows the problem) and the new 1.1.0 which seems to be for Hibernate 3.3 and not 3.2 :(

                Under the preview version, I see the following SQL being executed when creating the contact_mech entry for PostalAddress:

                2824 [main] DEBUG org.hibernate.SQL - insert into contact_mech_versions (_revision_type, contact_mech_id, _revision) values (?, ?, ?)
                Hibernate: insert into contact_mech_versions (_revision_type, contact_mech_id, _revision) values (?, ?, ?)
                2824 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - preparing statement
                


                We can see three parameters, which is missing the contact_mech_type_id discriminator column, and the info_string column, simply changing the dependency to use 1.0.0 however, generates the following SQL:

                3374 [main] DEBUG org.hibernate.util.JDBCExceptionReporter - could not insert: [entity.PostalAddress_versions] [insert into contact_mech_versions (_revision_type, info_string, contact_mech_type_id, contact_mech_id, _revision) values (?, ?, 3001, ?, ?)]
                org.postgresql.util.PSQLException: The column index is out of range: 5, number of columns: 4.
                


                In this run, we see the discriminator column and value and the info_string column, but it looks like somethings trying to set 5 parameters instead of 4 (as the discriminator is hardcoded).


                In addition, this code demonstrates (almost) demonstrates the validation issue I was having before as well. When hibernate creates a Party->PartyContactMech->ContactMech object tree, and the ContactMech fails validation - envers has already been told to version the Party/PartyContactMech's that will be rolled back. I don't think we can get around this (given they all get rolled back its not a problem) but I'm still unsure how I was getting the actual problem I saw.




                • 5. Re: Discrimator values in versioned entities causing problem
                  adamw

                  Thanks, the test case helped a lot :)

                  As we talked on IRC, the bug was with support of non-insertable columns. That's now fixed in trunk. JIRA bug: https://jira.jboss.org/jira/browse/ENVERS-64

                  --
                  Adam

                  • 6. Re: Discrimator values in versioned entities causing problem
                    talios

                    Hrm - I just compiled up the latest trunk and see that it works as expected when using my test case code, however - plugging it into my real app and I'm still seeing the original problem - so now I'm trying to figure out what else is different.

                    • 7. Re: Discrimator values in versioned entities causing problem
                      talios

                      Adam - I managed to reproduce (again) the problem and have uploaded a new test case project to ENVERS-64 - the problem seems to lie when having a discriminator column which is also a ManyToOne entity join.

                      • 8. Re: Discrimator values in versioned entities causing problem
                        adamw

                        Ok, thanks, I'll take a look at it towards the end of the week.

                        --
                        Adam

                        • 9. Re: Discrimator values in versioned entities causing problem
                          talios

                          Adam - by the looks of it this problem has regressed and returned during the move to hibernate core.

                          Any change of looking at this? Is the pre-hibernate code/repository still available at all? I was thinking of trying to find what patches you applied to get this working and see if I can patch them back in - from memory it was something to do with the discriminator columns not being declared to hibernate as non-insertable or something?

                          • 10. Re: Discrimator values in versioned entities causing problem
                            talios

                            Interesting - I just added some discriminator based tests to the current hibernate/envers tree and everything worked fine - this led me to tracing any differences between $work projects code and the tests...

                            In our classes, due to legacy code which was converted to hibernate, the column we declared as the discriminator column, we also had mapped as a ManyToOne member which the database contained metadata for the type.

                            Removing that ManyToOne field solved the problem with envers.

                            • 11. Re: Discrimator values in versioned entities causing problem
                              val94

                              hello talios,
                              I had a similary problem perhaps it's linked with your problem.

                              i use a class with inheritance and discriminator values, I had the same problems about numbers columns.
                              i don't use manytoone in the discriminator column.

                              i don't fix this problems but my root class used Embedded+ManytoOne;

                              i have duplicate all manytoone properties in the root class and the number of column is now correct.

                              i use the lastest trunk on svn.

                              valéry.

                              • 12. Re: Discrimator values in versioned entities causing problem
                                adamw

                                Hello,

                                I knew there was a second bug opened by you! :)
                                And it deals with exactly the thing you are talking about: discriminator column as a many-to-one relation:
                                http://opensource.atlassian.com/projects/hibernate/browse/HHH-3573

                                --
                                Adam

                                • 13. Re: Discrimator values in versioned entities causing problem
                                  talios

                                  Hi Adam, just back from my holidays now. Did the test case I attached to the ticket help at all?

                                  • 14. Re: Discrimator values in versioned entities causing problem
                                    adamw

                                    Hello,

                                    yes, thanks a lot! :) The bug should be fixed now.

                                    --
                                    Adam

                                    1 2 Previous Next