diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index ebdaa3166b..30e74decb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -2704,14 +2704,23 @@ public abstract class AbstractEntityPersister Insert insert = identityDelegate.prepareIdentifierGeneratingInsert(); insert.setTableName( getTableName( 0 ) ); - // add normal properties + // add normal properties except lobs for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) { - if ( includeProperty[i] && isPropertyOfTable( i, 0 ) ) { + if ( includeProperty[i] && isPropertyOfTable( i, 0 ) && !lobProperties.contains( i ) ) { // this property belongs on the table and is to be inserted insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i], propertyColumnWriters[i] ); } } + // HHH-4635 & HHH-8103 + // Oracle expects all Lob properties to be last in inserts + // and updates. Insert them at the end. + for ( int i : lobProperties ) { + if ( includeProperty[i] && isPropertyOfTable( i, 0 ) ) { + insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i], propertyColumnWriters[i] ); + } + } + // add the discriminator addDiscriminatorToInsert( insert ); diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/Document.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/Document.java new file mode 100644 index 0000000000..8540e37e6b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/Document.java @@ -0,0 +1,99 @@ +package org.hibernate.test.annotations.lob; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Lob; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +public class Document implements Serializable { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) // Required to use SequenceIdentityGenerator in test case for HHH-8103. + private Long id; + + private Integer revision; + + @Lob + @Column(length = 5000) + private String fullText; + + @Column(length = 120) + private String shortDescription; + + public Document() { + } + + public Document(Integer revision, String shortDescription, String fullText) { + this.revision = revision; + this.shortDescription = shortDescription; + this.fullText = fullText; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof Document ) ) return false; + + Document document = (Document) o; + + if ( fullText != null ? !fullText.equals( document.fullText ) : document.fullText != null ) return false; + if ( id != null ? !id.equals( document.id ) : document.id != null ) return false; + if ( revision != null ? !revision.equals( document.revision ) : document.revision != null ) return false; + if ( shortDescription != null ? !shortDescription.equals( document.shortDescription ) : document.shortDescription != null ) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + ( revision != null ? revision.hashCode() : 0 ); + result = 31 * result + ( shortDescription != null ? shortDescription.hashCode() : 0 ); + result = 31 * result + ( fullText != null ? fullText.hashCode() : 0 ); + return result; + } + + @Override + public String toString() { + return "Document(id = " + id + ", revision = " + revision + ", shortDescription = " + + shortDescription + ", fullText = " + fullText + ")"; + } + + public String getFullText() { + return fullText; + } + + public void setFullText(String fullText) { + this.fullText = fullText; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getRevision() { + return revision; + } + + public void setRevision(Integer revision) { + this.revision = revision; + } + + public String getShortDescription() { + return shortDescription; + } + + public void setShortDescription(String shortDescription) { + this.shortDescription = shortDescription; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/LobWithSequenceIdentityGeneratorTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/LobWithSequenceIdentityGeneratorTest.java new file mode 100644 index 0000000000..06641cd262 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/LobWithSequenceIdentityGeneratorTest.java @@ -0,0 +1,79 @@ +package org.hibernate.test.annotations.lob; + +import org.junit.Assert; +import org.junit.Test; + +import org.hibernate.Session; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.dialect.Oracle8iDialect; +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@TestForIssue( jiraKey = "HHH-8103" ) +@RequiresDialect( Oracle8iDialect.class ) +public class LobWithSequenceIdentityGeneratorTest extends BaseCoreFunctionalTestCase { + @Override + protected void configure(Configuration configuration) { + configuration.setProperty( Environment.DIALECT, OracleSeqIdGenDialect.class.getName() ); + configuration.setProperty( Environment.USE_NEW_ID_GENERATOR_MAPPINGS, "false" ); + configuration.setProperty( Environment.USE_GET_GENERATED_KEYS, "true" ); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Document.class }; + } + + @Test + public void testLobInsertUpdateDeleteSelect() { + Session session = openSession(); + + // Insert + session.getTransaction().begin(); + Document document = new Document( 1, "HHH-8103", "Oracle expects all LOB properties to be last in INSERT and UPDATE statements." ); + session.persist( document ); + session.getTransaction().commit(); + + session.clear(); + + session.getTransaction().begin(); + Assert.assertEquals( document, session.get( Document.class, document.getId() ) ); + session.getTransaction().commit(); + + session.clear(); + + // Update + session.getTransaction().begin(); + document = (Document) session.get( Document.class, document.getId() ); + document.setFullText( "Correct!" ); + session.update( document ); + session.getTransaction().commit(); + + session.clear(); + + session.getTransaction().begin(); + Assert.assertEquals( document, session.get( Document.class, document.getId() ) ); + session.getTransaction().commit(); + + session.clear(); + + // Delete + session.getTransaction().begin(); + document = (Document) session.get( Document.class, document.getId() ); + session.delete( document ); + session.getTransaction().commit(); + + session.clear(); + + session.getTransaction().begin(); + Assert.assertNull( session.get( Document.class, document.getId() ) ); + session.getTransaction().commit(); + + session.close(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/OracleSeqIdGenDialect.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/OracleSeqIdGenDialect.java new file mode 100644 index 0000000000..7a7f703b8c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/lob/OracleSeqIdGenDialect.java @@ -0,0 +1,14 @@ +package org.hibernate.test.annotations.lob; + +import org.hibernate.dialect.Oracle10gDialect; +import org.hibernate.id.SequenceIdentityGenerator; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +public class OracleSeqIdGenDialect extends Oracle10gDialect { + @Override + public Class getNativeIdentifierGeneratorClass() { + return SequenceIdentityGenerator.class; + } +}