Hibernate SVN: r18227 - core/trunk/annotations/src/main/resources/org/hibernate/ejb.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2009-12-15 12:15:33 -0500 (Tue, 15 Dec 2009)
New Revision: 18227
Added:
core/trunk/annotations/src/main/resources/org/hibernate/ejb/orm_2_0.xsd
Log:
HHH-4692 add orm_2_0.xml
Added: core/trunk/annotations/src/main/resources/org/hibernate/ejb/orm_2_0.xsd
===================================================================
--- core/trunk/annotations/src/main/resources/org/hibernate/ejb/orm_2_0.xsd (rev 0)
+++ core/trunk/annotations/src/main/resources/org/hibernate/ejb/orm_2_0.xsd 2009-12-15 17:15:33 UTC (rev 18227)
@@ -0,0 +1,1434 @@
+<?xml version="1.0" encoding="UTF-8"?> <!-- Java Persistence API object/relational mapping file schema -->
+<xsd:schema targetNamespace="http://java.sun.com/xml/ns/persistence/orm"
+ xmlns:orm="http://java.sun.com/xml/ns/persistence/orm"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified"
+ version="2.0">
+ <xsd:annotation>
+ <xsd:documentation>
+ @(#)orm_2_0.xsd 2.0 October 1 2009
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:annotation>
+ <xsd:documentation><![CDATA[
+This is the XML Schema for the persistence object/relational mapping file. The file may be named "META-INF/orm.xml" in the persistence archive or it may be named some other name which would be used to locate the file as resource on the classpath.
+Object/relational mapping files must indicate the object/relational mapping file schema by using the persistence namespace:
+http://java.sun.com/xml/ns/persistence
+and indicate the version of the schema by using the version element as shown below:
+<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
+http://java.sun.com/xml/ns/persistence/orm/orm_2_0.xsd" version="2.0">
+... </entity-mappings>
+]]></xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexType name="emptyType"/>
+ <xsd:simpleType name="versionType">
+ <xsd:restriction base="xsd:token">
+ <xsd:pattern value="[0-9]+(\.[0-9]+)*"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:element name="entity-mappings">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ The entity-mappings element is the root element of a mapping
+ file. It contains the following four types of elements:
+ 1. The persistence-unit-metadata element contains metadata for the entire persistence unit. It is
+ undefined if this element occurs in multiple mapping files within the same persistence unit.
+ 2. The package, schema, catalog and access elements apply to all of the entity, mapped-superclass
+ and embeddable elements defined in the same file in which they occur.
+ 3. The sequence-generator, table-generator, named-query, named-native-query and
+ sql-result-set-mapping elements are global to the persistence unit. It is undefined to have more
+ than one sequence-generator or table-generator of the same name in the same or different mapping
+ files in a persistence unit. It is also undefined to have more than one named-query,
+ named-native-query, or result-set-mapping of the same name in the same or different mapping files in
+ a persistence unit.
+ 4. The entity, mapped-superclass and embeddable elements each define the mapping information for a
+ managed persistent class. The mapping information contained in these elements may be complete or it
+ may be partial.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element
+ name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="persistence-unit-metadata" type="orm:persistence-unit-metadata" minOccurs="0"/>
+ <xsd:element name="package" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="schema" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="catalog" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="access" type="orm:access-type" minOccurs="0"/>
+ <xsd:element name="sequence-generator"
+ type="orm:sequence-generator"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xsd:element name="table-generator" type="orm:table-generator" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="named-query" type="orm:named-query" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="named-native-query"
+ type="orm:named-native-query"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xsd:element name="sql-result-set-mapping" type="orm:sql-result-set-mapping"
+ minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="mapped-superclass" type="orm:mapped-superclass" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="entity" type="orm:entity" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="embeddable" type="orm:embeddable" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="version" type="orm:versionType"
+ fixed="2.0" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+ <!-- **************************************************** -->
+ <xsd:complexType name="persistence-unit-metadata">
+ <xsd:annotation>
+ <xsd:documentation>
+ Metadata that applies to the persistence unit and not just to the mapping file in which it is contained.
+ If the xml-mapping-metadata-complete element is specified, the complete set of mapping metadata for the
+ persistence unit is contained in the XML mapping files for the persistence unit.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element
+ name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="xml-mapping-metadata-complete" type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="persistence-unit-defaults"
+ type="orm:persistence-unit-defaults"
+ minOccurs="0"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="persistence-unit-defaults">
+ <xsd:annotation>
+ <xsd:documentation>
+ These defaults are applied to the persistence unit as a whole unless they are overridden by local
+ annotation or XML element settings.
+ schema - Used as the schema for all tables, secondary tables, join tables, collection tables, sequence
+ generators, and table generators that apply to the persistence unit
+ catalog - Used as the catalog for all tables, secondary tables, join tables, collection tables, sequence
+ generators, and table generators that apply to the persistence unit
+ delimited-identifiers - Used to treat database identifiers as delimited identifiers.
+ access - Used as the access type for all managed classes in the persistence unit
+ cascade-persist - Adds cascade-persist to the set of cascade options in all entity relationships of the
+ persistence unit
+ entity-listeners - List of default entity listeners to be invoked on each entity in the persistence
+ unit.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="schema" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="catalog"
+ type="xsd:string"
+ minOccurs="0"/>
+ <xsd:element name="delimited-identifiers" type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="access"
+ type="orm:access-type" minOccurs="0"/>
+ <xsd:element name="cascade-persist" type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="entity-listeners" type="orm:entity-listeners" minOccurs="0"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="entity">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the settings and mappings for an entity. Is allowed to be sparsely populated and used in
+ conjunction
+ with the annotations. Alternatively, the metadata-complete attribute can be used to indicate that no
+ annotations
+ on the entity class (and its fields or properties) are to be processed. If this is the case then
+ the defaulting rules for the entity and its subelements will be recursively applied.
+ @Target(TYPE) @Retention(RUNTIME) public @interface Entity { String name() default "";
+ }
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element
+ name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="table" type="orm:table" minOccurs="0"/>
+ <xsd:element name="secondary-table" type="orm:secondary-table" minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xsd:element name="primary-key-join-column" type="orm:primary-key-join-column"
+ minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="id-class" type="orm:id-class" minOccurs="0"/>
+ <xsd:element name="inheritance"
+ type="orm:inheritance" minOccurs="0"/>
+ <xsd:element name="discriminator-value" type="orm:discriminator-value" minOccurs="0"/>
+ <xsd:element name="discriminator-column" type="orm:discriminator-column" minOccurs="0"/>
+ <xsd:element name="sequence-generator"
+ type="orm:sequence-generator" minOccurs="0"/>
+ <xsd:element name="table-generator" type="orm:table-generator" minOccurs="0"/>
+ <xsd:element name="named-query" type="orm:named-query" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="named-native-query"
+ type="orm:named-native-query" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="sql-result-set-mapping"
+ type="orm:sql-result-set-mapping" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="exclude-default-listeners"
+ type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="exclude-superclass-listeners" type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="entity-listeners" type="orm:entity-listeners" minOccurs="0"/>
+ <xsd:element name="pre-persist" type="orm:pre-persist"
+ minOccurs="0"/>
+ <xsd:element name="post-persist" type="orm:post-persist" minOccurs="0"/>
+ <xsd:element name="pre-remove" type="orm:pre-remove"
+ minOccurs="0"/>
+ <xsd:element name="post-remove" type="orm:post-remove" minOccurs="0"/>
+ <xsd:element name="pre-update" type="orm:pre-update"
+ minOccurs="0"/>
+ <xsd:element name="post-update" type="orm:post-update" minOccurs="0"/>
+ <xsd:element name="post-load" type="orm:post-load"
+ minOccurs="0"/>
+ <xsd:element name="attribute-override" type="orm:attribute-override" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="association-override" type="orm:association-override"
+ minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="attributes" type="orm:attributes" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="class" type="xsd:string" use="required"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ <xsd:attribute name="cacheable" type="xsd:boolean"/>
+ <xsd:attribute name="metadata-complete" type="xsd:boolean"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="access-type">
+ <xsd:annotation>
+ <xsd:documentation>
+ This element determines how the persistence provider accesses the state of an entity or embedded object.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="PROPERTY"/>
+ <xsd:enumeration value="FIELD"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="association-override">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface AssociationOverride {
+ }
+ String name(); JoinColumn[] joinColumns() default{}; JoinTable joinTable() default @JoinTable;
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:choice>
+ <xsd:element
+ name="join-column" type="orm:join-column" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="join-table" type="orm:join-table" minOccurs="0"/>
+ </xsd:choice>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="attribute-override">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface AttributeOverride {
+ String name(); Column column();
+ }
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="column" type="orm:column"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="attributes">
+ <xsd:annotation>
+ <xsd:documentation>
+ This element contains the entity field or property mappings. It may be sparsely populated to include
+ only a
+ subset of the fields or properties. If metadata-complete for the entity is true then the remainder of
+ the
+ attributes will be defaulted according to the default rules.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:choice>
+ <xsd:element name="id" type="orm:id" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="embedded-id" type="orm:embedded-id"
+ minOccurs="0"/>
+ </xsd:choice>
+ <xsd:element
+ name="basic" type="orm:basic" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="version" type="orm:version" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="many-to-one"
+ type="orm:many-to-one"
+ minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="one-to-many" type="orm:one-to-many" minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xsd:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="many-to-many" type="orm:many-to-many" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="element-collection"
+ type="orm:element-collection" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="embedded" type="orm:embedded"
+ minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="transient" type="orm:transient" minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="basic">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Basic {
+ FetchType fetch() default EAGER; boolean optional() default true;
+ }
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="column" type="orm:column" minOccurs="0"/>
+ <xsd:choice>
+ <xsd:element name="lob" type="orm:lob" minOccurs="0"/>
+ <xsd:element name="temporal" type="orm:temporal" minOccurs="0"/>
+ <xsd:element name="enumerated" type="orm:enumerated" minOccurs="0"/>
+ </xsd:choice>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="fetch" type="orm:fetch-type"/>
+ <xsd:attribute name="optional" type="xsd:boolean"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="cascade-type">
+ <xsd:annotation>
+ <xsd:documentation>
+ public enum CascadeType { ALL, PERSIST, MERGE, REMOVE, REFRESH, DETACH};
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element
+ name="cascade-all" type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="cascade-persist" type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="cascade-merge" type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="cascade-remove" type="orm:emptyType"
+ minOccurs="0"/>
+ <xsd:element name="cascade-refresh" type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="cascade-detach" type="orm:emptyType"
+ minOccurs="0"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="collection-table">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface CollectionTable {
+ }
+ String name() default ""; String catalog() default ""; String schema() default ""; JoinColumn[]
+ joinColumns()
+ default {}; UniqueConstraint[] uniqueConstraints() default {};
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="join-column" type="orm:join-column" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="unique-constraint" type="orm:unique-constraint" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="catalog" type="xsd:string"/>
+ <xsd:attribute name="schema" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="column">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Column {
+ }
+ String name() default ""; boolean unique() default false; boolean nullable() default true; boolean
+ insertable()
+ default true; boolean updatable() default true; String columnDefinition() default ""; String table()
+ default "";
+ int length() default 255; int precision() default 0; // decimal precision int scale() default 0; //
+ decimal
+ scale
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="unique" type="xsd:boolean"/>
+ <xsd:attribute name="nullable" type="xsd:boolean"/>
+ <xsd:attribute name="insertable" type="xsd:boolean"/>
+ <xsd:attribute name="updatable" type="xsd:boolean"/>
+ <xsd:attribute name="column-definition" type="xsd:string"/>
+ <xsd:attribute name="table" type="xsd:string"/>
+ <xsd:attribute name="length" type="xsd:int"/>
+ <xsd:attribute name="precision" type="xsd:int"/>
+ <xsd:attribute name="scale" type="xsd:int"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="column-result">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({}) @Retention(RUNTIME) public @interface ColumnResult {
+ }
+ String name();
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="discriminator-column">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface DiscriminatorColumn {
+ String name() default "DTYPE"; DiscriminatorType discriminatorType() default STRING; String
+ columnDefinition()
+ default ""; int length() default 31;
+ }
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="discriminator-type" type="orm:discriminator-type"/>
+ <xsd:attribute name="column-definition" type="xsd:string"/>
+ <xsd:attribute name="length" type="xsd:int"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="discriminator-type">
+ <xsd:annotation>
+ <xsd:documentation>public enum DiscriminatorType { STRING, CHAR, INTEGER };
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="STRING"/>
+ <xsd:enumeration value="CHAR"/>
+ <xsd:enumeration value="INTEGER"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="discriminator-value">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface DiscriminatorValue {
+ }
+ String value();
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string"/>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="element-collection">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface ElementCollection {
+ }
+ Class targetClass() default void.class; FetchType fetch() default LAZY;
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice>
+ <xsd:element
+ name="order-by" type="orm:order-by" minOccurs="0"/>
+ <xsd:element name="order-column" type="orm:order-column" minOccurs="0"/>
+ </xsd:choice>
+ <xsd:choice>
+ <xsd:element name="map-key" type="orm:map-key"
+ minOccurs="0"/>
+ <xsd:sequence>
+ <xsd:element name="map-key-class" type="orm:map-key-class" minOccurs="0"/>
+ <xsd:choice>
+ <xsd:element name="map-key-temporal" type="orm:temporal" minOccurs="0"/>
+ <xsd:element name="map-key-enumerated"
+ type="orm:enumerated"
+ minOccurs="0"/>
+ <xsd:element name="map-key-attribute-override" type="orm:attribute-override" minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xsd:choice>
+ <xsd:choice>
+ <xsd:element name="map-key-column" type="orm:map-key-column" minOccurs="0"/>
+ <xsd:element name="map-key-join-column"
+ type="orm:map-key-join-column" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:choice>
+ <xsd:choice>
+
+ <xsd:sequence>
+ <xsd:element name="column" type="orm:column" minOccurs="0"/>
+ <xsd:choice>
+ <xsd:element name="temporal" type="orm:temporal" minOccurs="0"/>
+ <xsd:element name="enumerated" type="orm:enumerated" minOccurs="0"/>
+ <xsd:element name="lob" type="orm:lob"
+ minOccurs="0"/>
+ </xsd:choice>
+ </xsd:sequence>
+ <xsd:sequence>
+ <xsd:element name="attribute-override"
+ type="orm:attribute-override"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xsd:element name="association-override"
+ type="orm:association-override"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+
+
+ </xsd:sequence>
+ </xsd:choice>
+ <xsd:element name="collection-table" type="orm:collection-table"
+ minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="target-class" type="xsd:string"/>
+ <xsd:attribute name="fetch" type="orm:fetch-type"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="embeddable">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the settings and mappings for embeddable objects. Is allowed to be sparsely populated and used
+ in
+ conjunction with
+ the annotations. Alternatively, the metadata-complete attribute can be used to indicate that no
+ annotations are
+ to be processed in the class. If this is the case then the defaulting rules will be recursively applied.
+ @Target({TYPE}) @Retention(RUNTIME) public @interface Embeddable {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="attributes" type="orm:embeddable-attributes"
+ minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="class" type="xsd:string" use="required"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ <xsd:attribute name="metadata-complete" type="xsd:boolean"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="embeddable-attributes">
+ <xsd:sequence>
+ <xsd:element name="basic" type="orm:basic" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="many-to-one" type="orm:many-to-one"
+ minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="one-to-many" type="orm:one-to-many" minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xsd:element name="one-to-one" type="orm:one-to-one" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="many-to-many"
+ type="orm:many-to-many" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="element-collection"
+ type="orm:element-collection"
+ minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="embedded" type="orm:embedded" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="transient" type="orm:transient" minOccurs="0" maxOccurs="unbounded"/>
+
+ </xsd:sequence>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="embedded">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Embedded {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="attribute-override" type="orm:attribute-override" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element
+
+ name="association-override" type="orm:association-override"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="embedded-id">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface EmbeddedId {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="attribute-override" type="orm:attribute-override" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="entity-listener">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines an entity listener to be invoked at lifecycle events for the entities that list this listener.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="pre-persist" type="orm:pre-persist" minOccurs="0"/>
+ <xsd:element name="post-persist" type="orm:post-persist" minOccurs="0"/>
+ <xsd:element name="pre-remove" type="orm:pre-remove" minOccurs="0"/>
+ <xsd:element name="post-remove" type="orm:post-remove"
+ minOccurs="0"/>
+ <xsd:element name="pre-update" type="orm:pre-update" minOccurs="0"/>
+ <xsd:element name="post-update"
+ type="orm:post-update" minOccurs="0"/>
+ <xsd:element name="post-load" type="orm:post-load" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="class" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="entity-listeners">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface EntityListeners {
+ }
+ Class[] value();
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="entity-listener" type="orm:entity-listener" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="entity-result">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({}) @Retention(RUNTIME) public @interface EntityResult {
+ }
+ Class entityClass(); FieldResult[] fields() default {}; String discriminatorColumn() default "";
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="field-result" type="orm:field-result" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="entity-class" type="xsd:string" use="required"/>
+ <xsd:attribute name="discriminator-column" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="enum-type">
+ <xsd:annotation>
+ <xsd:documentation>
+ public enum EnumType { ORDINAL,
+ }
+ STRING
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="ORDINAL"/>
+ <xsd:enumeration value="STRING"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="enumerated">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Enumerated {
+ }
+ EnumType value() default ORDINAL;
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="orm:enum-type"/>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="fetch-type">
+ <xsd:annotation>
+ <xsd:documentation>public enum FetchType { LAZY, EAGER };
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="LAZY"/>
+ <xsd:enumeration value="EAGER"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="field-result">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({}) @Retention(RUNTIME) public @interface FieldResult {
+ }
+ String name(); String column();
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="column" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="generated-value">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface GeneratedValue {
+ }
+ GenerationType strategy() default AUTO; String generator() default "";
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="strategy" type="orm:generation-type"/>
+ <xsd:attribute name="generator" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="generation-type">
+ <xsd:annotation>
+ <xsd:documentation>public enum GenerationType { TABLE, SEQUENCE, IDENTITY, AUTO };
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="TABLE"/>
+ <xsd:enumeration value="SEQUENCE"/>
+ <xsd:enumeration value="IDENTITY"/>
+ <xsd:enumeration value="AUTO"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <!-- **************************************************** -->
+ <xsd:complexType name="id">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Id {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element
+ name="column" type="orm:column" minOccurs="0"/>
+ <xsd:element name="generated-value" type="orm:generated-value" minOccurs="0"/>
+ <xsd:element name="temporal" type="orm:temporal" minOccurs="0"/>
+ <xsd:element name="table-generator" type="orm:table-generator"
+ minOccurs="0"/>
+ <xsd:element name="sequence-generator" type="orm:sequence-generator"
+ minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="id-class">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface IdClass {
+ }
+ Class value();
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="class" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="inheritance">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface Inheritance {
+ }
+ InheritanceType strategy() default SINGLE_TABLE;
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="strategy" type="orm:inheritance-type"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="inheritance-type">
+ <xsd:annotation>
+ <xsd:documentation>
+ public enum InheritanceType { SINGLE_TABLE, JOINED, TABLE_PER_CLASS};
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="SINGLE_TABLE"/>
+ <xsd:enumeration value="JOINED"/>
+ <xsd:enumeration value="TABLE_PER_CLASS"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="join-column">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface JoinColumn {
+ }
+ String name() default ""; String referencedColumnName() default ""; boolean unique() default false;
+ boolean
+ nullable() default true; boolean insertable() default true; boolean updatable() default true; String
+ columnDefinition() default ""; String table() default "";
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="referenced-column-name" type="xsd:string"/>
+ <xsd:attribute name="unique" type="xsd:boolean"/>
+ <xsd:attribute name="nullable" type="xsd:boolean"/>
+ <xsd:attribute name="insertable" type="xsd:boolean"/>
+ <xsd:attribute name="updatable" type="xsd:boolean"/>
+ <xsd:attribute name="column-definition" type="xsd:string"/>
+ <xsd:attribute name="table" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="join-table">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface JoinTable {
+ }
+ String name() default ""; String catalog() default ""; String schema() default ""; JoinColumn[]
+ joinColumns()
+ default {}; JoinColumn[] inverseJoinColumns() default {}; UniqueConstraint[] uniqueConstraints() default
+ {};
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="join-column" type="orm:join-column" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="inverse-join-column" type="orm:join-column" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="unique-constraint" type="orm:unique-constraint" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="catalog" type="xsd:string"/>
+ <xsd:attribute name="schema" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="lob">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Lob {}
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="lock-mode-type">
+ <xsd:annotation>
+ <xsd:documentation>
+ public enum LockModeType { READ, WRITE, OPTIMISTIC, OPTIMISTIC_FORCE_INCREMENT, PESSIMISTIC_READ,
+ PESSIMISTIC_WRITE, PESSIMISTIC_FORCE_INCREMENT, NONE};
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="READ"/>
+ <xsd:enumeration value="WRITE"/>
+ <xsd:enumeration value="OPTIMISTIC"/>
+ <xsd:enumeration value="OPTIMISTIC_FORCE_INCREMENT"/>
+ <xsd:enumeration value="PESSIMISTIC_READ"/>
+ <xsd:enumeration value="PESSIMISTIC_WRITE"/>
+ <xsd:enumeration value="PESSIMISTIC_FORCE_INCREMENT"/>
+ <xsd:enumeration value="NONE"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <!-- **************************************************** -->
+ <xsd:complexType name="many-to-many">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface ManyToMany {
+ }
+ Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default
+ LAZY;
+ String mappedBy() default "";
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice>
+ <xsd:element name="order-by" type="orm:order-by"
+ minOccurs="0"/>
+ <xsd:element name="order-column" type="orm:order-column"
+ minOccurs="0"/>
+ </xsd:choice>
+ <xsd:choice>
+ <xsd:element name="map-key" type="orm:map-key"
+ minOccurs="0"/>
+ <xsd:sequence>
+ <xsd:element name="map-key-class" type="orm:map-key-class" minOccurs="0"/>
+ <xsd:choice>
+ <xsd:element name="map-key-temporal" type="orm:temporal" minOccurs="0"/>
+ <xsd:element name="map-key-enumerated"
+ type="orm:enumerated" minOccurs="0"/>
+ <xsd:element name="map-key-attribute-override" type="orm:attribute-override"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ <xsd:choice>
+ <xsd:element name="map-key-join-column"
+ type="orm:map-key-join-column"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:choice>
+ <xsd:element name="join-table" type="orm:join-table"
+ minOccurs="0"/>
+ <xsd:element name="cascade" type="orm:cascade-type"
+ minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="target-entity" type="xsd:string"/>
+ <xsd:attribute name="fetch" type="orm:fetch-type"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ <xsd:attribute name="mapped-by" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="many-to-one">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface ManyToOne {
+
+ name="map-key-column" type="orm:map-key-column"
+ }
+ Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default
+ EAGER;
+ boolean optional() default true;
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice>
+ <xsd:element name="join-column" type="orm:join-column"
+ minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="join-table" type="orm:join-table"
+ minOccurs="0"/>
+ </xsd:choice>
+ <xsd:element name="cascade" type="orm:cascade-type"
+ minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="target-entity" type="xsd:string"/>
+ <xsd:attribute name="fetch" type="orm:fetch-type"/>
+ <xsd:attribute name="optional" type="xsd:boolean"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ <xsd:attribute name="maps-id" type="xsd:string"/>
+ <xsd:attribute name="id" type="xsd:boolean"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="map-key">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface MapKey {
+ }
+ String name() default "";
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="map-key-class">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface MapKeyClass {
+ }
+ Class value();
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="class" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="map-key-column">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface MapKeyColumn {
+ }
+ String name() default ""; boolean unique() default false; boolean nullable() default false; boolean
+ insertable()
+ default true; boolean updatable() default true; String columnDefinition() default ""; String table()
+ default "";
+ int length() default 255; int precision() default 0; // decimal precision int scale() default 0; //
+ decimal
+ scale
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="unique" type="xsd:boolean"/>
+ <xsd:attribute name="nullable" type="xsd:boolean"/>
+ <xsd:attribute name="insertable" type="xsd:boolean"/>
+ <xsd:attribute name="updatable" type="xsd:boolean"/>
+ <xsd:attribute name="column-definition" type="xsd:string"/>
+ <xsd:attribute name="table" type="xsd:string"/>
+ <xsd:attribute name="length" type="xsd:int"/>
+ <xsd:attribute name="precision" type="xsd:int"/>
+ <xsd:attribute name="scale" type="xsd:int"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="map-key-join-column">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface MapKeyJoinColumn {
+ }
+ String name() default ""; String referencedColumnName() default ""; boolean unique() default false;
+ boolean
+ nullable() default false; boolean insertable() default true; boolean updatable() default true; String
+ columnDefinition() default ""; String table() default "";
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="referenced-column-name" type="xsd:string"/>
+ <xsd:attribute name="unique" type="xsd:boolean"/>
+ <xsd:attribute name="nullable" type="xsd:boolean"/>
+ <xsd:attribute name="insertable" type="xsd:boolean"/>
+ <xsd:attribute name="updatable" type="xsd:boolean"/>
+ <xsd:attribute name="column-definition" type="xsd:string"/>
+ <xsd:attribute name="table" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="mapped-superclass">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the settings and mappings for a mapped superclass. Is allowed to be sparsely populated and used
+ in
+ conjunction with the annotations. Alternatively, the metadata-complete attribute can be used to indicate
+ that no
+ annotations are to be processed If this is the case then the defaulting rules will be recursively
+ applied.
+ @Target(TYPE) @Retention(RUNTIME) public @interface MappedSuperclass{}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="id-class" type="orm:id-class" minOccurs="0"/>
+ <xsd:element name="exclude-default-listeners" type="orm:emptyType"
+ minOccurs="0"/>
+ <xsd:element name="exclude-superclass-listeners" type="orm:emptyType" minOccurs="0"/>
+ <xsd:element name="entity-listeners"
+ type="orm:entity-listeners" minOccurs="0"/>
+ <xsd:element name="pre-persist" type="orm:pre-persist" minOccurs="0"/>
+ <xsd:element name="post-persist" type="orm:post-persist"
+ minOccurs="0"/>
+ <xsd:element name="pre-remove" type="orm:pre-remove" minOccurs="0"/>
+ <xsd:element name="post-remove" type="orm:post-remove"
+ minOccurs="0"/>
+ <xsd:element name="pre-update" type="orm:pre-update" minOccurs="0"/>
+ <xsd:element name="post-update" type="orm:post-update"
+ minOccurs="0"/>
+ <xsd:element name="post-load" type="orm:post-load" minOccurs="0"/>
+
+ <xsd:element name="attributes" type="orm:attributes" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="class" type="xsd:string" use="required"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ <xsd:attribute name="metadata-complete" type="xsd:boolean"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="named-native-query">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface NamedNativeQuery {
+ }
+ String name(); String query(); QueryHint[] hints() default {}; Class resultClass() default void.class;
+ String
+ resultSetMapping() default ""; //named SqlResultSetMapping
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="query" type="xsd:string"/>
+ <xsd:element name="hint" type="orm:query-hint" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="result-class" type="xsd:string"/>
+ <xsd:attribute name="result-set-mapping" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="named-query">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface NamedQuery {
+ }
+ String name(); String query(); LockModeType lockMode() default NONE; QueryHint[] hints() default {};
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="query" type="xsd:string"/>
+ <xsd:element name="lock-mode" type="orm:lock-mode-type" minOccurs="0"/>
+ <xsd:element name="hint"
+ type="orm:query-hint"
+ minOccurs="0" maxOccurs="unbounded"/>
+
+
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="one-to-many">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface OneToMany {
+ }
+ Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default
+ LAZY;
+ String mappedBy() default "";
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice>
+ <xsd:element name="order-by" type="orm:order-by"
+ minOccurs="0"/>
+ <xsd:element name="order-column" type="orm:order-column" minOccurs="0"/>
+ </xsd:choice>
+ <xsd:choice>
+
+ <xsd:element name="map-key" type="orm:map-key" minOccurs="0"/>
+ <xsd:sequence>
+ <xsd:element name="map-key-class" type="orm:map-key-class"
+ minOccurs="0"/>
+ <xsd:choice>
+ <xsd:element name="map-key-temporal" type="orm:temporal" minOccurs="0"/>
+ <xsd:element name="map-key-enumerated"
+ type="orm:enumerated" minOccurs="0"/>
+ <xsd:element name="map-key-attribute-override" type="orm:attribute-override"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:choice>
+ <xsd:choice>
+ <xsd:element name="map-key-column" type="orm:map-key-column" minOccurs="0"/>
+ <xsd:element name="map-key-join-column"
+ type="orm:map-key-join-column"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ </xsd:choice>
+ </xsd:sequence>
+
+ </xsd:choice>
+ <xsd:choice>
+
+ </xsd:choice>
+ <xsd:choice>
+ <xsd:element name="join-table" type="orm:join-table" minOccurs="0"/>
+ <xsd:element name="join-column" type="orm:join-column" minOccurs="0" maxOccurs="unbounded"/>
+
+
+ </xsd:choice>
+ <xsd:element name="cascade" type="orm:cascade-type"
+ minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="target-entity" type="xsd:string"/>
+ <xsd:attribute name="fetch" type="orm:fetch-type"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ <xsd:attribute name="mapped-by" type="xsd:string"/>
+ <xsd:attribute name="orphan-removal" type="xsd:boolean"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="one-to-one">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface OneToOne {
+ }
+ Class targetEntity() default void.class; CascadeType[] cascade() default {}; FetchType fetch() default
+ EAGER;
+ boolean optional() default true;
+ String mappedBy() default ""; boolean orphanRemoval() default false;
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice>
+ <xsd:element name="primary-key-join-column"
+ type="orm:primary-key-join-column"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xsd:element name="join-column" type="orm:join-column" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element name="join-table"
+ type="orm:join-table" minOccurs="0"/>
+ </xsd:choice>
+ <xsd:element name="cascade" type="orm:cascade-type"
+ minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute
+ name="target-entity" type="xsd:string"/>
+ <xsd:attribute name="fetch" type="orm:fetch-type"/>
+ <xsd:attribute name="optional" type="xsd:boolean"/>
+ <xsd:attribute
+ name="access" type="orm:access-type"/>
+ <xsd:attribute name="mapped-by" type="xsd:string"/>
+ <xsd:attribute name="orphan-removal" type="xsd:boolean"/>
+ <xsd:attribute
+ name="maps-id" type="xsd:string"/>
+ <xsd:attribute
+ name="id" type="xsd:boolean"/>
+
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="order-by">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface OrderBy {
+ }
+ String value() default "";
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:string"/>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="order-column">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface OrderColumn {
+ String name() default ""; boolean nullable() default true; boolean insertable() default true; boolean
+ updatable() default true; String columnDefinition() default "";
+ }
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="nullable" type="xsd:boolean"/>
+ <xsd:attribute name="insertable" type="xsd:boolean"/>
+ <xsd:attribute name="updatable" type="xsd:boolean"/>
+ <xsd:attribute name="column-definition" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="post-load">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD}) @Retention(RUNTIME) public @interface PostLoad {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="method-name"
+
+ type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="post-persist">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD}) @Retention(RUNTIME) public @interface PostPersist {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="method-name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="post-remove">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD}) @Retention(RUNTIME) public @interface PostRemove {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="method-name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="post-update">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD}) @Retention(RUNTIME) public @interface PostUpdate {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="method-name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="pre-persist">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD}) @Retention(RUNTIME) public @interface PrePersist {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="method-name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="pre-remove">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD}) @Retention(RUNTIME) public @interface PreRemove {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="method-name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="pre-update">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD}) @Retention(RUNTIME) public @interface PreUpdate {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="method-name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="primary-key-join-column">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface PrimaryKeyJoinColumn {
+ }
+ String name() default ""; String referencedColumnName() default ""; String columnDefinition() default
+ "";
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="referenced-column-name" type="xsd:string"/>
+ <xsd:attribute name="column-definition" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="query-hint">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({}) @Retention(RUNTIME) public @interface QueryHint {
+ }
+ String name(); String value();
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="value" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="secondary-table">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface SecondaryTable {
+ String name(); String catalog() default ""; String schema() default ""; PrimaryKeyJoinColumn[]
+ pkJoinColumns()
+ default {}; UniqueConstraint[] uniqueConstraints() default {};
+ }
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="primary-key-join-column"
+ type="orm:primary-key-join-column"
+ minOccurs="0"
+ maxOccurs="unbounded"/>
+ <xsd:element
+
+ name="unique-constraint" type="orm:unique-constraint" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="catalog" type="xsd:string"/>
+ <xsd:attribute name="schema" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="sequence-generator">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface SequenceGenerator {
+ }
+ String name(); String sequenceName() default ""; String catalog() default ""; String schema() default
+ ""; int
+ initialValue() default 1; int allocationSize() default 50;
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="sequence-name" type="xsd:string"/>
+ <xsd:attribute name="catalog" type="xsd:string"/>
+ <xsd:attribute name="schema" type="xsd:string"/>
+ <xsd:attribute name="initial-value" type="xsd:int"/>
+ <xsd:attribute name="allocation-size" type="xsd:int"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="sql-result-set-mapping">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface SqlResultSetMapping {
+ }
+ String name(); EntityResult[] entities() default {}; ColumnResult[] columns() default {};
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element
+ name="entity-result" type="orm:entity-result" minOccurs="0" maxOccurs="unbounded"/>
+ <xsd:element
+ name="column-result"
+ type="orm:column-result" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="table">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE}) @Retention(RUNTIME) public @interface Table {
+ String name() default "";
+ String catalog() default ""; String schema() default ""; UniqueConstraint[] uniqueConstraints() default
+ {};
+ }
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="unique-constraint" type="orm:unique-constraint" minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string"/>
+ <xsd:attribute name="catalog" type="xsd:string"/>
+ <xsd:attribute name="schema" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="table-generator">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME) public @interface TableGenerator {
+ }
+ String name(); String table() default ""; String catalog() default ""; String schema() default "";
+ String
+ pkColumnName() default ""; String valueColumnName() default ""; String pkColumnValue() default ""; int
+ initialValue() default 0; int allocationSize() default 50; UniqueConstraint[] uniqueConstraints()
+ default {};
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="description" type="xsd:string" minOccurs="0"/>
+ <xsd:element name="unique-constraint" type="orm:unique-constraint"
+ minOccurs="0" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="table" type="xsd:string"/>
+ <xsd:attribute name="catalog" type="xsd:string"/>
+ <xsd:attribute name="schema" type="xsd:string"/>
+ <xsd:attribute name="pk-column-name" type="xsd:string"/>
+ <xsd:attribute name="value-column-name" type="xsd:string"/>
+ <xsd:attribute name="pk-column-value" type="xsd:string"/>
+ <xsd:attribute name="initial-value" type="xsd:int"/>
+ <xsd:attribute name="allocation-size" type="xsd:int"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="temporal">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Temporal {
+ TemporalType value(); }
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="orm:temporal-type"/>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:simpleType name="temporal-type">
+ <xsd:annotation>
+ <xsd:documentation>
+ public enum TemporalType { DATE, // java.sql.Date TIME, // java.sql.Time TIMESTAMP // java.sql.Timestamp
+ }
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:restriction base="xsd:token">
+ <xsd:enumeration value="DATE"/>
+ <xsd:enumeration value="TIME"/>
+ <xsd:enumeration value="TIMESTAMP"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="transient">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Transient {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="unique-constraint">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({}) @Retention(RUNTIME) public @interface UniqueConstraint {
+ }
+ String name() default ""; String[] columnNames();
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="column-name" type="xsd:string" maxOccurs="unbounded"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string"/>
+ </xsd:complexType>
+ <!-- **************************************************** -->
+ <xsd:complexType name="version">
+ <xsd:annotation>
+ <xsd:documentation>
+ @Target({METHOD, FIELD}) @Retention(RUNTIME) public @interface Version {}
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element name="column" type="orm:column" minOccurs="0"/>
+ <xsd:element name="temporal" type="orm:temporal" minOccurs="0"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="access" type="orm:access-type"/>
+ </xsd:complexType>
+</xsd:schema>
\ No newline at end of file
15 years
Hibernate SVN: r18226 - in core/trunk/envers/src: main/java/org/hibernate/envers/entities and 3 other directories.
by hibernate-commits@lists.jboss.org
Author: adamw
Date: 2009-12-15 07:52:20 -0500 (Tue, 15 Dec 2009)
New Revision: 18226
Modified:
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java
core/trunk/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java
core/trunk/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java
core/trunk/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java
core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java
Log:
HHH-4694:
- if a field is non-insertable in a "fake" bidirectional relation, not storing modifications made on the non-insertable side
- also, not generating bidirectional collection changes in such case
- updating test
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java 2009-12-15 12:27:01 UTC (rev 18225)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java 2009-12-15 12:52:20 UTC (rev 18226)
@@ -224,7 +224,7 @@
// The mapper will only be used to map from entity to map, so no need to provide other details
// when constructing the PropertyData.
new PropertyData(auditMappedBy, null, null, null),
- referencedEntityName);
+ referencedEntityName, false);
} else {
fakeBidirectionalRelationMapper = null;
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java 2009-12-15 12:27:01 UTC (rev 18225)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java 2009-12-15 12:52:20 UTC (rev 18226)
@@ -70,7 +70,7 @@
// Storing information about this relation
mainGenerator.getEntitiesConfigurations().get(entityName).addToOneRelation(
- propertyAuditingData.getName(), referencedEntityName, relMapper);
+ propertyAuditingData.getName(), referencedEntityName, relMapper, insertable);
// If the property isn't insertable, checking if this is not a "fake" bidirectional many-to-one relationship,
// that is, when the one side owns the relation (and is a collection), and the many side is non insertable.
@@ -79,6 +79,7 @@
// the entity that didn't involve the relation, it's value will then be stored properly. In case of changes
// to the entity that did involve the relation, it's the responsibility of the collection side to store the
// proper data.
+ boolean nonInsertableFake = false;
if (!insertable) {
ClassAuditingData referencedAuditingData = mainGenerator.getClassesAuditingData().getClassAuditingData(referencedEntityName);
@@ -91,6 +92,7 @@
referencedEntityName + " entity.");
insertable = true;
+ nonInsertableFake = true;
break;
}
}
@@ -106,7 +108,7 @@
// Adding mapper for the id
PropertyData propertyData = propertyAuditingData.getPropertyData();
- mapper.addComposite(propertyData, new ToOneIdMapper(relMapper, propertyData, referencedEntityName));
+ mapper.addComposite(propertyData, new ToOneIdMapper(relMapper, propertyData, referencedEntityName, nonInsertableFake));
}
@SuppressWarnings({"unchecked"})
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java 2009-12-15 12:27:01 UTC (rev 18225)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java 2009-12-15 12:52:20 UTC (rev 18226)
@@ -51,31 +51,31 @@
this.relations = new HashMap<String, RelationDescription>();
}
- public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper) {
+ public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper, boolean insertable) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE,
- toEntityName, null, idMapper, null));
+ toEntityName, null, idMapper, null, insertable));
}
public void addToOneNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName,
IdMapper idMapper) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE_NOT_OWNING,
- toEntityName, mappedByPropertyName, idMapper, null));
+ toEntityName, mappedByPropertyName, idMapper, null, true));
}
public void addToManyNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName,
IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_NOT_OWNING,
- toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper));
+ toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper, true));
}
public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE,
- toEntityName, null, null, null));
+ toEntityName, null, null, null, true));
}
public void addToManyMiddleNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE_NOT_OWNING,
- toEntityName, mappedByPropertyName, null, null));
+ toEntityName, mappedByPropertyName, null, null, true));
}
public boolean isRelation(String propertyName) {
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java 2009-12-15 12:27:01 UTC (rev 18225)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java 2009-12-15 12:52:20 UTC (rev 18226)
@@ -36,17 +36,19 @@
private final String mappedByPropertyName;
private final IdMapper idMapper;
private final PropertyMapper fakeBidirectionalRelationMapper;
+ private final boolean insertable;
private boolean bidirectional;
public RelationDescription(String fromPropertyName, RelationType relationType, String toEntityName,
String mappedByPropertyName, IdMapper idMapper,
- PropertyMapper fakeBidirectionalRelationMapper) {
+ PropertyMapper fakeBidirectionalRelationMapper, boolean insertable) {
this.fromPropertyName = fromPropertyName;
this.relationType = relationType;
this.toEntityName = toEntityName;
this.mappedByPropertyName = mappedByPropertyName;
this.idMapper = idMapper;
this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper;
+ this.insertable = insertable;
this.bidirectional = false;
}
@@ -75,6 +77,10 @@
return fakeBidirectionalRelationMapper;
}
+ public boolean isInsertable() {
+ return insertable;
+ }
+
public boolean isBidirectional() {
return bidirectional;
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java 2009-12-15 12:27:01 UTC (rev 18225)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/entities/mapper/relation/ToOneIdMapper.java 2009-12-15 12:52:20 UTC (rev 18226)
@@ -49,20 +49,26 @@
private final IdMapper delegate;
private final PropertyData propertyData;
private final String referencedEntityName;
+ private final boolean nonInsertableFake;
- public ToOneIdMapper(IdMapper delegate, PropertyData propertyData, String referencedEntityName) {
+ public ToOneIdMapper(IdMapper delegate, PropertyData propertyData, String referencedEntityName, boolean nonInsertableFake) {
this.delegate = delegate;
this.propertyData = propertyData;
this.referencedEntityName = referencedEntityName;
+ this.nonInsertableFake = nonInsertableFake;
}
public boolean mapToMapFromEntity(SessionImplementor session, Map<String, Object> data, Object newObj, Object oldObj) {
HashMap<String, Object> newData = new HashMap<String, Object>();
data.put(propertyData.getName(), newData);
- delegate.mapToMapFromEntity(newData, newObj);
+ // If this property is originally non-insertable, but made insertable because it is in a many-to-one "fake"
+ // bi-directional relation, we always store the "old", unchaged data, to prevent storing changes made
+ // to this field. It is the responsibility of the collection to properly update it if it really changed.
+ delegate.mapToMapFromEntity(newData, nonInsertableFake ? oldObj : newObj);
- return !Tools.entitiesEqual(session, newObj, oldObj);
+ //noinspection SimplifiableConditionalExpression
+ return nonInsertableFake ? false : !Tools.entitiesEqual(session, newObj, oldObj);
}
public void mapToEntityFromMap(AuditConfiguration verCfg, Object obj, Map data, Object primaryKey,
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java 2009-12-15 12:27:01 UTC (rev 18225)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java 2009-12-15 12:52:20 UTC (rev 18226)
@@ -84,7 +84,8 @@
for (int i=0; i<propertyNames.length; i++) {
String propertyName = propertyNames[i];
RelationDescription relDesc = verCfg.getEntCfg().getRelationDescription(entityName, propertyName);
- if (relDesc != null && relDesc.isBidirectional() && relDesc.getRelationType() == RelationType.TO_ONE) {
+ if (relDesc != null && relDesc.isBidirectional() && relDesc.getRelationType() == RelationType.TO_ONE &&
+ relDesc.isInsertable()) {
// Checking for changes
Object oldValue = oldState == null ? null : oldState[i];
Object newValue = newState == null ? null : newState[i];
@@ -144,11 +145,14 @@
if (verCfg.getEntCfg().isVersioned(entityName)) {
AuditSync verSync = verCfg.getSyncManager().get(event.getSession());
- verSync.addWorkUnit(new AddWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
- event.getId(), event.getPersister(), event.getState()));
+ AuditWorkUnit workUnit = new AddWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
+ event.getId(), event.getPersister(), event.getState());
+ verSync.addWorkUnit(workUnit);
- generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(),
- null, event.getSession());
+ if (workUnit.containsWork()) {
+ generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(),
+ null, event.getSession());
+ }
}
}
@@ -158,11 +162,14 @@
if (verCfg.getEntCfg().isVersioned(entityName)) {
AuditSync verSync = verCfg.getSyncManager().get(event.getSession());
- verSync.addWorkUnit(new ModWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
- event.getId(), event.getPersister(), event.getState(), event.getOldState()));
+ AuditWorkUnit workUnit = new ModWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
+ event.getId(), event.getPersister(), event.getState(), event.getOldState());
+ verSync.addWorkUnit(workUnit);
- generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(),
- event.getOldState(), event.getSession());
+ if (workUnit.containsWork()) {
+ generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, event.getState(),
+ event.getOldState(), event.getSession());
+ }
}
}
@@ -172,11 +179,14 @@
if (verCfg.getEntCfg().isVersioned(entityName)) {
AuditSync verSync = verCfg.getSyncManager().get(event.getSession());
- verSync.addWorkUnit(new DelWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
- event.getId(), event.getPersister(), event.getDeletedState()));
+ AuditWorkUnit workUnit = new DelWorkUnit(event.getSession(), event.getPersister().getEntityName(), verCfg,
+ event.getId(), event.getPersister(), event.getDeletedState());
+ verSync.addWorkUnit(workUnit);
- generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, null,
- event.getDeletedState(), event.getSession());
+ if (workUnit.containsWork()) {
+ generateBidirectionalCollectionChangeWorkUnits(verSync, event.getPersister(), entityName, null,
+ event.getDeletedState(), event.getSession());
+ }
}
}
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java 2009-12-15 12:27:01 UTC (rev 18225)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java 2009-12-15 12:52:20 UTC (rev 18226)
@@ -86,12 +86,27 @@
em.getTransaction().commit();
em.clear();
+ // No revision - no changes
+ em.getTransaction().begin();
+
+ ing1 = em.find(ListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
+ ing2 = em.find(ListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
+ ed1 = em.find(ListJoinColumnBidirectionalRefEdEntity.class, ed1.getId());
+ ed2 = em.find(ListJoinColumnBidirectionalRefEdEntity.class, ed2.getId());
+
+ ed2.setOwner(ing2);
+
+ em.getTransaction().commit();
+ em.clear();
+
// Revision 3 (ing1: ed1, ed2)
em.getTransaction().begin();
ed1 = em.find(ListJoinColumnBidirectionalRefEdEntity.class, ed1.getId());
ed1.setData("ed1 bis");
+ // Shouldn't get written
+ ed1.setOwner(ing2);
em.getTransaction().commit();
em.clear();
15 years
Hibernate SVN: r18225 - in core/trunk/envers: src/main/java/org/hibernate/envers/event and 3 other directories.
by hibernate-commits@lists.jboss.org
Author: adamw
Date: 2009-12-15 07:27:01 -0500 (Tue, 15 Dec 2009)
New Revision: 18225
Added:
core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefEdEntity1.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefEdEntity2.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefIngEntity.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/DoubleJoinColumnBidirectionalList.java
Modified:
core/trunk/envers/pom.xml
core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/FakeBidirectionalRelationWorkUnit.java
Log:
HHH-4694:
- adding support for multiple "fake" bidirectional relations
- downgrading testng back to 5.8 as using 5.10 was OOMing the tests
- updating h2 dependency to a newer version
Modified: core/trunk/envers/pom.xml
===================================================================
--- core/trunk/envers/pom.xml 2009-12-15 10:50:11 UTC (rev 18224)
+++ core/trunk/envers/pom.xml 2009-12-15 12:27:01 UTC (rev 18225)
@@ -95,14 +95,14 @@
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
- <version>5.10</version>
+ <version>5.8</version>
<classifier>jdk15</classifier>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
- <version>1.0.79</version>
+ <version>1.2.125</version>
<scope>test</scope>
</dependency>
<dependency>
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java 2009-12-15 10:50:11 UTC (rev 18224)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java 2009-12-15 12:27:01 UTC (rev 18225)
@@ -229,7 +229,7 @@
relatedId, relatedObj);
verSync.addWorkUnit(new FakeBidirectionalRelationWorkUnit(event.getSession(), relatedEntityName, verCfg,
- relatedId, event.getAffectedOwnerOrNull(), rd, revType, nestedWorkUnit));
+ relatedId, referencingPropertyName, event.getAffectedOwnerOrNull(), rd, revType, nestedWorkUnit));
}
// We also have to generate a collection change work unit for the owning entity.
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/FakeBidirectionalRelationWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/FakeBidirectionalRelationWorkUnit.java 2009-12-15 10:50:11 UTC (rev 18224)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/FakeBidirectionalRelationWorkUnit.java 2009-12-15 12:27:01 UTC (rev 18225)
@@ -8,6 +8,8 @@
import java.io.Serializable;
import java.util.Map;
import java.util.HashMap;
+import java.util.Set;
+import java.util.HashSet;
/**
* A work unit that handles "fake" bidirectional one-to-many relations (mapped with {@code @OneToMany+@JoinColumn} and
@@ -15,44 +17,49 @@
* @author Adam Warski (adam at warski dot org)
*/
public class FakeBidirectionalRelationWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit {
- private final Object owningEntity;
- private final RelationDescription rd;
- private final RevisionType revisionType;
+ private final Map<String, FakeRelationChange> fakeRelationChanges;
/*
* The work unit responsible for generating the "raw" entity data to be saved.
*/
private final AuditWorkUnit nestedWorkUnit;
-
public FakeBidirectionalRelationWorkUnit(SessionImplementor sessionImplementor, String entityName,
- AuditConfiguration verCfg, Serializable id, Object owningEntity,
+ AuditConfiguration verCfg, Serializable id,
+ String referencingPropertyName, Object owningEntity,
RelationDescription rd, RevisionType revisionType,
AuditWorkUnit nestedWorkUnit) {
super(sessionImplementor, entityName, verCfg, id);
+ this.nestedWorkUnit = nestedWorkUnit;
+ // Adding the change for the relation.
+ fakeRelationChanges = new HashMap<String, FakeRelationChange>();
+ fakeRelationChanges.put(referencingPropertyName, new FakeRelationChange(owningEntity, rd, revisionType));
+ }
- this.owningEntity = owningEntity;
- this.rd = rd;
- this.revisionType = revisionType;
+ public FakeBidirectionalRelationWorkUnit(FakeBidirectionalRelationWorkUnit original,
+ Map<String, FakeRelationChange> fakeRelationChanges,
+ AuditWorkUnit nestedWorkUnit) {
+ super(original.sessionImplementor, original.entityName, original.verCfg, original.id);
+
+ this.fakeRelationChanges = fakeRelationChanges;
this.nestedWorkUnit = nestedWorkUnit;
}
public FakeBidirectionalRelationWorkUnit(FakeBidirectionalRelationWorkUnit original, AuditWorkUnit nestedWorkUnit) {
super(original.sessionImplementor, original.entityName, original.verCfg, original.id);
- this.owningEntity = original.owningEntity;
- this.rd = original.rd;
- this.revisionType = original.revisionType;
this.nestedWorkUnit = nestedWorkUnit;
+
+ fakeRelationChanges = new HashMap<String, FakeRelationChange>(original.getFakeRelationChanges());
}
public AuditWorkUnit getNestedWorkUnit() {
return nestedWorkUnit;
}
- public RevisionType getRevisionType() {
- return revisionType;
+ public Map<String, FakeRelationChange> getFakeRelationChanges() {
+ return fakeRelationChanges;
}
public boolean containsWork() {
@@ -64,11 +71,10 @@
// Making a defensive copy not to modify the data held by the nested work unit.
Map<String, Object> nestedData = new HashMap<String, Object>(nestedWorkUnit.generateData(revisionData));
- // Now adding data for the fake relation.
- // If the revision type is "DEL", it means that the object is removed from the collection. Then the
- // new owner will in fact be null.
- rd.getFakeBidirectionalRelationMapper().mapToMapFromEntity(sessionImplementor, nestedData,
- revisionType == RevisionType.DEL ? null : owningEntity, null);
+ // Now adding data for all fake relations.
+ for (FakeRelationChange fakeRelationChange : fakeRelationChanges.values()) {
+ fakeRelationChange.generateData(sessionImplementor, nestedData);
+ }
return nestedData;
}
@@ -90,19 +96,23 @@
}
public AuditWorkUnit merge(FakeBidirectionalRelationWorkUnit second) {
- /*
- * The merging rules are the following (revision types of the first and second work units):
- * - DEL, DEL - return any (the work units are the same)
- * - DEL, ADD - return ADD (points to new owner)
- * - ADD, DEL - return ADD (points to new owner)
- * - ADD, ADD - return second (points to newer owner)
- */
+ // First merging the nested work units.
+ AuditWorkUnit mergedNested = second.getNestedWorkUnit().dispatch(nestedWorkUnit);
- if (revisionType == RevisionType.DEL || second.getRevisionType() == RevisionType.ADD) {
- return second;
+ // Now merging the fake relation changes from both work units.
+ Map<String, FakeRelationChange> secondFakeRelationChanges = second.getFakeRelationChanges();
+ Map<String, FakeRelationChange> mergedFakeRelationChanges = new HashMap<String, FakeRelationChange>();
+ Set<String> allPropertyNames = new HashSet<String>(fakeRelationChanges.keySet());
+ allPropertyNames.addAll(secondFakeRelationChanges.keySet());
+
+ for (String propertyName : allPropertyNames) {
+ mergedFakeRelationChanges.put(propertyName,
+ FakeRelationChange.merge(
+ fakeRelationChanges.get(propertyName),
+ secondFakeRelationChanges.get(propertyName)));
}
- return this;
+ return new FakeBidirectionalRelationWorkUnit(this, mergedFakeRelationChanges, mergedNested);
}
public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
@@ -116,4 +126,48 @@
// Creating a new fake relation work unit with the nested merged data
return new FakeBidirectionalRelationWorkUnit(frwu, nestedMerged);
}
+
+ /**
+ * Describes a change to a single fake bidirectional relation.
+ */
+ private static class FakeRelationChange {
+ private final Object owningEntity;
+ private final RelationDescription rd;
+ private final RevisionType revisionType;
+
+ public FakeRelationChange(Object owningEntity, RelationDescription rd, RevisionType revisionType) {
+ this.owningEntity = owningEntity;
+ this.rd = rd;
+ this.revisionType = revisionType;
+ }
+
+ public RevisionType getRevisionType() {
+ return revisionType;
+ }
+
+ public void generateData(SessionImplementor sessionImplementor, Map<String, Object> data) {
+ // If the revision type is "DEL", it means that the object is removed from the collection. Then the
+ // new owner will in fact be null.
+ rd.getFakeBidirectionalRelationMapper().mapToMapFromEntity(sessionImplementor, data,
+ revisionType == RevisionType.DEL ? null : owningEntity, null);
+ }
+
+ public static FakeRelationChange merge(FakeRelationChange first, FakeRelationChange second) {
+ if (first == null) { return second; }
+ if (second == null) { return first; }
+
+ /*
+ * The merging rules are the following (revision types of the first and second changes):
+ * - DEL, DEL - return any (the work units are the same)
+ * - DEL, ADD - return ADD (points to new owner)
+ * - ADD, DEL - return ADD (points to new owner)
+ * - ADD, ADD - return second (points to newer owner)
+ */
+ if (first.getRevisionType() == RevisionType.DEL || second.getRevisionType() == RevisionType.ADD) {
+ return second;
+ } else {
+ return first;
+ }
+ }
+ }
}
Copied: core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefEdEntity1.java (from rev 18224, core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefEdEntity.java)
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefEdEntity1.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefEdEntity1.java 2009-12-15 12:27:01 UTC (rev 18225)
@@ -0,0 +1,85 @@
+package org.hibernate.envers.test.entities.onetomany.detached;
+
+import org.hibernate.envers.Audited;
+
+import javax.persistence.*;
+
+/**
+ * Entity for {@link org.hibernate.envers.test.integration.onetomany.detached.DoubleJoinColumnBidirectionalList} test.
+ * Owned side of the first relation.
+ * @author Adam Warski (adam at warski dot org)
+ */
+@Entity
+@Audited
+public class DoubleListJoinColumnBidirectionalRefEdEntity1 {
+ @Id
+ @GeneratedValue
+ private Integer id;
+
+ private String data;
+
+ @ManyToOne
+ @JoinColumn(name = "some_join_column_1", insertable = false, updatable = false)
+ private DoubleListJoinColumnBidirectionalRefIngEntity owner;
+
+ public DoubleListJoinColumnBidirectionalRefEdEntity1() { }
+
+ public DoubleListJoinColumnBidirectionalRefEdEntity1(Integer id, String data, DoubleListJoinColumnBidirectionalRefIngEntity owner) {
+ this.id = id;
+ this.data = data;
+ this.owner = owner;
+ }
+
+ public DoubleListJoinColumnBidirectionalRefEdEntity1(String data, DoubleListJoinColumnBidirectionalRefIngEntity owner) {
+ this.data = data;
+ this.owner = owner;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public DoubleListJoinColumnBidirectionalRefIngEntity getOwner() {
+ return owner;
+ }
+
+ public void setOwner(DoubleListJoinColumnBidirectionalRefIngEntity owner) {
+ this.owner = owner;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DoubleListJoinColumnBidirectionalRefEdEntity1)) return false;
+
+ DoubleListJoinColumnBidirectionalRefEdEntity1 that = (DoubleListJoinColumnBidirectionalRefEdEntity1) o;
+
+ if (data != null ? !data.equals(that.data) : that.data != null) return false;
+ //noinspection RedundantIfStatement
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (id != null ? id.hashCode() : 0);
+ result = 31 * result + (data != null ? data.hashCode() : 0);
+ return result;
+ }
+
+ public String toString() {
+ return "DoubleListJoinColumnBidirectionalRefIngEntity1(id = " + id + ", data = " + data + ")";
+ }
+}
\ No newline at end of file
Added: core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefEdEntity2.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefEdEntity2.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefEdEntity2.java 2009-12-15 12:27:01 UTC (rev 18225)
@@ -0,0 +1,85 @@
+package org.hibernate.envers.test.entities.onetomany.detached;
+
+import org.hibernate.envers.Audited;
+
+import javax.persistence.*;
+
+/**
+ * Entity for {@link org.hibernate.envers.test.integration.onetomany.detached.DoubleJoinColumnBidirectionalList} test.
+ * Owned side of the second relation.
+ * @author Adam Warski (adam at warski dot org)
+ */
+@Entity
+@Audited
+public class DoubleListJoinColumnBidirectionalRefEdEntity2 {
+ @Id
+ @GeneratedValue
+ private Integer id;
+
+ private String data;
+
+ @ManyToOne
+ @JoinColumn(name = "some_join_column_2", insertable = false, updatable = false)
+ private DoubleListJoinColumnBidirectionalRefIngEntity owner;
+
+ public DoubleListJoinColumnBidirectionalRefEdEntity2() { }
+
+ public DoubleListJoinColumnBidirectionalRefEdEntity2(Integer id, String data, DoubleListJoinColumnBidirectionalRefIngEntity owner) {
+ this.id = id;
+ this.data = data;
+ this.owner = owner;
+ }
+
+ public DoubleListJoinColumnBidirectionalRefEdEntity2(String data, DoubleListJoinColumnBidirectionalRefIngEntity owner) {
+ this.data = data;
+ this.owner = owner;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public DoubleListJoinColumnBidirectionalRefIngEntity getOwner() {
+ return owner;
+ }
+
+ public void setOwner(DoubleListJoinColumnBidirectionalRefIngEntity owner) {
+ this.owner = owner;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DoubleListJoinColumnBidirectionalRefEdEntity2)) return false;
+
+ DoubleListJoinColumnBidirectionalRefEdEntity2 that = (DoubleListJoinColumnBidirectionalRefEdEntity2) o;
+
+ if (data != null ? !data.equals(that.data) : that.data != null) return false;
+ //noinspection RedundantIfStatement
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (id != null ? id.hashCode() : 0);
+ result = 31 * result + (data != null ? data.hashCode() : 0);
+ return result;
+ }
+
+ public String toString() {
+ return "DoubleListJoinColumnBidirectionalRefIngEntity2(id = " + id + ", data = " + data + ")";
+ }
+}
\ No newline at end of file
Copied: core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefIngEntity.java (from rev 18224, core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefIngEntity.java)
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefIngEntity.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/DoubleListJoinColumnBidirectionalRefIngEntity.java 2009-12-15 12:27:01 UTC (rev 18225)
@@ -0,0 +1,100 @@
+package org.hibernate.envers.test.entities.onetomany.detached;
+
+import org.hibernate.envers.AuditMappedBy;
+import org.hibernate.envers.Audited;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Entity for {@link org.hibernate.envers.test.integration.onetomany.detached.DoubleJoinColumnBidirectionalList} test.
+ * Owning side of the relations.
+ * @author Adam Warski (adam at warski dot org)
+ */
+@Entity
+@Audited
+public class DoubleListJoinColumnBidirectionalRefIngEntity {
+ @Id
+ @GeneratedValue
+ private Integer id;
+
+ private String data;
+
+ @OneToMany
+ @JoinColumn(name = "some_join_column_1")
+ @AuditMappedBy(mappedBy = "owner")
+ private List<DoubleListJoinColumnBidirectionalRefEdEntity1> references1 = new ArrayList<DoubleListJoinColumnBidirectionalRefEdEntity1>();
+
+ @OneToMany
+ @JoinColumn(name = "some_join_column_2")
+ @AuditMappedBy(mappedBy = "owner")
+ private List<DoubleListJoinColumnBidirectionalRefEdEntity2> references2 = new ArrayList<DoubleListJoinColumnBidirectionalRefEdEntity2>();
+
+ public DoubleListJoinColumnBidirectionalRefIngEntity() { }
+
+ public DoubleListJoinColumnBidirectionalRefIngEntity(Integer id, String data) {
+ this.id = id;
+ this.data = data;
+ }
+
+ public DoubleListJoinColumnBidirectionalRefIngEntity(String data) {
+ this(null, data);
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public List<DoubleListJoinColumnBidirectionalRefEdEntity1> getReferences1() {
+ return references1;
+ }
+
+ public void setReferences1(List<DoubleListJoinColumnBidirectionalRefEdEntity1> references1) {
+ this.references1 = references1;
+ }
+
+ public List<DoubleListJoinColumnBidirectionalRefEdEntity2> getReferences2() {
+ return references2;
+ }
+
+ public void setReferences2(List<DoubleListJoinColumnBidirectionalRefEdEntity2> references2) {
+ this.references2 = references2;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof DoubleListJoinColumnBidirectionalRefIngEntity)) return false;
+
+ DoubleListJoinColumnBidirectionalRefIngEntity that = (DoubleListJoinColumnBidirectionalRefIngEntity) o;
+
+ if (data != null ? !data.equals(that.data) : that.data != null) return false;
+ //noinspection RedundantIfStatement
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (id != null ? id.hashCode() : 0);
+ result = 31 * result + (data != null ? data.hashCode() : 0);
+ return result;
+ }
+
+ public String toString() {
+ return "DoubleListJoinColumnBidirectionalRefIngEntity(id = " + id + ", data = " + data + ")";
+ }
+}
\ No newline at end of file
Copied: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/DoubleJoinColumnBidirectionalList.java (from rev 18224, core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java)
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/DoubleJoinColumnBidirectionalList.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/DoubleJoinColumnBidirectionalList.java 2009-12-15 12:27:01 UTC (rev 18225)
@@ -0,0 +1,299 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.envers.test.integration.onetomany.detached;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.envers.test.AbstractEntityTest;
+import org.hibernate.envers.test.entities.onetomany.detached.DoubleListJoinColumnBidirectionalRefEdEntity1;
+import org.hibernate.envers.test.entities.onetomany.detached.DoubleListJoinColumnBidirectionalRefEdEntity2;
+import org.hibernate.envers.test.entities.onetomany.detached.DoubleListJoinColumnBidirectionalRefIngEntity;
+import static org.hibernate.envers.test.tools.TestTools.checkList;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * Test for a double "fake" bidirectional mapping where one side uses @OneToMany+@JoinColumn
+ * (and thus owns the relation), and the other uses a @ManyToOne(insertable=false, updatable=false).
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class DoubleJoinColumnBidirectionalList extends AbstractEntityTest {
+ private Integer ed1_1_id;
+ private Integer ed2_1_id;
+ private Integer ed1_2_id;
+ private Integer ed2_2_id;
+
+ private Integer ing1_id;
+ private Integer ing2_id;
+
+ public void configure(Ejb3Configuration cfg) {
+ cfg.addAnnotatedClass(DoubleListJoinColumnBidirectionalRefIngEntity.class);
+ cfg.addAnnotatedClass(DoubleListJoinColumnBidirectionalRefEdEntity1.class);
+ cfg.addAnnotatedClass(DoubleListJoinColumnBidirectionalRefEdEntity2.class);
+ }
+
+ @Test(enabled = true)
+ public void createData() {
+ EntityManager em = getEntityManager();
+
+ DoubleListJoinColumnBidirectionalRefEdEntity1 ed1_1 = new DoubleListJoinColumnBidirectionalRefEdEntity1("ed1_1", null);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 ed1_2 = new DoubleListJoinColumnBidirectionalRefEdEntity1("ed1_2", null);
+
+ DoubleListJoinColumnBidirectionalRefEdEntity2 ed2_1 = new DoubleListJoinColumnBidirectionalRefEdEntity2("ed2_1", null);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 ed2_2 = new DoubleListJoinColumnBidirectionalRefEdEntity2("ed2_2", null);
+
+ DoubleListJoinColumnBidirectionalRefIngEntity ing1 = new DoubleListJoinColumnBidirectionalRefIngEntity("coll1");
+ DoubleListJoinColumnBidirectionalRefIngEntity ing2 = new DoubleListJoinColumnBidirectionalRefIngEntity("coll2");
+
+ // Revision 1 (ing1: ed1_1, ed2_1, ing2: ed1_2, ed2_2)
+ em.getTransaction().begin();
+
+ ing1.getReferences1().add(ed1_1);
+ ing1.getReferences2().add(ed2_1);
+
+ ing2.getReferences1().add(ed1_2);
+ ing2.getReferences2().add(ed2_2);
+
+ em.persist(ed1_1);
+ em.persist(ed1_2);
+ em.persist(ed2_1);
+ em.persist(ed2_2);
+ em.persist(ing1);
+ em.persist(ing2);
+
+ em.getTransaction().commit();
+
+ // Revision 2 (ing1: ed1_1, ed1_2, ed2_1, ed2_2)
+ em.getTransaction().begin();
+
+ ing1 = em.find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
+ ing2 = em.find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
+ ed1_1 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_1.getId());
+ ed1_2 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2.getId());
+ ed2_1 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1.getId());
+ ed2_2 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_2.getId());
+
+ ing2.getReferences1().clear();
+ ing2.getReferences2().clear();
+
+ ing1.getReferences1().add(ed1_2);
+ ing1.getReferences2().add(ed2_2);
+
+ em.getTransaction().commit();
+ em.clear();
+
+ // Revision 3 (ing1: ed1_1, ed1_2, ed2_1, ed2_2)
+ em.getTransaction().begin();
+
+ ing1 = em.find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
+ ing2 = em.find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
+ ed1_1 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_1.getId());
+ ed1_2 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2.getId());
+ ed2_1 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1.getId());
+ ed2_2 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_2.getId());
+
+ ed1_1.setData("ed1_1 bis");
+ ed2_2.setData("ed2_2 bis");
+
+ em.getTransaction().commit();
+ em.clear();
+
+ // Revision 4 (ing1: ed2_2, ing2: ed2_1, ed1_1, ed1_2)
+ em.getTransaction().begin();
+
+ ing1 = em.find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
+ ing2 = em.find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
+ ed1_1 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_1.getId());
+ ed1_2 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2.getId());
+ ed2_1 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1.getId());
+ ed2_2 = em.find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_2.getId());
+
+ ing1.getReferences1().clear();
+ ing2.getReferences1().add(ed1_1);
+ ing2.getReferences1().add(ed1_2);
+
+ ing1.getReferences2().remove(ed2_1);
+ ing2.getReferences2().add(ed2_1);
+
+ em.getTransaction().commit();
+ em.clear();
+
+ //
+
+ ing1_id = ing1.getId();
+ ing2_id = ing2.getId();
+
+ ed1_1_id = ed1_1.getId();
+ ed1_2_id = ed1_2.getId();
+ ed2_1_id = ed2_1.getId();
+ ed2_2_id = ed2_2.getId();
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testRevisionsCounts() {
+ assertEquals(Arrays.asList(1, 2, 4), getAuditReader().getRevisions(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1_id));
+ assertEquals(Arrays.asList(1, 2, 4), getAuditReader().getRevisions(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2_id));
+
+ assertEquals(Arrays.asList(1, 3, 4), getAuditReader().getRevisions(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_1_id));
+ assertEquals(Arrays.asList(1, 2, 4), getAuditReader().getRevisions(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2_id));
+
+ assertEquals(Arrays.asList(1, 4), getAuditReader().getRevisions(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1_id));
+ assertEquals(Arrays.asList(1, 2, 3), getAuditReader().getRevisions(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_2_id));
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfIng1() {
+ DoubleListJoinColumnBidirectionalRefEdEntity1 ed1_1_fromRev1 = new DoubleListJoinColumnBidirectionalRefEdEntity1(ed1_1_id, "ed1_1", null);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 ed1_1_fromRev3 = new DoubleListJoinColumnBidirectionalRefEdEntity1(ed1_1_id, "ed1_1 bis", null);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 ed1_2 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2_id);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 ed2_1 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1_id);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 ed2_2_fromRev1 = new DoubleListJoinColumnBidirectionalRefEdEntity2(ed2_2_id, "ed2_2", null);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 ed2_2_fromRev3 = new DoubleListJoinColumnBidirectionalRefEdEntity2(ed2_2_id, "ed2_2 bis", null);
+
+ DoubleListJoinColumnBidirectionalRefIngEntity rev1 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 1);
+ DoubleListJoinColumnBidirectionalRefIngEntity rev2 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 2);
+ DoubleListJoinColumnBidirectionalRefIngEntity rev3 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 3);
+ DoubleListJoinColumnBidirectionalRefIngEntity rev4 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 4);
+
+ assertTrue(checkList(rev1.getReferences1(), ed1_1_fromRev1));
+ assertTrue(checkList(rev2.getReferences1(), ed1_1_fromRev1, ed1_2));
+ assertTrue(checkList(rev3.getReferences1(), ed1_1_fromRev3, ed1_2));
+ assertTrue(checkList(rev4.getReferences1()));
+
+ assertTrue(checkList(rev1.getReferences2(), ed2_1));
+ assertTrue(checkList(rev2.getReferences2(), ed2_1, ed2_2_fromRev1));
+ assertTrue(checkList(rev3.getReferences2(), ed2_1, ed2_2_fromRev3));
+ assertTrue(checkList(rev4.getReferences2(), ed2_2_fromRev3));
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfIng2() {
+ DoubleListJoinColumnBidirectionalRefEdEntity1 ed1_1_fromRev3 = new DoubleListJoinColumnBidirectionalRefEdEntity1(ed1_1_id, "ed1_1 bis", null);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 ed1_2 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2_id);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 ed2_1 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1_id);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 ed2_2_fromRev1 = new DoubleListJoinColumnBidirectionalRefEdEntity2(ed2_2_id, "ed2_2", null);
+
+ DoubleListJoinColumnBidirectionalRefIngEntity rev1 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 1);
+ DoubleListJoinColumnBidirectionalRefIngEntity rev2 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 2);
+ DoubleListJoinColumnBidirectionalRefIngEntity rev3 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 3);
+ DoubleListJoinColumnBidirectionalRefIngEntity rev4 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 4);
+
+ assertTrue(checkList(rev1.getReferences1(), ed1_2));
+ assertTrue(checkList(rev2.getReferences1()));
+ assertTrue(checkList(rev3.getReferences1()));
+ assertTrue(checkList(rev4.getReferences1(), ed1_1_fromRev3, ed1_2));
+
+ assertTrue(checkList(rev1.getReferences2(), ed2_2_fromRev1));
+ assertTrue(checkList(rev2.getReferences2()));
+ assertTrue(checkList(rev3.getReferences2()));
+ assertTrue(checkList(rev4.getReferences2(), ed2_1));
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfEd1_1() {
+ DoubleListJoinColumnBidirectionalRefIngEntity ing1 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1_id);
+ DoubleListJoinColumnBidirectionalRefIngEntity ing2 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2_id);
+
+ DoubleListJoinColumnBidirectionalRefEdEntity1 rev1 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_1_id, 1);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 rev2 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_1_id, 2);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 rev3 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_1_id, 3);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 rev4 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_1_id, 4);
+
+ assertTrue(rev1.getOwner().equals(ing1));
+ assertTrue(rev2.getOwner().equals(ing1));
+ assertTrue(rev3.getOwner().equals(ing1));
+ assertTrue(rev4.getOwner().equals(ing2));
+
+ assertEquals(rev1.getData(), "ed1_1");
+ assertEquals(rev2.getData(), "ed1_1");
+ assertEquals(rev3.getData(), "ed1_1 bis");
+ assertEquals(rev4.getData(), "ed1_1 bis");
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfEd1_2() {
+ DoubleListJoinColumnBidirectionalRefIngEntity ing1 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1_id);
+ DoubleListJoinColumnBidirectionalRefIngEntity ing2 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2_id);
+
+ DoubleListJoinColumnBidirectionalRefEdEntity1 rev1 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2_id, 1);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 rev2 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2_id, 2);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 rev3 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2_id, 3);
+ DoubleListJoinColumnBidirectionalRefEdEntity1 rev4 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity1.class, ed1_2_id, 4);
+
+ assertTrue(rev1.getOwner().equals(ing2));
+ assertTrue(rev2.getOwner().equals(ing1));
+ assertTrue(rev3.getOwner().equals(ing1));
+ assertTrue(rev4.getOwner().equals(ing2));
+
+ assertEquals(rev1.getData(), "ed1_2");
+ assertEquals(rev2.getData(), "ed1_2");
+ assertEquals(rev3.getData(), "ed1_2");
+ assertEquals(rev4.getData(), "ed1_2");
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfEd2_1() {
+ DoubleListJoinColumnBidirectionalRefIngEntity ing1 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1_id);
+ DoubleListJoinColumnBidirectionalRefIngEntity ing2 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2_id);
+
+ DoubleListJoinColumnBidirectionalRefEdEntity2 rev1 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1_id, 1);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 rev2 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1_id, 2);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 rev3 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1_id, 3);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 rev4 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_1_id, 4);
+
+ assertTrue(rev1.getOwner().equals(ing1));
+ assertTrue(rev2.getOwner().equals(ing1));
+ assertTrue(rev3.getOwner().equals(ing1));
+ assertTrue(rev4.getOwner().equals(ing2));
+
+ assertEquals(rev1.getData(), "ed2_1");
+ assertEquals(rev2.getData(), "ed2_1");
+ assertEquals(rev3.getData(), "ed2_1");
+ assertEquals(rev4.getData(), "ed2_1");
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfEd2_2() {
+ DoubleListJoinColumnBidirectionalRefIngEntity ing1 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing1_id);
+ DoubleListJoinColumnBidirectionalRefIngEntity ing2 = getEntityManager().find(DoubleListJoinColumnBidirectionalRefIngEntity.class, ing2_id);
+
+ DoubleListJoinColumnBidirectionalRefEdEntity2 rev1 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_2_id, 1);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 rev2 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_2_id, 2);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 rev3 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_2_id, 3);
+ DoubleListJoinColumnBidirectionalRefEdEntity2 rev4 = getAuditReader().find(DoubleListJoinColumnBidirectionalRefEdEntity2.class, ed2_2_id, 4);
+
+ assertTrue(rev1.getOwner().equals(ing2));
+ assertTrue(rev2.getOwner().equals(ing1));
+ assertTrue(rev3.getOwner().equals(ing1));
+ assertTrue(rev4.getOwner().equals(ing1));
+
+ assertEquals(rev1.getData(), "ed2_2");
+ assertEquals(rev2.getData(), "ed2_2");
+ assertEquals(rev3.getData(), "ed2_2 bis");
+ assertEquals(rev4.getData(), "ed2_2 bis");
+ }
+}
\ No newline at end of file
15 years
Hibernate SVN: r18224 - in core/trunk/envers: src/main/java/org/hibernate/envers and 9 other directories.
by hibernate-commits@lists.jboss.org
Author: adamw
Date: 2009-12-15 05:50:11 -0500 (Tue, 15 Dec 2009)
New Revision: 18224
Added:
core/trunk/envers/src/main/java/org/hibernate/envers/AuditMappedBy.java
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/ClassesAuditingData.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/FakeBidirectionalRelationWorkUnit.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefEdEntity.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefIngEntity.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java
Modified:
core/trunk/envers/pom.xml
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/EntitiesConfigurator.java
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/ClassAuditingData.java
core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java
core/trunk/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java
core/trunk/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java
core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AbstractAuditWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AddWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AuditWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/CollectionChangeWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/DelWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/ModWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeVisitor.java
core/trunk/envers/src/main/java/org/hibernate/envers/tools/MappingTools.java
Log:
HHH-4694:
- adding support for "fake" bidirectional many-to-one relations using an experimental @AuditMappedBy annotation
- adding a new work unit, extracting common work unit code
- upgrading TestNG to 5.10
Modified: core/trunk/envers/pom.xml
===================================================================
--- core/trunk/envers/pom.xml 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/pom.xml 2009-12-15 10:50:11 UTC (rev 18224)
@@ -95,7 +95,7 @@
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
- <version>5.8</version>
+ <version>5.10</version>
<classifier>jdk15</classifier>
<scope>test</scope>
</dependency>
Added: core/trunk/envers/src/main/java/org/hibernate/envers/AuditMappedBy.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/AuditMappedBy.java (rev 0)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/AuditMappedBy.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -0,0 +1,37 @@
+package org.hibernate.envers;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * <p>
+ * Annotation to specify a "fake" bi-directional relation. Such a relation uses {@code @OneToMany} +
+ * {@code @JoinColumn} on the one side, and {@code @ManyToOne} + {@code @Column(insertable=false, updatable=false)} on
+ * the many side. Then, Envers won't use a join table to audit this relation, but will store changes as in a normal
+ * bi-directional relation.
+ * </p>
+ *
+ * <p>
+ * This annotation is <b>experimental</b> and may change in future releases.
+ * </p>
+ *
+ * @author Adam Warski (adam at warski dot org)
+ */
+(a)Retention(RetentionPolicy.RUNTIME)
+(a)Target({ElementType.METHOD, ElementType.FIELD})
+public @interface AuditMappedBy {
+ /**
+ * @return Name of the property in the related entity which maps back to this entity. The property should be
+ * mapped with {@code @ManyToOne} and {@code @Column(insertable=false, updatable=false)}.
+ */
+ String mappedBy();
+
+ /**
+ * @return Name of the property in the related entity which maps to the position column. Should be specified only
+ * for indexed collection, when @{@link org.hibernate.annotations.IndexColumn} is used on the collection.
+ * The property should be mapped with {@code @Column(insertable=false, updatable=false)}.
+ */
+ String positionMappedBy() default "";
+}
Added: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/ClassesAuditingData.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/ClassesAuditingData.java (rev 0)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/ClassesAuditingData.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -0,0 +1,43 @@
+package org.hibernate.envers.configuration;
+
+import org.hibernate.envers.configuration.metadata.reader.ClassAuditingData;
+import org.hibernate.mapping.PersistentClass;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+
+/**
+ * A helper class holding auditing meta-data for all persistent classes.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class ClassesAuditingData {
+ private final Map<String, ClassAuditingData> entityNameToAuditingData = new HashMap<String, ClassAuditingData>();
+ private final Map<PersistentClass, ClassAuditingData> persistentClassToAuditingData = new LinkedHashMap<PersistentClass, ClassAuditingData>();
+
+ /**
+ * Stores information about auditing meta-data for the given class.
+ * @param pc Persistent class.
+ * @param cad Auditing meta-data for the given class.
+ */
+ public void addClassAuditingData(PersistentClass pc, ClassAuditingData cad) {
+ entityNameToAuditingData.put(pc.getEntityName(), cad);
+ persistentClassToAuditingData.put(pc, cad);
+ }
+
+ /**
+ * @return A collection of all auditing meta-data for persistent classes.
+ */
+ public Collection<Map.Entry<PersistentClass, ClassAuditingData>> getAllClassAuditedData() {
+ return persistentClassToAuditingData.entrySet();
+ }
+
+ /**
+ * @param entityName Name of the entity.
+ * @return Auditing meta-data for the given entity.
+ */
+ public ClassAuditingData getClassAuditingData(String entityName) {
+ return entityNameToAuditingData.get(entityName);
+ }
+}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/EntitiesConfigurator.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/EntitiesConfigurator.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/EntitiesConfigurator.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -60,30 +60,36 @@
Document revisionInfoXmlMapping, Element revisionInfoRelationMapping) {
// Creating a name register to capture all audit entity names created.
AuditEntityNameRegister auditEntityNameRegister = new AuditEntityNameRegister();
-
- AuditMetadataGenerator auditMetaGen = new AuditMetadataGenerator(cfg, globalCfg, verEntCfg,
- revisionInfoRelationMapping, auditEntityNameRegister);
DOMWriter writer = new DOMWriter();
// Sorting the persistent class topologically - superclass always before subclass
Iterator<PersistentClass> classes = GraphTopologicalSort.sort(new PersistentClassGraphDefiner(cfg)).iterator();
- Map<PersistentClass, ClassAuditingData> pcDatas =
- new HashMap<PersistentClass, ClassAuditingData>();
+ ClassesAuditingData classesAuditingData = new ClassesAuditingData();
Map<PersistentClass, EntityXmlMappingData> xmlMappings = new HashMap<PersistentClass, EntityXmlMappingData>();
- // First pass
+ // Reading metadata from annotations
while (classes.hasNext()) {
PersistentClass pc = classes.next();
+
// Collecting information from annotations on the persistent class pc
AnnotationsMetadataReader annotationsMetadataReader =
new AnnotationsMetadataReader(globalCfg, reflectionManager, pc);
ClassAuditingData auditData = annotationsMetadataReader.getAuditData();
+ classesAuditingData.addClassAuditingData(pc, auditData);
+ }
+
+ AuditMetadataGenerator auditMetaGen = new AuditMetadataGenerator(cfg, globalCfg, verEntCfg,
+ revisionInfoRelationMapping, auditEntityNameRegister, classesAuditingData);
+
+ // First pass
+ for (Map.Entry<PersistentClass, ClassAuditingData> pcDatasEntry : classesAuditingData.getAllClassAuditedData()) {
+ PersistentClass pc = pcDatasEntry.getKey();
+ ClassAuditingData auditData = pcDatasEntry.getValue();
+
EntityXmlMappingData xmlMappingData = new EntityXmlMappingData();
if (auditData.isAudited()) {
- pcDatas.put(pc, auditData);
-
if (!StringTools.isEmpty(auditData.getAuditTable().value())) {
verEntCfg.addCustomAuditTableName(pc.getEntityName(), auditData.getAuditTable().value());
}
@@ -97,31 +103,29 @@
}
// Second pass
- for (Map.Entry<PersistentClass, ClassAuditingData> pcDatasEntry : pcDatas.entrySet()) {
+ for (Map.Entry<PersistentClass, ClassAuditingData> pcDatasEntry : classesAuditingData.getAllClassAuditedData()) {
EntityXmlMappingData xmlMappingData = xmlMappings.get(pcDatasEntry.getKey());
- auditMetaGen.generateSecondPass(pcDatasEntry.getKey(), pcDatasEntry.getValue(), xmlMappingData);
+ if (pcDatasEntry.getValue().isAudited()) {
+ auditMetaGen.generateSecondPass(pcDatasEntry.getKey(), pcDatasEntry.getValue(), xmlMappingData);
+ try {
+ cfg.addDocument(writer.write(xmlMappingData.getMainXmlMapping()));
+ //writeDocument(xmlMappingData.getMainXmlMapping());
- try {
- cfg.addDocument(writer.write(xmlMappingData.getMainXmlMapping()));
- // TODO
- //writeDocument(xmlMappingData.getMainXmlMapping());
-
- for (Document additionalMapping : xmlMappingData.getAdditionalXmlMappings()) {
- cfg.addDocument(writer.write(additionalMapping));
- // TODO
- //writeDocument(additionalMapping);
+ for (Document additionalMapping : xmlMappingData.getAdditionalXmlMappings()) {
+ cfg.addDocument(writer.write(additionalMapping));
+ //writeDocument(additionalMapping);
+ }
+ } catch (DocumentException e) {
+ throw new MappingException(e);
}
- } catch (DocumentException e) {
- throw new MappingException(e);
}
}
// Only if there are any versioned classes
- if (pcDatas.size() > 0) {
+ if (classesAuditingData.getAllClassAuditedData().size() > 0) {
try {
if (revisionInfoXmlMapping != null) {
- // TODO
//writeDocument(revisionInfoXmlMapping);
cfg.addDocument(writer.write(revisionInfoXmlMapping));
}
@@ -134,7 +138,7 @@
auditMetaGen.getNotAuditedEntitiesConfigurations());
}
- // todo
+ @SuppressWarnings({"UnusedDeclaration"})
private void writeDocument(Document e) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Writer w = new PrintWriter(baos);
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/AuditMetadataGenerator.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -30,6 +30,7 @@
import org.dom4j.Element;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.configuration.AuditEntitiesConfiguration;
+import org.hibernate.envers.configuration.ClassesAuditingData;
import org.hibernate.envers.configuration.metadata.reader.ClassAuditingData;
import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData;
import org.hibernate.envers.entities.EntityConfiguration;
@@ -62,15 +63,22 @@
private final AuditEntitiesConfiguration verEntCfg;
private final Element revisionInfoRelationMapping;
+ /*
+ * Generators for different kinds of property values/types.
+ */
private final BasicMetadataGenerator basicMetadataGenerator;
private final ComponentMetadataGenerator componentMetadataGenerator;
private final IdMetadataGenerator idMetadataGenerator;
private final ToOneRelationMetadataGenerator toOneRelationMetadataGenerator;
+ /*
+ * Here information about already generated mappings will be accumulated.
+ */
private final Map<String, EntityConfiguration> entitiesConfigurations;
private final Map<String, EntityConfiguration> notAuditedEntitiesConfigurations;
private final AuditEntityNameRegister auditEntityNameRegister;
+ private final ClassesAuditingData classesAuditingData;
// Map entity name -> (join descriptor -> element describing the "versioned" join)
private final Map<String, Map<Join, Element>> entitiesJoins;
@@ -78,7 +86,8 @@
public AuditMetadataGenerator(Configuration cfg, GlobalConfiguration globalCfg,
AuditEntitiesConfiguration verEntCfg,
Element revisionInfoRelationMapping,
- AuditEntityNameRegister auditEntityNameRegister) {
+ AuditEntityNameRegister auditEntityNameRegister,
+ ClassesAuditingData classesAuditingData) {
this.cfg = cfg;
this.globalCfg = globalCfg;
this.verEntCfg = verEntCfg;
@@ -89,7 +98,8 @@
this.idMetadataGenerator = new IdMetadataGenerator(this);
this.toOneRelationMetadataGenerator = new ToOneRelationMetadataGenerator(this);
- this.auditEntityNameRegister = auditEntityNameRegister;
+ this.auditEntityNameRegister = auditEntityNameRegister;
+ this.classesAuditingData = classesAuditingData;
entitiesConfigurations = new HashMap<String, EntityConfiguration>();
notAuditedEntitiesConfigurations = new HashMap<String, EntityConfiguration>();
@@ -448,6 +458,10 @@
return entitiesConfigurations;
}
+ public ClassesAuditingData getClassesAuditingData() {
+ return classesAuditingData;
+ }
+
// Getters for generators and configuration
BasicMetadataGenerator getBasicMetadataGenerator() {
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -40,13 +40,11 @@
import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData;
import org.hibernate.envers.entities.EntityConfiguration;
import org.hibernate.envers.entities.IdMappingData;
+import org.hibernate.envers.entities.PropertyData;
import org.hibernate.envers.entities.mapper.CompositeMapperBuilder;
-import org.hibernate.envers.entities.mapper.relation.BasicCollectionMapper;
-import org.hibernate.envers.entities.mapper.relation.CommonCollectionMapperData;
-import org.hibernate.envers.entities.mapper.relation.ListCollectionMapper;
-import org.hibernate.envers.entities.mapper.relation.MapCollectionMapper;
-import org.hibernate.envers.entities.mapper.relation.MiddleComponentData;
-import org.hibernate.envers.entities.mapper.relation.MiddleIdData;
+import org.hibernate.envers.entities.mapper.PropertyMapper;
+import org.hibernate.envers.entities.mapper.id.IdMapper;
+import org.hibernate.envers.entities.mapper.relation.*;
import org.hibernate.envers.entities.mapper.relation.component.MiddleDummyComponentMapper;
import org.hibernate.envers.entities.mapper.relation.component.MiddleMapKeyIdComponentMapper;
import org.hibernate.envers.entities.mapper.relation.component.MiddleMapKeyPropertyComponentMapper;
@@ -61,6 +59,7 @@
import org.hibernate.envers.entities.mapper.relation.query.RelationQueryGenerator;
import org.hibernate.envers.tools.StringTools;
import org.hibernate.envers.tools.Tools;
+import org.hibernate.envers.tools.MappingTools;
import org.hibernate.MappingException;
import org.hibernate.mapping.Collection;
@@ -79,12 +78,16 @@
import org.hibernate.type.SortedMapType;
import org.hibernate.type.SortedSetType;
import org.hibernate.type.Type;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Generates metadata for a collection-valued property.
* @author Adam Warski (adam at warski dot org)
*/
public final class CollectionMetadataGenerator {
+ private static final Logger log = LoggerFactory.getLogger(CollectionMetadataGenerator.class);
+
private final AuditMetadataGenerator mainGenerator;
private final String propertyName;
private final Collection propertyValue;
@@ -145,10 +148,13 @@
void addCollection() {
Type type = propertyValue.getType();
- if ((type instanceof BagType || type instanceof SetType || type instanceof MapType || type instanceof ListType) &&
- (propertyValue.getElement() instanceof OneToMany) && (propertyValue.isInverse())) {
+ boolean oneToManyAttachedType = type instanceof BagType || type instanceof SetType || type instanceof MapType || type instanceof ListType;
+ boolean inverseOneToMany = (propertyValue.getElement() instanceof OneToMany) && (propertyValue.isInverse());
+ boolean fakeOneToManyBidirectional = (propertyValue.getElement() instanceof OneToMany) && (propertyAuditingData.getAuditMappedBy() != null);
+
+ if (oneToManyAttachedType && (inverseOneToMany || fakeOneToManyBidirectional)) {
// A one-to-many relation mapped using @ManyToOne and @OneToMany(mappedBy="...")
- addOneToManyAttached();
+ addOneToManyAttached(fakeOneToManyBidirectional);
} else {
// All other kinds of relations require a middle (join) table.
addWithMiddleTable();
@@ -161,7 +167,10 @@
}
@SuppressWarnings({"unchecked"})
- private void addOneToManyAttached() {
+ private void addOneToManyAttached(boolean fakeOneToManyBidirectional) {
+ log.debug("Adding audit mapping for property " + referencingEntityName + "." + propertyName +
+ ": one-to-many collection, using a join column on the referenced entity.");
+
String mappedBy = getMappedBy(propertyValue);
IdMappingData referencedIdMapping = mainGenerator.getReferencedIdMappingData(referencingEntityName,
@@ -200,9 +209,29 @@
// Checking the type of the collection and adding an appropriate mapper.
addMapper(commonCollectionMapperData, elementComponentData, indexComponentData);
+ PropertyMapper fakeBidirectionalRelationMapper;
+ if (fakeOneToManyBidirectional) {
+ // In case of a fake many-to-one bidirectional relation, we have to generate a mapper which maps
+ // the mapped-by property name to the id of the related entity (which is the owner of the collection).
+ String auditMappedBy = propertyAuditingData.getAuditMappedBy();
+
+ // Creating a prefixed relation mapper.
+ IdMapper relMapper = referencingIdMapping.getIdMapper().prefixMappedProperties(
+ MappingTools.createToOneRelationPrefix(auditMappedBy));
+
+ fakeBidirectionalRelationMapper = new ToOneIdMapper(
+ relMapper,
+ // The mapper will only be used to map from entity to map, so no need to provide other details
+ // when constructing the PropertyData.
+ new PropertyData(auditMappedBy, null, null, null),
+ referencedEntityName);
+ } else {
+ fakeBidirectionalRelationMapper = null;
+ }
+
// Storing information about this relation.
referencingEntityConfiguration.addToManyNotOwningRelation(propertyName, mappedBy,
- referencedEntityName, referencingIdData.getPrefixedMapper());
+ referencedEntityName, referencingIdData.getPrefixedMapper(), fakeBidirectionalRelationMapper);
}
/**
@@ -237,6 +266,9 @@
@SuppressWarnings({"unchecked"})
private void addWithMiddleTable() {
+ log.debug("Adding audit mapping for property " + referencingEntityName + "." + propertyName +
+ ": collection with a join table.");
+
// Generating the name of the middle table
String auditMiddleTableName;
String auditMiddleEntityName;
@@ -249,6 +281,8 @@
auditMiddleEntityName = mainGenerator.getVerEntCfg().getAuditEntityName(middleTableName);
}
+ log.debug("Using join table name: " + auditMiddleTableName);
+
// Generating the XML mapping for the middle entity, only if the relation isn't inverse.
// If the relation is inverse, will be later checked by comparing middleEntityXml with null.
Element middleEntityXml;
@@ -413,8 +447,8 @@
} else {
// Last but one parameter: collection components are always insertable
boolean mapped = mainGenerator.getBasicMetadataGenerator().addBasic(xmlMapping,
- new PropertyAuditingData(prefix, "field", ModificationStore.FULL, RelationTargetAuditMode.AUDITED), value, null,
- true, true);
+ new PropertyAuditingData(prefix, "field", ModificationStore.FULL, RelationTargetAuditMode.AUDITED, null, null),
+ value, null, true, true);
if (mapped) {
// Simple values are always stored in the first item of the array returned by the query generator.
@@ -499,11 +533,35 @@
return middleEntityXmlId;
}
+ private String getMappedByCommon(PersistentClass referencedClass) {
+ // If there's an @AuditMappedBy specified, returning it directly.
+ String auditMappedBy = propertyAuditingData.getAuditMappedBy();
+ if (auditMappedBy != null) {
+ // Checking that the property exists.
+ try {
+ referencedClass.getProperty(auditMappedBy);
+ } catch (MappingException me) {
+ throw new MappingException("@AuditMappedBy points to a property that can be read: " +
+ referencedClass.getEntityName() + "." + auditMappedBy, me);
+ }
+
+ return auditMappedBy;
+ }
+
+ return null;
+ }
+
@SuppressWarnings({"unchecked"})
private String getMappedBy(Collection collectionValue) {
- Iterator<Property> assocClassProps =
- ((OneToMany) collectionValue.getElement()).getAssociatedClass().getPropertyIterator();
+ PersistentClass referencedClass = ((OneToMany) collectionValue.getElement()).getAssociatedClass();
+ String mappedByCommon = getMappedByCommon(referencedClass);
+ if (mappedByCommon != null) {
+ return mappedByCommon;
+ }
+
+ Iterator<Property> assocClassProps = referencedClass.getPropertyIterator();
+
while (assocClassProps.hasNext()) {
Property property = assocClassProps.next();
@@ -519,6 +577,11 @@
@SuppressWarnings({"unchecked"})
private String getMappedBy(Table collectionTable, PersistentClass referencedClass) {
+ String mappedByCommon = getMappedByCommon(referencedClass);
+ if (mappedByCommon != null) {
+ return mappedByCommon;
+ }
+
Iterator<Property> properties = referencedClass.getPropertyIterator();
while (properties.hasNext()) {
Property property = properties.next();
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -139,6 +139,6 @@
private PropertyAuditingData getIdPersistentPropertyAuditingData(Property property) {
return new PropertyAuditingData(property.getName(), property.getPropertyAccessorName(),
- ModificationStore.FULL, RelationTargetAuditMode.AUDITED);
+ ModificationStore.FULL, RelationTargetAuditMode.AUDITED, null, null);
}
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/ToOneRelationMetadataGenerator.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -32,17 +32,23 @@
import org.hibernate.envers.entities.mapper.relation.OneToOneNotOwningMapper;
import org.hibernate.envers.entities.mapper.relation.ToOneIdMapper;
import org.hibernate.envers.configuration.metadata.reader.PropertyAuditingData;
+import org.hibernate.envers.configuration.metadata.reader.ClassAuditingData;
+import org.hibernate.envers.tools.MappingTools;
import org.hibernate.MappingException;
import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* Generates metadata for to-one relations (reference-valued properties).
* @author Adam Warski (adam at warski dot org)
*/
public final class ToOneRelationMetadataGenerator {
+ private static final Logger log = LoggerFactory.getLogger(ToOneRelationMetadataGenerator.class);
+
private final AuditMetadataGenerator mainGenerator;
ToOneRelationMetadataGenerator(AuditMetadataGenerator auditMetadataGenerator) {
@@ -57,7 +63,7 @@
IdMappingData idMapping = mainGenerator.getReferencedIdMappingData(entityName, referencedEntityName,
propertyAuditingData, true);
- String lastPropertyPrefix = propertyAuditingData.getName() + "_";
+ String lastPropertyPrefix = MappingTools.createToOneRelationPrefix(propertyAuditingData.getName());
// Generating the id mapper for the relation
IdMapper relMapper = idMapping.getIdMapper().prefixMappedProperties(lastPropertyPrefix);
@@ -66,6 +72,30 @@
mainGenerator.getEntitiesConfigurations().get(entityName).addToOneRelation(
propertyAuditingData.getName(), referencedEntityName, relMapper);
+ // If the property isn't insertable, checking if this is not a "fake" bidirectional many-to-one relationship,
+ // that is, when the one side owns the relation (and is a collection), and the many side is non insertable.
+ // When that's the case and the user specified to store this relation without a middle table (using
+ // @AuditMappedBy), we have to make the property insertable for the purposes of Envers. In case of changes to
+ // the entity that didn't involve the relation, it's value will then be stored properly. In case of changes
+ // to the entity that did involve the relation, it's the responsibility of the collection side to store the
+ // proper data.
+ if (!insertable) {
+ ClassAuditingData referencedAuditingData = mainGenerator.getClassesAuditingData().getClassAuditingData(referencedEntityName);
+
+ // Looking through the properties of the referenced entity to find the right property.
+ for (String referencedPropertyName : referencedAuditingData.getPropertyNames()) {
+ String auditMappedBy = referencedAuditingData.getPropertyAuditingData(referencedPropertyName).getAuditMappedBy();
+ if (propertyAuditingData.getName().equals(auditMappedBy)) {
+ log.debug("Non-insertable property " + entityName + "." + propertyAuditingData.getName() +
+ " will be made insertable because a matching @AuditMappedBy was found in the " +
+ referencedEntityName + " entity.");
+
+ insertable = true;
+ break;
+ }
+ }
+ }
+
// Adding an element to the mapping corresponding to the references entity id's
Element properties = (Element) idMapping.getXmlRelationMapping().clone();
properties.addAttribute("name", propertyAuditingData.getName());
@@ -97,7 +127,7 @@
throw new MappingException("An audited relation to a non-audited entity " + entityName + "!");
}
- String lastPropertyPrefix = owningReferencePropertyName + "_";
+ String lastPropertyPrefix = MappingTools.createToOneRelationPrefix(owningReferencePropertyName);
String referencedEntityName = propertyValue.getReferencedEntityName();
// Generating the id mapper for the relation
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -14,12 +14,7 @@
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.annotations.common.reflection.ReflectionManager;
-import org.hibernate.envers.AuditJoinTable;
-import org.hibernate.envers.AuditOverride;
-import org.hibernate.envers.AuditOverrides;
-import org.hibernate.envers.Audited;
-import org.hibernate.envers.ModificationStore;
-import org.hibernate.envers.NotAudited;
+import org.hibernate.envers.*;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.tools.MappingTools;
import org.hibernate.mapping.Component;
@@ -177,10 +172,21 @@
return false; // not audited due to AuditOverride annotation
}
addPropertyMapKey(property, propertyData);
+ setPropertyAuditMappedBy(property, propertyData);
return true;
}
+ private void setPropertyAuditMappedBy(XProperty property, PropertyAuditingData propertyData) {
+ AuditMappedBy auditMappedBy = property.getAnnotation(AuditMappedBy.class);
+ if (auditMappedBy != null) {
+ propertyData.setAuditMappedBy(auditMappedBy.mappedBy());
+ if (!"".equals(auditMappedBy.positionMappedBy())) {
+ propertyData.setPositionMappedBy(auditMappedBy.positionMappedBy());
+ }
+ }
+ }
+
private void addPropertyMapKey(XProperty property, PropertyAuditingData propertyData) {
MapKey mapKey = property.getAnnotation(MapKey.class);
if (mapKey != null) {
@@ -254,7 +260,7 @@
public Class<? extends Annotation> annotationType() { return this.getClass(); }
};
- private class ComponentPropertiesSource implements PersistentPropertiesSource {
+ private class ComponentPropertiesSource implements PersistentPropertiesSource {
private final XClass xclass;
private final Component component;
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/ClassAuditingData.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/ClassAuditingData.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/ClassAuditingData.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -57,6 +57,10 @@
return properties.get(propertyName);
}
+ public Iterable<String> getPropertyNames() {
+ return properties.keySet();
+ }
+
public Map<String, String> getSecondaryTableDictionary() {
return secondaryTableDictionary;
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -46,17 +46,22 @@
private String accessType;
private final List<AuditOverride> auditJoinTableOverrides = new ArrayList<AuditOverride>(0);
private RelationTargetAuditMode relationTargetAuditMode;
+ private String auditMappedBy;
+ private String positionMappedBy;
public PropertyAuditingData() {
}
public PropertyAuditingData(String name, String accessType, ModificationStore store,
- RelationTargetAuditMode relationTargetAuditMode) {
+ RelationTargetAuditMode relationTargetAuditMode,
+ String auditMappedBy, String positionMappedBy) {
this.name = name;
this.beanName = name;
this.accessType = accessType;
this.store = store;
this.relationTargetAuditMode = relationTargetAuditMode;
+ this.auditMappedBy = auditMappedBy;
+ this.positionMappedBy = positionMappedBy;
}
public String getName() {
@@ -115,7 +120,23 @@
return auditJoinTableOverrides;
}
- public void addAuditingOverride(AuditOverride annotation) {
+ public String getAuditMappedBy() {
+ return auditMappedBy;
+ }
+
+ public void setAuditMappedBy(String auditMappedBy) {
+ this.auditMappedBy = auditMappedBy;
+ }
+
+ public String getPositionMappedBy() {
+ return positionMappedBy;
+ }
+
+ public void setPositionMappedBy(String positionMappedBy) {
+ this.positionMappedBy = positionMappedBy;
+ }
+
+ public void addAuditingOverride(AuditOverride annotation) {
if (annotation != null) {
String overrideName = annotation.name();
boolean present = false;
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/entities/EntityConfiguration.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -27,6 +27,7 @@
import java.util.Map;
import org.hibernate.envers.entities.mapper.ExtendedPropertyMapper;
+import org.hibernate.envers.entities.mapper.PropertyMapper;
import org.hibernate.envers.entities.mapper.id.IdMapper;
/**
@@ -52,29 +53,29 @@
public void addToOneRelation(String fromPropertyName, String toEntityName, IdMapper idMapper) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE,
- toEntityName, null, idMapper));
+ toEntityName, null, idMapper, null));
}
public void addToOneNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName,
IdMapper idMapper) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_ONE_NOT_OWNING,
- toEntityName, mappedByPropertyName, idMapper));
+ toEntityName, mappedByPropertyName, idMapper, null));
}
public void addToManyNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName,
- IdMapper idMapper) {
+ IdMapper idMapper, PropertyMapper fakeBidirectionalRelationMapper) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_NOT_OWNING,
- toEntityName, mappedByPropertyName, idMapper));
+ toEntityName, mappedByPropertyName, idMapper, fakeBidirectionalRelationMapper));
}
public void addToManyMiddleRelation(String fromPropertyName, String toEntityName) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE,
- toEntityName, null, null));
+ toEntityName, null, null, null));
}
public void addToManyMiddleNotOwningRelation(String fromPropertyName, String mappedByPropertyName, String toEntityName) {
relations.put(fromPropertyName, new RelationDescription(fromPropertyName, RelationType.TO_MANY_MIDDLE_NOT_OWNING,
- toEntityName, mappedByPropertyName, null));
+ toEntityName, mappedByPropertyName, null, null));
}
public boolean isRelation(String propertyName) {
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/entities/RelationDescription.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -24,6 +24,7 @@
package org.hibernate.envers.entities;
import org.hibernate.envers.entities.mapper.id.IdMapper;
+import org.hibernate.envers.entities.mapper.PropertyMapper;
/**
* @author Adam Warski (adam at warski dot org)
@@ -34,15 +35,18 @@
private final String toEntityName;
private final String mappedByPropertyName;
private final IdMapper idMapper;
+ private final PropertyMapper fakeBidirectionalRelationMapper;
private boolean bidirectional;
public RelationDescription(String fromPropertyName, RelationType relationType, String toEntityName,
- String mappedByPropertyName, IdMapper idMapper) {
+ String mappedByPropertyName, IdMapper idMapper,
+ PropertyMapper fakeBidirectionalRelationMapper) {
this.fromPropertyName = fromPropertyName;
this.relationType = relationType;
this.toEntityName = toEntityName;
this.mappedByPropertyName = mappedByPropertyName;
this.idMapper = idMapper;
+ this.fakeBidirectionalRelationMapper = fakeBidirectionalRelationMapper;
this.bidirectional = false;
}
@@ -67,6 +71,10 @@
return idMapper;
}
+ public PropertyMapper getFakeBidirectionalRelationMapper() {
+ return fakeBidirectionalRelationMapper;
+ }
+
public boolean isBidirectional() {
return bidirectional;
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/event/AuditEventListener.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -24,6 +24,7 @@
package org.hibernate.envers.event;
import java.io.Serializable;
+import java.util.List;
import org.hibernate.envers.configuration.AuditConfiguration;
import org.hibernate.envers.entities.RelationDescription;
@@ -31,12 +32,9 @@
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
import org.hibernate.envers.entities.mapper.id.IdMapper;
import org.hibernate.envers.synchronization.AuditSync;
-import org.hibernate.envers.synchronization.work.AddWorkUnit;
-import org.hibernate.envers.synchronization.work.CollectionChangeWorkUnit;
-import org.hibernate.envers.synchronization.work.DelWorkUnit;
-import org.hibernate.envers.synchronization.work.ModWorkUnit;
-import org.hibernate.envers.synchronization.work.PersistentCollectionChangeWorkUnit;
+import org.hibernate.envers.synchronization.work.*;
import org.hibernate.envers.tools.Tools;
+import org.hibernate.envers.RevisionType;
import org.hibernate.cfg.Configuration;
import org.hibernate.collection.PersistentCollection;
@@ -57,6 +55,7 @@
import org.hibernate.event.PreCollectionUpdateEvent;
import org.hibernate.event.PreCollectionUpdateEventListener;
import org.hibernate.persister.entity.EntityPersister;
+import org.hibernate.persister.collection.AbstractCollectionPersister;
import org.hibernate.proxy.HibernateProxy;
/**
@@ -182,7 +181,8 @@
}
private void generateBidirectionalCollectionChangeWorkUnits(AuditSync verSync, AbstractCollectionEvent event,
- PersistentCollectionChangeWorkUnit workUnit) {
+ PersistentCollectionChangeWorkUnit workUnit,
+ RelationDescription rd) {
// Checking if this is enabled in configuration ...
if (!verCfg.getGlobalCfg().isGenerateRevisionsForCollections()) {
return;
@@ -190,12 +190,9 @@
// Checking if this is not a bidirectional relation - then, a revision needs also be generated for
// the other side of the relation.
- RelationDescription relDesc = verCfg.getEntCfg().getRelationDescription(event.getAffectedOwnerEntityName(),
- workUnit.getReferencingPropertyName());
-
// relDesc can be null if this is a collection of simple values (not a relation).
- if (relDesc != null && relDesc.isBidirectional()) {
- String relatedEntityName = relDesc.getToEntityName();
+ if (rd != null && rd.isBidirectional()) {
+ String relatedEntityName = rd.getToEntityName();
IdMapper relatedIdMapper = verCfg.getEntCfg().get(relatedEntityName).getIdMapper();
for (PersistentCollectionChangeData changeData : workUnit.getCollectionChanges()) {
@@ -208,6 +205,38 @@
}
}
+ private void generateFakeBidirecationalRelationWorkUnits(AuditSync verSync, PersistentCollection newColl, Serializable oldColl,
+ String collectionEntityName, String referencingPropertyName,
+ AbstractCollectionEvent event,
+ RelationDescription rd) {
+ // First computing the relation changes
+ List<PersistentCollectionChangeData> collectionChanges = verCfg.getEntCfg().get(collectionEntityName).getPropertyMapper()
+ .mapCollectionChanges(referencingPropertyName, newColl, oldColl, event.getAffectedOwnerIdOrNull());
+
+ // Getting the id mapper for the related entity, as the work units generated will corrspond to the related
+ // entities.
+ String relatedEntityName = rd.getToEntityName();
+ IdMapper relatedIdMapper = verCfg.getEntCfg().get(relatedEntityName).getIdMapper();
+
+ // For each collection change, generating the bidirectional work unit.
+ for (PersistentCollectionChangeData changeData : collectionChanges) {
+ Object relatedObj = changeData.getChangedElement();
+ Serializable relatedId = (Serializable) relatedIdMapper.mapToIdFromEntity(relatedObj);
+ RevisionType revType = (RevisionType) changeData.getData().get(verCfg.getAuditEntCfg().getRevisionTypePropName());
+
+ // By default, the nested work unit is a collection change work unit.
+ AuditWorkUnit nestedWorkUnit = new CollectionChangeWorkUnit(event.getSession(), relatedEntityName, verCfg,
+ relatedId, relatedObj);
+
+ verSync.addWorkUnit(new FakeBidirectionalRelationWorkUnit(event.getSession(), relatedEntityName, verCfg,
+ relatedId, event.getAffectedOwnerOrNull(), rd, revType, nestedWorkUnit));
+ }
+
+ // We also have to generate a collection change work unit for the owning entity.
+ verSync.addWorkUnit(new CollectionChangeWorkUnit(event.getSession(), collectionEntityName, verCfg,
+ event.getAffectedOwnerIdOrNull(), event.getAffectedOwnerOrNull()));
+ }
+
private void onCollectionAction(AbstractCollectionEvent event, PersistentCollection newColl, Serializable oldColl,
CollectionEntry collectionEntry) {
String entityName = event.getAffectedOwnerEntityName();
@@ -215,16 +244,28 @@
if (verCfg.getEntCfg().isVersioned(entityName)) {
AuditSync verSync = verCfg.getSyncManager().get(event.getSession());
- PersistentCollectionChangeWorkUnit workUnit = new PersistentCollectionChangeWorkUnit(event.getSession(),
- entityName, verCfg, newColl, collectionEntry, oldColl, event.getAffectedOwnerIdOrNull());
- verSync.addWorkUnit(workUnit);
+ String ownerEntityName = ((AbstractCollectionPersister) collectionEntry.getLoadedPersister()).getOwnerEntityName();
+ String referencingPropertyName = collectionEntry.getRole().substring(ownerEntityName.length() + 1);
- if (workUnit.containsWork()) {
- // There are some changes: a revision needs also be generated for the collection owner
- verSync.addWorkUnit(new CollectionChangeWorkUnit(event.getSession(), event.getAffectedOwnerEntityName(),
- verCfg, event.getAffectedOwnerIdOrNull(), event.getAffectedOwnerOrNull()));
+ // Checking if this is not a "fake" many-to-one bidirectional relation. The relation description may be
+ // null in case of collections of non-entities.
+ RelationDescription rd = verCfg.getEntCfg().get(entityName).getRelationDescription(referencingPropertyName);
+ if (rd != null && rd.getMappedByPropertyName() != null) {
+ generateFakeBidirecationalRelationWorkUnits(verSync, newColl, oldColl, entityName,
+ referencingPropertyName, event, rd);
+ } else {
+ PersistentCollectionChangeWorkUnit workUnit = new PersistentCollectionChangeWorkUnit(event.getSession(),
+ entityName, verCfg, newColl, collectionEntry, oldColl, event.getAffectedOwnerIdOrNull(),
+ referencingPropertyName);
+ verSync.addWorkUnit(workUnit);
- generateBidirectionalCollectionChangeWorkUnits(verSync, event, workUnit);
+ if (workUnit.containsWork()) {
+ // There are some changes: a revision needs also be generated for the collection owner
+ verSync.addWorkUnit(new CollectionChangeWorkUnit(event.getSession(), event.getAffectedOwnerEntityName(),
+ verCfg, event.getAffectedOwnerIdOrNull(), event.getAffectedOwnerOrNull()));
+
+ generateBidirectionalCollectionChangeWorkUnits(verSync, event, workUnit, rd);
+ }
}
}
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AbstractAuditWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AbstractAuditWorkUnit.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AbstractAuditWorkUnit.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -64,6 +64,14 @@
data.put(entitiesCfg.getOriginalIdPropName(), originalId);
}
+ public void perform(Session session, Object revisionData) {
+ Map<String, Object> data = generateData(revisionData);
+
+ session.save(verCfg.getAuditEntCfg().getAuditEntityName(getEntityName()), data);
+
+ setPerformed(data);
+ }
+
public Object getEntityId() {
return id;
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AddWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AddWorkUnit.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AddWorkUnit.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -30,7 +30,6 @@
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditConfiguration;
-import org.hibernate.Session;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
@@ -60,12 +59,9 @@
return true;
}
- public void perform(Session session, Object revisionData) {
+ public Map<String, Object> generateData(Object revisionData) {
fillDataWithId(data, revisionData, RevisionType.ADD);
-
- session.save(verCfg.getAuditEntCfg().getAuditEntityName(getEntityName()), data);
-
- setPerformed(data);
+ return data;
}
public AuditWorkUnit merge(AddWorkUnit second) {
@@ -84,6 +80,10 @@
return this;
}
+ public AuditWorkUnit merge(FakeBidirectionalRelationWorkUnit second) {
+ return FakeBidirectionalRelationWorkUnit.merge(second, this, second.getNestedWorkUnit());
+ }
+
public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
return first.merge(this);
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AuditWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AuditWorkUnit.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AuditWorkUnit.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -25,7 +25,10 @@
import org.hibernate.Session;
+import java.util.Map;
+
/**
+ * TODO: refactor constructors into factory methods
* @author Adam Warski (adam at warski dot org)
*/
public interface AuditWorkUnit extends WorkUnitMergeVisitor, WorkUnitMergeDispatcher {
@@ -35,7 +38,20 @@
boolean containsWork();
boolean isPerformed();
-
+
+ /**
+ * Perform this work unit in the given session.
+ * @param session Session, in which the work unit should be performed.
+ * @param revisionData The current revision data, which will be used to populate the work unit with the correct
+ * revision relation.
+ */
void perform(Session session, Object revisionData);
void undo(Session session);
+
+ /**
+ * @param revisionData The current revision data, which will be used to populate the work unit with the correct
+ * revision relation.
+ * @return Generates data that should be saved when performing this work unit.
+ */
+ Map<String, Object> generateData(Object revisionData);
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/CollectionChangeWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/CollectionChangeWorkUnit.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/CollectionChangeWorkUnit.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -30,7 +30,6 @@
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditConfiguration;
-import org.hibernate.Session;
import org.hibernate.engine.SessionImplementor;
/**
@@ -50,16 +49,14 @@
return true;
}
- public void perform(Session session, Object revisionData) {
+ public Map<String, Object> generateData(Object revisionData) {
Map<String, Object> data = new HashMap<String, Object>();
fillDataWithId(data, revisionData, RevisionType.MOD);
verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().mapToMapFromEntity(sessionImplementor,
data, entity, null);
- session.save(verCfg.getAuditEntCfg().getAuditEntityName(getEntityName()), data);
-
- setPerformed(data);
+ return data;
}
public AuditWorkUnit merge(AddWorkUnit second) {
@@ -78,6 +75,10 @@
return this;
}
+ public AuditWorkUnit merge(FakeBidirectionalRelationWorkUnit second) {
+ return second;
+ }
+
public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
return first.merge(this);
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/DelWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/DelWorkUnit.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/DelWorkUnit.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -30,7 +30,6 @@
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditConfiguration;
-import org.hibernate.Session;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.engine.SessionImplementor;
@@ -53,7 +52,7 @@
return true;
}
- public void perform(Session session, Object revisionData) {
+ public Map<String, Object> generateData(Object revisionData) {
Map<String, Object> data = new HashMap<String, Object>();
fillDataWithId(data, revisionData, RevisionType.DEL);
@@ -62,9 +61,7 @@
propertyNames, state, state);
}
- session.save(verCfg.getAuditEntCfg().getAuditEntityName(getEntityName()), data);
-
- setPerformed(data);
+ return data;
}
public AuditWorkUnit merge(AddWorkUnit second) {
@@ -83,6 +80,10 @@
return this;
}
+ public AuditWorkUnit merge(FakeBidirectionalRelationWorkUnit second) {
+ return this;
+ }
+
public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
return first.merge(this);
}
Added: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/FakeBidirectionalRelationWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/FakeBidirectionalRelationWorkUnit.java (rev 0)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/FakeBidirectionalRelationWorkUnit.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -0,0 +1,119 @@
+package org.hibernate.envers.synchronization.work;
+
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.envers.configuration.AuditConfiguration;
+import org.hibernate.envers.entities.RelationDescription;
+import org.hibernate.envers.RevisionType;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * A work unit that handles "fake" bidirectional one-to-many relations (mapped with {@code @OneToMany+@JoinColumn} and
+ * {@code @ManyToOne+@Column(insertable=false, updatable=false)}.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class FakeBidirectionalRelationWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit {
+ private final Object owningEntity;
+ private final RelationDescription rd;
+ private final RevisionType revisionType;
+
+ /*
+ * The work unit responsible for generating the "raw" entity data to be saved.
+ */
+ private final AuditWorkUnit nestedWorkUnit;
+
+
+ public FakeBidirectionalRelationWorkUnit(SessionImplementor sessionImplementor, String entityName,
+ AuditConfiguration verCfg, Serializable id, Object owningEntity,
+ RelationDescription rd, RevisionType revisionType,
+ AuditWorkUnit nestedWorkUnit) {
+ super(sessionImplementor, entityName, verCfg, id);
+
+
+ this.owningEntity = owningEntity;
+ this.rd = rd;
+ this.revisionType = revisionType;
+ this.nestedWorkUnit = nestedWorkUnit;
+ }
+
+ public FakeBidirectionalRelationWorkUnit(FakeBidirectionalRelationWorkUnit original, AuditWorkUnit nestedWorkUnit) {
+ super(original.sessionImplementor, original.entityName, original.verCfg, original.id);
+
+ this.owningEntity = original.owningEntity;
+ this.rd = original.rd;
+ this.revisionType = original.revisionType;
+ this.nestedWorkUnit = nestedWorkUnit;
+ }
+
+ public AuditWorkUnit getNestedWorkUnit() {
+ return nestedWorkUnit;
+ }
+
+ public RevisionType getRevisionType() {
+ return revisionType;
+ }
+
+ public boolean containsWork() {
+ return true;
+ }
+
+ public Map<String, Object> generateData(Object revisionData) {
+ // Generating data with the nested work unit. This data contains all data except the fake relation.
+ // Making a defensive copy not to modify the data held by the nested work unit.
+ Map<String, Object> nestedData = new HashMap<String, Object>(nestedWorkUnit.generateData(revisionData));
+
+ // Now adding data for the fake relation.
+ // If the revision type is "DEL", it means that the object is removed from the collection. Then the
+ // new owner will in fact be null.
+ rd.getFakeBidirectionalRelationMapper().mapToMapFromEntity(sessionImplementor, nestedData,
+ revisionType == RevisionType.DEL ? null : owningEntity, null);
+
+ return nestedData;
+ }
+
+ public AuditWorkUnit merge(AddWorkUnit second) {
+ return merge(this, nestedWorkUnit, second);
+ }
+
+ public AuditWorkUnit merge(ModWorkUnit second) {
+ return merge(this, nestedWorkUnit, second);
+ }
+
+ public AuditWorkUnit merge(DelWorkUnit second) {
+ return second;
+ }
+
+ public AuditWorkUnit merge(CollectionChangeWorkUnit second) {
+ return this;
+ }
+
+ public AuditWorkUnit merge(FakeBidirectionalRelationWorkUnit second) {
+ /*
+ * The merging rules are the following (revision types of the first and second work units):
+ * - DEL, DEL - return any (the work units are the same)
+ * - DEL, ADD - return ADD (points to new owner)
+ * - ADD, DEL - return ADD (points to new owner)
+ * - ADD, ADD - return second (points to newer owner)
+ */
+
+ if (revisionType == RevisionType.DEL || second.getRevisionType() == RevisionType.ADD) {
+ return second;
+ }
+
+ return this;
+ }
+
+ public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
+ return first.merge(this);
+ }
+
+ public static AuditWorkUnit merge(FakeBidirectionalRelationWorkUnit frwu, AuditWorkUnit nestedFirst,
+ AuditWorkUnit nestedSecond) {
+ AuditWorkUnit nestedMerged = nestedSecond.dispatch(nestedFirst);
+
+ // Creating a new fake relation work unit with the nested merged data
+ return new FakeBidirectionalRelationWorkUnit(frwu, nestedMerged);
+ }
+}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/ModWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/ModWorkUnit.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/ModWorkUnit.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -30,7 +30,6 @@
import org.hibernate.envers.RevisionType;
import org.hibernate.envers.configuration.AuditConfiguration;
-import org.hibernate.Session;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.persister.entity.EntityPersister;
@@ -54,12 +53,10 @@
return changes;
}
- public void perform(Session session, Object revisionData) {
+ public Map<String, Object> generateData(Object revisionData) {
fillDataWithId(data, revisionData, RevisionType.MOD);
- session.save(verCfg.getAuditEntCfg().getAuditEntityName(getEntityName()), data);
-
- setPerformed(data);
+ return data;
}
public Map<String, Object> getData() {
@@ -82,6 +79,10 @@
return this;
}
+ public AuditWorkUnit merge(FakeBidirectionalRelationWorkUnit second) {
+ return FakeBidirectionalRelationWorkUnit.merge(second, this, second.getNestedWorkUnit());
+ }
+
public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
return first.merge(this);
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -34,7 +34,6 @@
import org.hibernate.envers.entities.mapper.PersistentCollectionChangeData;
import org.hibernate.Session;
-import org.hibernate.persister.collection.AbstractCollectionPersister;
import org.hibernate.engine.CollectionEntry;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.collection.PersistentCollection;
@@ -48,11 +47,11 @@
public PersistentCollectionChangeWorkUnit(SessionImplementor sessionImplementor, String entityName,
AuditConfiguration auditCfg, PersistentCollection collection,
- CollectionEntry collectionEntry, Serializable snapshot, Serializable id) {
+ CollectionEntry collectionEntry, Serializable snapshot, Serializable id,
+ String referencingPropertyName) {
super(sessionImplementor, entityName, auditCfg, new PersistentCollectionChangeWorkUnitId(id, collectionEntry.getRole()));
- String ownerEntityName = ((AbstractCollectionPersister) collectionEntry.getLoadedPersister()).getOwnerEntityName();
- referencingPropertyName = collectionEntry.getRole().substring(ownerEntityName.length() + 1);
+ this.referencingPropertyName = referencingPropertyName;
collectionChanges = auditCfg.getEntCfg().get(getEntityName()).getPropertyMapper()
.mapCollectionChanges(referencingPropertyName, collection, snapshot, id);
@@ -72,6 +71,10 @@
return collectionChanges != null && collectionChanges.size() != 0;
}
+ public Map<String, Object> generateData(Object revisionData) {
+ throw new UnsupportedOperationException("Cannot generate data for a collection change work unit!");
+ }
+
@SuppressWarnings({"unchecked"})
public void perform(Session session, Object revisionData) {
AuditEntitiesConfiguration entitiesCfg = verCfg.getAuditEntCfg();
@@ -109,6 +112,10 @@
return null;
}
+ public AuditWorkUnit merge(FakeBidirectionalRelationWorkUnit second) {
+ return null;
+ }
+
public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
if (first instanceof PersistentCollectionChangeWorkUnit) {
PersistentCollectionChangeWorkUnit original = (PersistentCollectionChangeWorkUnit) first;
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeVisitor.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeVisitor.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeVisitor.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -32,4 +32,5 @@
AuditWorkUnit merge(ModWorkUnit second);
AuditWorkUnit merge(DelWorkUnit second);
AuditWorkUnit merge(CollectionChangeWorkUnit second);
+ AuditWorkUnit merge(FakeBidirectionalRelationWorkUnit second);
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/tools/MappingTools.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/tools/MappingTools.java 2009-12-13 01:17:08 UTC (rev 18223)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/tools/MappingTools.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -12,4 +12,12 @@
public static String createComponentPrefix(String componentName) {
return componentName + "_";
}
+
+ /**
+ * @param referencePropertyName The name of the property that holds the relation to the entity.
+ * @return A prefix which should be used to prefix an id mapper for the related entity.
+ */
+ public static String createToOneRelationPrefix(String referencePropertyName) {
+ return referencePropertyName + "_";
+ }
}
Added: core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefEdEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefEdEntity.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefEdEntity.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -0,0 +1,85 @@
+package org.hibernate.envers.test.entities.onetomany.detached;
+
+import org.hibernate.envers.Audited;
+
+import javax.persistence.*;
+
+/**
+ * Entity for {@link org.hibernate.envers.test.integration.onetomany.detached.JoinColumnBidirectionalList} test.
+ * Owned side of the relation.
+ * @author Adam Warski (adam at warski dot org)
+ */
+@Entity
+@Audited
+public class ListJoinColumnBidirectionalRefEdEntity {
+ @Id
+ @GeneratedValue
+ private Integer id;
+
+ private String data;
+
+ @ManyToOne
+ @JoinColumn(name = "some_join_column", insertable = false, updatable = false)
+ private ListJoinColumnBidirectionalRefIngEntity owner;
+
+ public ListJoinColumnBidirectionalRefEdEntity() { }
+
+ public ListJoinColumnBidirectionalRefEdEntity(Integer id, String data, ListJoinColumnBidirectionalRefIngEntity owner) {
+ this.id = id;
+ this.data = data;
+ this.owner = owner;
+ }
+
+ public ListJoinColumnBidirectionalRefEdEntity(String data, ListJoinColumnBidirectionalRefIngEntity owner) {
+ this.data = data;
+ this.owner = owner;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public ListJoinColumnBidirectionalRefIngEntity getOwner() {
+ return owner;
+ }
+
+ public void setOwner(ListJoinColumnBidirectionalRefIngEntity owner) {
+ this.owner = owner;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ListJoinColumnBidirectionalRefEdEntity)) return false;
+
+ ListJoinColumnBidirectionalRefEdEntity that = (ListJoinColumnBidirectionalRefEdEntity) o;
+
+ if (data != null ? !data.equals(that.data) : that.data != null) return false;
+ //noinspection RedundantIfStatement
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (id != null ? id.hashCode() : 0);
+ result = 31 * result + (data != null ? data.hashCode() : 0);
+ return result;
+ }
+
+ public String toString() {
+ return "ListJoinColumnBidirectionalRefEdEntity(id = " + id + ", data = " + data + ")";
+ }
+}
Added: core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefIngEntity.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefIngEntity.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/entities/onetomany/detached/ListJoinColumnBidirectionalRefIngEntity.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -0,0 +1,90 @@
+package org.hibernate.envers.test.entities.onetomany.detached;
+
+import org.hibernate.envers.Audited;
+import org.hibernate.envers.AuditMappedBy;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Arrays;
+
+/**
+ * Entity for {@link org.hibernate.envers.test.integration.onetomany.detached.JoinColumnBidirectionalList} test.
+ * Owning side of the relation.
+ * @author Adam Warski (adam at warski dot org)
+ */
+@Entity
+@Audited
+public class ListJoinColumnBidirectionalRefIngEntity {
+ @Id
+ @GeneratedValue
+ private Integer id;
+
+ private String data;
+
+ @OneToMany
+ @JoinColumn(name = "some_join_column")
+ @AuditMappedBy(mappedBy = "owner")
+ private List<ListJoinColumnBidirectionalRefEdEntity> references;
+
+ public ListJoinColumnBidirectionalRefIngEntity() { }
+
+ public ListJoinColumnBidirectionalRefIngEntity(Integer id, String data, ListJoinColumnBidirectionalRefEdEntity... references) {
+ this.id = id;
+ this.data = data;
+ this.references = new ArrayList<ListJoinColumnBidirectionalRefEdEntity>();
+ this.references.addAll(Arrays.asList(references));
+ }
+
+ public ListJoinColumnBidirectionalRefIngEntity(String data, ListJoinColumnBidirectionalRefEdEntity... references) {
+ this(null, data, references);
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public List<ListJoinColumnBidirectionalRefEdEntity> getReferences() {
+ return references;
+ }
+
+ public void setReferences(List<ListJoinColumnBidirectionalRefEdEntity> references) {
+ this.references = references;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof ListJoinColumnBidirectionalRefIngEntity)) return false;
+
+ ListJoinColumnBidirectionalRefIngEntity that = (ListJoinColumnBidirectionalRefIngEntity) o;
+
+ if (data != null ? !data.equals(that.data) : that.data != null) return false;
+ //noinspection RedundantIfStatement
+ if (id != null ? !id.equals(that.id) : that.id != null) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (id != null ? id.hashCode() : 0);
+ result = 31 * result + (data != null ? data.hashCode() : 0);
+ return result;
+ }
+
+ public String toString() {
+ return "ListJoinColumnBidirectionalRefIngEntity(id = " + id + ", data = " + data + ")";
+ }
+}
Added: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java (rev 0)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/onetomany/detached/JoinColumnBidirectionalList.java 2009-12-15 10:50:11 UTC (rev 18224)
@@ -0,0 +1,204 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.envers.test.integration.onetomany.detached;
+
+import org.hibernate.ejb.Ejb3Configuration;
+import org.hibernate.envers.test.AbstractEntityTest;
+import org.hibernate.envers.test.entities.onetomany.detached.ListJoinColumnBidirectionalRefEdEntity;
+import org.hibernate.envers.test.entities.onetomany.detached.ListJoinColumnBidirectionalRefIngEntity;
+import static org.hibernate.envers.test.tools.TestTools.checkList;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import org.testng.annotations.Test;
+
+import javax.persistence.EntityManager;
+import java.util.Arrays;
+
+/**
+ * Test for a "fake" bidirectional mapping where one side uses @OneToMany+@JoinColumn (and thus owns the relatin),
+ * and the other uses a @ManyToOne(insertable=false, updatable=false).
+ * @author Adam Warski (adam at warski dot org)
+ */
+public class JoinColumnBidirectionalList extends AbstractEntityTest {
+ private Integer ed1_id;
+ private Integer ed2_id;
+
+ private Integer ing1_id;
+ private Integer ing2_id;
+
+ public void configure(Ejb3Configuration cfg) {
+ cfg.addAnnotatedClass(ListJoinColumnBidirectionalRefIngEntity.class);
+ cfg.addAnnotatedClass(ListJoinColumnBidirectionalRefEdEntity.class);
+ }
+
+ @Test(enabled = true)
+ public void createData() {
+ EntityManager em = getEntityManager();
+
+ ListJoinColumnBidirectionalRefEdEntity ed1 = new ListJoinColumnBidirectionalRefEdEntity("ed1", null);
+ ListJoinColumnBidirectionalRefEdEntity ed2 = new ListJoinColumnBidirectionalRefEdEntity("ed2", null);
+
+ ListJoinColumnBidirectionalRefIngEntity ing1 = new ListJoinColumnBidirectionalRefIngEntity("coll1", ed1);
+ ListJoinColumnBidirectionalRefIngEntity ing2 = new ListJoinColumnBidirectionalRefIngEntity("coll1", ed2);
+
+ // Revision 1 (ing1: ed1, ing2: ed2)
+ em.getTransaction().begin();
+
+ em.persist(ed1);
+ em.persist(ed2);
+ em.persist(ing1);
+ em.persist(ing2);
+
+ em.getTransaction().commit();
+
+ // Revision 2 (ing1: ed1, ed2)
+ em.getTransaction().begin();
+
+ ing1 = em.find(ListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
+ ing2 = em.find(ListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
+ ed1 = em.find(ListJoinColumnBidirectionalRefEdEntity.class, ed1.getId());
+ ed2 = em.find(ListJoinColumnBidirectionalRefEdEntity.class, ed2.getId());
+
+ ing2.getReferences().remove(ed2);
+ ing1.getReferences().add(ed2);
+
+ em.getTransaction().commit();
+ em.clear();
+
+ // Revision 3 (ing1: ed1, ed2)
+ em.getTransaction().begin();
+
+ ed1 = em.find(ListJoinColumnBidirectionalRefEdEntity.class, ed1.getId());
+
+ ed1.setData("ed1 bis");
+
+ em.getTransaction().commit();
+ em.clear();
+
+ // Revision 4 (ing2: ed1, ed2)
+ em.getTransaction().begin();
+
+ ing1 = em.find(ListJoinColumnBidirectionalRefIngEntity.class, ing1.getId());
+ ing2 = em.find(ListJoinColumnBidirectionalRefIngEntity.class, ing2.getId());
+
+ ing1.getReferences().clear();
+ ing2.getReferences().add(ed1);
+ ing2.getReferences().add(ed2);
+
+ em.getTransaction().commit();
+ em.clear();
+
+ //
+
+ ing1_id = ing1.getId();
+ ing2_id = ing2.getId();
+
+ ed1_id = ed1.getId();
+ ed2_id = ed2.getId();
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testRevisionsCounts() {
+ assertEquals(Arrays.asList(1, 2, 4), getAuditReader().getRevisions(ListJoinColumnBidirectionalRefIngEntity.class, ing1_id));
+ assertEquals(Arrays.asList(1, 2, 4), getAuditReader().getRevisions(ListJoinColumnBidirectionalRefIngEntity.class, ing2_id));
+
+ assertEquals(Arrays.asList(1, 3, 4), getAuditReader().getRevisions(ListJoinColumnBidirectionalRefEdEntity.class, ed1_id));
+ assertEquals(Arrays.asList(1, 2, 4), getAuditReader().getRevisions(ListJoinColumnBidirectionalRefEdEntity.class, ed2_id));
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfIng1() {
+ ListJoinColumnBidirectionalRefEdEntity ed1_fromRev1 = new ListJoinColumnBidirectionalRefEdEntity(ed1_id, "ed1", null);
+ ListJoinColumnBidirectionalRefEdEntity ed1_fromRev3 = new ListJoinColumnBidirectionalRefEdEntity(ed1_id, "ed1 bis", null);
+ ListJoinColumnBidirectionalRefEdEntity ed2 = getEntityManager().find(ListJoinColumnBidirectionalRefEdEntity.class, ed2_id);
+
+ ListJoinColumnBidirectionalRefIngEntity rev1 = getAuditReader().find(ListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 1);
+ ListJoinColumnBidirectionalRefIngEntity rev2 = getAuditReader().find(ListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 2);
+ ListJoinColumnBidirectionalRefIngEntity rev3 = getAuditReader().find(ListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 3);
+ ListJoinColumnBidirectionalRefIngEntity rev4 = getAuditReader().find(ListJoinColumnBidirectionalRefIngEntity.class, ing1_id, 4);
+
+ assertTrue(checkList(rev1.getReferences(), ed1_fromRev1));
+ assertTrue(checkList(rev2.getReferences(), ed1_fromRev1, ed2));
+ assertTrue(checkList(rev3.getReferences(), ed1_fromRev3, ed2));
+ assertTrue(checkList(rev4.getReferences()));
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfIng2() {
+ ListJoinColumnBidirectionalRefEdEntity ed1 = getEntityManager().find(ListJoinColumnBidirectionalRefEdEntity.class, ed1_id);
+ ListJoinColumnBidirectionalRefEdEntity ed2 = getEntityManager().find(ListJoinColumnBidirectionalRefEdEntity.class, ed2_id);
+
+ ListJoinColumnBidirectionalRefIngEntity rev1 = getAuditReader().find(ListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 1);
+ ListJoinColumnBidirectionalRefIngEntity rev2 = getAuditReader().find(ListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 2);
+ ListJoinColumnBidirectionalRefIngEntity rev3 = getAuditReader().find(ListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 3);
+ ListJoinColumnBidirectionalRefIngEntity rev4 = getAuditReader().find(ListJoinColumnBidirectionalRefIngEntity.class, ing2_id, 4);
+
+ assertTrue(checkList(rev1.getReferences(), ed2));
+ assertTrue(checkList(rev2.getReferences()));
+ assertTrue(checkList(rev3.getReferences()));
+ assertTrue(checkList(rev4.getReferences(), ed1, ed2));
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfEd1() {
+ ListJoinColumnBidirectionalRefIngEntity ing1 = getEntityManager().find(ListJoinColumnBidirectionalRefIngEntity.class, ing1_id);
+ ListJoinColumnBidirectionalRefIngEntity ing2 = getEntityManager().find(ListJoinColumnBidirectionalRefIngEntity.class, ing2_id);
+
+ ListJoinColumnBidirectionalRefEdEntity rev1 = getAuditReader().find(ListJoinColumnBidirectionalRefEdEntity.class, ed1_id, 1);
+ ListJoinColumnBidirectionalRefEdEntity rev2 = getAuditReader().find(ListJoinColumnBidirectionalRefEdEntity.class, ed1_id, 2);
+ ListJoinColumnBidirectionalRefEdEntity rev3 = getAuditReader().find(ListJoinColumnBidirectionalRefEdEntity.class, ed1_id, 3);
+ ListJoinColumnBidirectionalRefEdEntity rev4 = getAuditReader().find(ListJoinColumnBidirectionalRefEdEntity.class, ed1_id, 4);
+
+ assertTrue(rev1.getOwner().equals(ing1));
+ assertTrue(rev2.getOwner().equals(ing1));
+ assertTrue(rev3.getOwner().equals(ing1));
+ assertTrue(rev4.getOwner().equals(ing2));
+
+ assertEquals(rev1.getData(), "ed1");
+ assertEquals(rev2.getData(), "ed1");
+ assertEquals(rev3.getData(), "ed1 bis");
+ assertEquals(rev4.getData(), "ed1 bis");
+ }
+
+ @Test(enabled = true, dependsOnMethods = "createData")
+ public void testHistoryOfEd2() {
+ ListJoinColumnBidirectionalRefIngEntity ing1 = getEntityManager().find(ListJoinColumnBidirectionalRefIngEntity.class, ing1_id);
+ ListJoinColumnBidirectionalRefIngEntity ing2 = getEntityManager().find(ListJoinColumnBidirectionalRefIngEntity.class, ing2_id);
+
+ ListJoinColumnBidirectionalRefEdEntity rev1 = getAuditReader().find(ListJoinColumnBidirectionalRefEdEntity.class, ed2_id, 1);
+ ListJoinColumnBidirectionalRefEdEntity rev2 = getAuditReader().find(ListJoinColumnBidirectionalRefEdEntity.class, ed2_id, 2);
+ ListJoinColumnBidirectionalRefEdEntity rev3 = getAuditReader().find(ListJoinColumnBidirectionalRefEdEntity.class, ed2_id, 3);
+ ListJoinColumnBidirectionalRefEdEntity rev4 = getAuditReader().find(ListJoinColumnBidirectionalRefEdEntity.class, ed2_id, 4);
+
+ assertTrue(rev1.getOwner().equals(ing2));
+ assertTrue(rev2.getOwner().equals(ing1));
+ assertTrue(rev3.getOwner().equals(ing1));
+ assertTrue(rev4.getOwner().equals(ing2));
+
+ assertEquals(rev1.getData(), "ed2");
+ assertEquals(rev2.getData(), "ed2");
+ assertEquals(rev3.getData(), "ed2");
+ assertEquals(rev4.getData(), "ed2");
+ }
+}
15 years
Hibernate SVN: r18211 - in core/trunk/annotations/src: main/java/org/hibernate/cfg and 3 other directories.
by hibernate-commits@lists.jboss.org
Author: hardy.ferentschik
Date: 2009-12-11 14:14:01 -0500 (Fri, 11 Dec 2009)
New Revision: 18211
Modified:
core/trunk/annotations/src/main/java/org/hibernate/annotations/AccessType.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/ClassPropertyHolder.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java
core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/access/AccessTest.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/access/Furniture.java
core/trunk/annotations/src/test/java/org/hibernate/test/annotations/embedded/Country.java
Log:
HHH-4527
No functional changes yet. Just some cleanup and refactoring (extract method, encapsulate field)
Modified: core/trunk/annotations/src/main/java/org/hibernate/annotations/AccessType.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/annotations/AccessType.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/main/java/org/hibernate/annotations/AccessType.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -23,17 +23,20 @@
*/
package org.hibernate.annotations;
-import static java.lang.annotation.ElementType.*;
import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
/**
* Property Access type
*
* @author Emmanuel Bernard
*/
-@Target({TYPE, METHOD, FIELD})
+@Target({ TYPE, METHOD, FIELD })
@Retention(RUNTIME)
public @interface AccessType {
String value();
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationBinder.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -176,6 +176,7 @@
* package)
*
* @author Emmanuel Bernard
+ * @author Hardy Ferentschik
*/
@SuppressWarnings("unchecked")
public final class AnnotationBinder {
@@ -247,7 +248,7 @@
}
public static void bindPackage(String packageName, ExtendedMappings mappings) {
- XPackage pckg = null;
+ XPackage pckg;
try {
pckg = mappings.getReflectionManager().packageForName( packageName );
}
@@ -438,153 +439,72 @@
bindFilterDefs(clazzToProcess, mappings);
}
- if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) //will be processed by their subentities
- || AnnotatedClassType.NONE.equals( classType ) //to be ignored
- || AnnotatedClassType.EMBEDDABLE.equals( classType ) //allow embeddable element declaration
- ) {
- if ( AnnotatedClassType.NONE.equals( classType )
- && clazzToProcess.isAnnotationPresent( org.hibernate.annotations.Entity.class ) ) {
- log.warn( "Class annotated @org.hibernate.annotations.Entity but not javax.persistence.Entity "
- + "(most likely a user error): {}", clazzToProcess.getName() );
- }
+ if( !isEntityClassType( clazzToProcess, classType ) ) {
return;
}
- if ( !classType.equals( AnnotatedClassType.ENTITY ) ) {
- //TODO make this test accurate by removing the none elements artifically added
- throw new AnnotationException(
- "Annotated class should have a @javax.persistence.Entity, @javax.persistence.Embeddable or @javax.persistence.EmbeddedSuperclass annotation: " + clazzToProcess
- .getName()
- );
- }
- XAnnotatedElement annotatedClass = clazzToProcess;
+
log.info( "Binding entity from annotated class: {}", clazzToProcess.getName() );
- final ReflectionManager reflectionManager = mappings.getReflectionManager();
- InheritanceState superEntityState =
- InheritanceState.getSuperEntityInheritanceState(
- clazzToProcess, inheritanceStatePerClass, reflectionManager
- );
- PersistentClass superEntity = superEntityState != null ?
- mappings.getClass(
- superEntityState.clazz.getName()
- ) :
- null;
- if ( superEntity == null ) {
- //check if superclass is not a potential persistent class
- if ( inheritanceState.hasParents ) {
- throw new AssertionFailure(
- "Subclass has to be binded after it's mother class: "
- + superEntityState.clazz.getName()
- );
- }
- }
- bindQueries( annotatedClass, mappings );
- bindFilterDefs( annotatedClass, mappings );
- bindTypeDefs( annotatedClass, mappings );
- BinderHelper.bindAnyMetaDefs( annotatedClass, mappings );
+ PersistentClass superEntity = getSuperEntity(clazzToProcess, inheritanceStatePerClass, mappings, inheritanceState);
+
+ bindQueries( clazzToProcess, mappings );
+ bindFilterDefs( clazzToProcess, mappings );
+ bindTypeDefs( clazzToProcess, mappings );
+ BinderHelper.bindAnyMetaDefs( clazzToProcess, mappings );
+
String schema = "";
String table = ""; //might be no @Table annotation on the annotated class
String catalog = "";
- String discrimValue = null;
List<UniqueConstraintHolder> uniqueConstraints = new ArrayList<UniqueConstraintHolder>();
- Ejb3DiscriminatorColumn discriminatorColumn = null;
- Ejb3JoinColumn[] inheritanceJoinedColumns = null;
-
- if ( annotatedClass.isAnnotationPresent( javax.persistence.Table.class ) ) {
- javax.persistence.Table tabAnn = annotatedClass.getAnnotation( javax.persistence.Table.class );
+ if ( clazzToProcess.isAnnotationPresent( javax.persistence.Table.class ) ) {
+ javax.persistence.Table tabAnn = clazzToProcess.getAnnotation( javax.persistence.Table.class );
table = tabAnn.name();
schema = tabAnn.schema();
catalog = tabAnn.catalog();
uniqueConstraints = TableBinder.buildUniqueConstraintHolders( tabAnn.uniqueConstraints() );
}
- final boolean hasJoinedColumns = inheritanceState.hasParents
- && InheritanceType.JOINED.equals( inheritanceState.type );
- if ( hasJoinedColumns ) {
- //@Inheritance(JOINED) subclass need to link back to the super entity
- PrimaryKeyJoinColumns jcsAnn = annotatedClass.getAnnotation( PrimaryKeyJoinColumns.class );
- boolean explicitInheritanceJoinedColumns = jcsAnn != null && jcsAnn.value().length != 0;
- if ( explicitInheritanceJoinedColumns ) {
- int nbrOfInhJoinedColumns = jcsAnn.value().length;
- PrimaryKeyJoinColumn jcAnn;
- inheritanceJoinedColumns = new Ejb3JoinColumn[nbrOfInhJoinedColumns];
- for (int colIndex = 0; colIndex < nbrOfInhJoinedColumns; colIndex++) {
- jcAnn = jcsAnn.value()[colIndex];
- inheritanceJoinedColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(
- jcAnn, null, superEntity.getIdentifier(),
- (Map<String, Join>) null, (PropertyHolder) null, mappings
- );
- }
- }
- else {
- PrimaryKeyJoinColumn jcAnn = annotatedClass.getAnnotation( PrimaryKeyJoinColumn.class );
- inheritanceJoinedColumns = new Ejb3JoinColumn[1];
- inheritanceJoinedColumns[0] = Ejb3JoinColumn.buildJoinColumn(
- jcAnn, null, superEntity.getIdentifier(),
- (Map<String, Join>) null, (PropertyHolder) null, mappings
- );
- }
- log.debug( "Subclass joined column(s) created" );
- }
- else {
- if ( annotatedClass.isAnnotationPresent( javax.persistence.PrimaryKeyJoinColumns.class )
- || annotatedClass.isAnnotationPresent( javax.persistence.PrimaryKeyJoinColumn.class ) ) {
- log.warn( "Root entity should not hold an PrimaryKeyJoinColum(s), will be ignored" );
- }
- }
- if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.type ) ) {
- javax.persistence.DiscriminatorColumn discAnn = annotatedClass.getAnnotation(
+ Ejb3JoinColumn[] inheritanceJoinedColumns = makeInheritanceJoinColumns( clazzToProcess, mappings, inheritanceState, superEntity );
+ Ejb3DiscriminatorColumn discriminatorColumn = null;
+ String discrimValue = null;
+ if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
+ javax.persistence.DiscriminatorColumn discAnn = clazzToProcess.getAnnotation(
javax.persistence.DiscriminatorColumn.class
);
DiscriminatorType discriminatorType = discAnn != null ?
discAnn.discriminatorType() :
DiscriminatorType.STRING;
- org.hibernate.annotations.DiscriminatorFormula discFormulaAnn = annotatedClass.getAnnotation(
+ org.hibernate.annotations.DiscriminatorFormula discFormulaAnn = clazzToProcess.getAnnotation(
org.hibernate.annotations.DiscriminatorFormula.class
);
- if ( !inheritanceState.hasParents ) {
+ if ( !inheritanceState.hasParents() ) {
discriminatorColumn = Ejb3DiscriminatorColumn.buildDiscriminatorColumn(
discriminatorType, discAnn, discFormulaAnn, mappings
);
}
- if ( discAnn != null && inheritanceState.hasParents ) {
+ if ( discAnn != null && inheritanceState.hasParents() ) {
log.warn(
"Discriminator column has to be defined in the root entity, it will be ignored in subclass: {}",
clazzToProcess.getName()
);
}
- discrimValue = annotatedClass.isAnnotationPresent( DiscriminatorValue.class ) ?
- annotatedClass.getAnnotation( DiscriminatorValue.class ).value() :
+
+ discrimValue = clazzToProcess.isAnnotationPresent( DiscriminatorValue.class ) ?
+ clazzToProcess.getAnnotation( DiscriminatorValue.class ).value() :
null;
}
- //we now know what kind of persistent entity it is
- PersistentClass persistentClass;
- //create persistent class
- if ( !inheritanceState.hasParents ) {
- persistentClass = new RootClass();
- }
- else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.type ) ) {
- persistentClass = new SingleTableSubclass( superEntity );
- }
- else if ( InheritanceType.JOINED.equals( inheritanceState.type ) ) {
- persistentClass = new JoinedSubclass( superEntity );
- }
- else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.type ) ) {
- persistentClass = new UnionSubclass( superEntity );
- }
- else {
- throw new AssertionFailure( "Unknown inheritance type: " + inheritanceState.type );
- }
- Proxy proxyAnn = annotatedClass.getAnnotation( Proxy.class );
- BatchSize sizeAnn = annotatedClass.getAnnotation( BatchSize.class );
- Where whereAnn = annotatedClass.getAnnotation( Where.class );
- Entity entityAnn = annotatedClass.getAnnotation( Entity.class );
- org.hibernate.annotations.Entity hibEntityAnn = annotatedClass.getAnnotation(
+ PersistentClass persistentClass = makePersistentClass( inheritanceState, superEntity );
+
+ Proxy proxyAnn = clazzToProcess.getAnnotation( Proxy.class );
+ BatchSize sizeAnn = clazzToProcess.getAnnotation( BatchSize.class );
+ Where whereAnn = clazzToProcess.getAnnotation( Where.class );
+ Entity entityAnn = clazzToProcess.getAnnotation( Entity.class );
+ org.hibernate.annotations.Entity hibEntityAnn = clazzToProcess.getAnnotation(
org.hibernate.annotations.Entity.class
);
- org.hibernate.annotations.Cache cacheAnn = annotatedClass.getAnnotation(
+ org.hibernate.annotations.Cache cacheAnn = clazzToProcess.getAnnotation(
org.hibernate.annotations.Cache.class
);
EntityBinder entityBinder = new EntityBinder(
@@ -598,14 +518,14 @@
entityBinder.setInheritanceState( inheritanceState );
//Filters are not allowed on subclasses
- if ( !inheritanceState.hasParents ) {
+ if ( !inheritanceState.hasParents() ) {
bindFilters(clazzToProcess, entityBinder, mappings);
}
entityBinder.bindEntity();
if ( inheritanceState.hasTable() ) {
- Check checkAnn = annotatedClass.getAnnotation( Check.class );
+ Check checkAnn = clazzToProcess.getAnnotation( Check.class );
String constraints = checkAnn == null ?
null :
checkAnn.constraints();
@@ -617,32 +537,29 @@
);
}
else {
- if ( annotatedClass.isAnnotationPresent( Table.class ) ) {
+ if ( clazzToProcess.isAnnotationPresent( Table.class ) ) {
log.warn( "Illegal use of @Table in a subclass of a SINGLE_TABLE hierarchy: " + clazzToProcess
.getName() );
}
}
-// Map<String, Column[]> columnOverride = PropertyHolderBuilder.buildHierarchyColumnOverride(
-// clazzToProcess,
-// persistentClass.getClassName()
-// );
+
PropertyHolder propertyHolder = PropertyHolderBuilder.buildPropertyHolder(
clazzToProcess,
persistentClass,
entityBinder, mappings, inheritanceStatePerClass
);
- javax.persistence.SecondaryTable secTabAnn = annotatedClass.getAnnotation(
+ javax.persistence.SecondaryTable secTabAnn = clazzToProcess.getAnnotation(
javax.persistence.SecondaryTable.class
);
- javax.persistence.SecondaryTables secTabsAnn = annotatedClass.getAnnotation(
+ javax.persistence.SecondaryTables secTabsAnn = clazzToProcess.getAnnotation(
javax.persistence.SecondaryTables.class
);
entityBinder.firstLevelSecondaryTablesBinding( secTabAnn, secTabsAnn );
- OnDelete onDeleteAnn = annotatedClass.getAnnotation( OnDelete.class );
+ OnDelete onDeleteAnn = clazzToProcess.getAnnotation( OnDelete.class );
boolean onDeleteAppropriate = false;
- if ( InheritanceType.JOINED.equals( inheritanceState.type ) && inheritanceState.hasParents ) {
+ if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) && inheritanceState.hasParents() ) {
onDeleteAppropriate = true;
final JoinedSubclass jsc = (JoinedSubclass) persistentClass;
if ( persistentClass.getEntityPersisterClass() == null ) {
@@ -650,7 +567,7 @@
}
SimpleValue key = new DependantValue( jsc.getTable(), jsc.getIdentifier() );
jsc.setKey( key );
- ForeignKey fk = annotatedClass.getAnnotation( ForeignKey.class );
+ ForeignKey fk = clazzToProcess.getAnnotation( ForeignKey.class );
if ( fk != null && !BinderHelper.isDefault( fk.name() ) ) {
key.setForeignKeyName( fk.name() );
}
@@ -666,14 +583,14 @@
mappings.addSecondPass( new CreateKeySecondPass( jsc ) );
}
- else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.type ) ) {
- if ( inheritanceState.hasParents ) {
+ else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
+ if ( inheritanceState.hasParents() ) {
if ( persistentClass.getEntityPersisterClass() == null ) {
persistentClass.getRootClass().setEntityPersisterClass( SingleTableEntityPersister.class );
}
}
else {
- if ( inheritanceState.hasSons || !discriminatorColumn.isImplicit() ) {
+ if ( inheritanceState.hasSiblings() || !discriminatorColumn.isImplicit() ) {
//need a discriminator column
bindDiscriminatorToPersistentClass(
(RootClass) persistentClass,
@@ -685,8 +602,8 @@
}
}
}
- else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.type ) ) {
- if ( inheritanceState.hasParents ) {
+ else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.getType() ) ) {
+ if ( inheritanceState.hasParents() ) {
if ( persistentClass.getEntityPersisterClass() == null ) {
persistentClass.getRootClass().setEntityPersisterClass( UnionSubclassEntityPersister.class );
}
@@ -699,7 +616,7 @@
}
//try to find class level generators
- HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( annotatedClass, mappings );
+ HashMap<String, IdGenerator> classGenerators = buildLocalGenerators( clazzToProcess, mappings );
// check properties
List<PropertyData> elements =
@@ -709,30 +626,29 @@
if ( elements == null ) {
throw new AnnotationException( "No identifier specified for entity: " + propertyHolder.getEntityName() );
}
- final boolean subclassAndSingleTableStrategy = inheritanceState.type == InheritanceType.SINGLE_TABLE
- && inheritanceState.hasParents;
+ final boolean subclassAndSingleTableStrategy = inheritanceState.getType() == InheritanceType.SINGLE_TABLE
+ && inheritanceState.hasParents();
//process idclass if any
Set<String> idProperties = new HashSet<String>();
IdClass idClass = null;
XClass current = null;
- if ( !inheritanceState.hasParents ) {
+ if ( !inheritanceState.hasParents() ) {
//look for idClass
- current = inheritanceState.clazz;
InheritanceState state = inheritanceState;
do {
- current = state.clazz;
+ current = state.getClazz();
if ( current.isAnnotationPresent( IdClass.class ) ) {
idClass = current.getAnnotation( IdClass.class );
break;
}
state = InheritanceState.getSuperclassInheritanceState(
- current, inheritanceStatePerClass, reflectionManager
+ current, inheritanceStatePerClass, mappings.getReflectionManager()
);
}
while ( state != null );
}
if ( idClass != null ) {
- XClass compositeClass = reflectionManager.toXClass( idClass.value() );
+ XClass compositeClass = mappings.getReflectionManager().toXClass( idClass.value() );
boolean isComponent = true;
boolean propertyAnnotated = entityBinder.isPropertyAnnotated( compositeClass );
String propertyAccessor = entityBinder.getPropertyAccessor( compositeClass );
@@ -837,7 +753,7 @@
);
}
- if ( !inheritanceState.hasParents ) {
+ if ( !inheritanceState.hasParents() ) {
final RootClass rootClass = (RootClass) persistentClass;
mappings.addSecondPass( new CreateKeySecondPass( rootClass ) );
}
@@ -848,14 +764,121 @@
mappings.addClass( persistentClass );
//Process secondary tables and complementary definitions (ie o.h.a.Table)
- mappings.addSecondPass( new SecondaryTableSecondPass( entityBinder, propertyHolder, annotatedClass ) );
+ mappings.addSecondPass( new SecondaryTableSecondPass( entityBinder, propertyHolder, clazzToProcess ) );
//add process complementary Table definition (index & all)
- entityBinder.processComplementaryTableDefinitions( annotatedClass.getAnnotation( org.hibernate.annotations.Table.class ) );
- entityBinder.processComplementaryTableDefinitions( annotatedClass.getAnnotation( org.hibernate.annotations.Tables.class ) );
+ entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Table.class ) );
+ entityBinder.processComplementaryTableDefinitions( clazzToProcess.getAnnotation( org.hibernate.annotations.Tables.class ) );
}
+ private static PersistentClass makePersistentClass(InheritanceState inheritanceState, PersistentClass superEntity) {
+ //we now know what kind of persistent entity it is
+ PersistentClass persistentClass;
+ //create persistent class
+ if ( !inheritanceState.hasParents() ) {
+ persistentClass = new RootClass();
+ }
+ else if ( InheritanceType.SINGLE_TABLE.equals( inheritanceState.getType() ) ) {
+ persistentClass = new SingleTableSubclass( superEntity );
+ }
+ else if ( InheritanceType.JOINED.equals( inheritanceState.getType() ) ) {
+ persistentClass = new JoinedSubclass( superEntity );
+ }
+ else if ( InheritanceType.TABLE_PER_CLASS.equals( inheritanceState.getType() ) ) {
+ persistentClass = new UnionSubclass( superEntity );
+ }
+ else {
+ throw new AssertionFailure( "Unknown inheritance type: " + inheritanceState.getType() );
+ }
+ return persistentClass;
+ }
+
+ private static Ejb3JoinColumn[] makeInheritanceJoinColumns(XClass clazzToProcess, ExtendedMappings mappings, InheritanceState inheritanceState, PersistentClass superEntity) {
+ Ejb3JoinColumn[] inheritanceJoinedColumns = null;
+ final boolean hasJoinedColumns = inheritanceState.hasParents()
+ && InheritanceType.JOINED.equals( inheritanceState.getType() );
+ if ( hasJoinedColumns ) {
+ //@Inheritance(JOINED) subclass need to link back to the super entity
+ PrimaryKeyJoinColumns jcsAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumns.class );
+ boolean explicitInheritanceJoinedColumns = jcsAnn != null && jcsAnn.value().length != 0;
+ if ( explicitInheritanceJoinedColumns ) {
+ int nbrOfInhJoinedColumns = jcsAnn.value().length;
+ PrimaryKeyJoinColumn jcAnn;
+ inheritanceJoinedColumns = new Ejb3JoinColumn[nbrOfInhJoinedColumns];
+ for (int colIndex = 0; colIndex < nbrOfInhJoinedColumns; colIndex++) {
+ jcAnn = jcsAnn.value()[colIndex];
+ inheritanceJoinedColumns[colIndex] = Ejb3JoinColumn.buildJoinColumn(
+ jcAnn, null, superEntity.getIdentifier(),
+ ( Map<String, Join> ) null, ( PropertyHolder ) null, mappings
+ );
+ }
+ }
+ else {
+ PrimaryKeyJoinColumn jcAnn = clazzToProcess.getAnnotation( PrimaryKeyJoinColumn.class );
+ inheritanceJoinedColumns = new Ejb3JoinColumn[1];
+ inheritanceJoinedColumns[0] = Ejb3JoinColumn.buildJoinColumn(
+ jcAnn, null, superEntity.getIdentifier(),
+ (Map<String, Join>) null, (PropertyHolder) null, mappings
+ );
+ }
+ log.debug( "Subclass joined column(s) created" );
+ }
+ else {
+ if ( clazzToProcess.isAnnotationPresent( PrimaryKeyJoinColumns.class )
+ || clazzToProcess.isAnnotationPresent( PrimaryKeyJoinColumn.class ) ) {
+ log.warn( "Root entity should not hold an PrimaryKeyJoinColum(s), will be ignored" );
+ }
+ }
+ return inheritanceJoinedColumns;
+ }
+
+ private static PersistentClass getSuperEntity(XClass clazzToProcess, Map<XClass, InheritanceState> inheritanceStatePerClass, ExtendedMappings mappings, InheritanceState inheritanceState) {
+ final ReflectionManager reflectionManager = mappings.getReflectionManager();
+ InheritanceState superEntityState =
+ InheritanceState.getInheritanceStateOfSuperEntity(
+ clazzToProcess, inheritanceStatePerClass, reflectionManager
+ );
+ PersistentClass superEntity = superEntityState != null ?
+ mappings.getClass(
+ superEntityState.getClazz().getName()
+ ) :
+ null;
+ if ( superEntity == null ) {
+ //check if superclass is not a potential persistent class
+ if ( inheritanceState.hasParents() ) {
+ throw new AssertionFailure(
+ "Subclass has to be binded after it's mother class: "
+ + superEntityState.getClazz().getName()
+ );
+ }
+ }
+ return superEntity;
+ }
+
+ private static boolean isEntityClassType(XClass clazzToProcess, AnnotatedClassType classType) {
+ if ( AnnotatedClassType.EMBEDDABLE_SUPERCLASS.equals( classType ) //will be processed by their subentities
+ || AnnotatedClassType.NONE.equals( classType ) //to be ignored
+ || AnnotatedClassType.EMBEDDABLE.equals( classType ) //allow embeddable element declaration
+ ) {
+ if ( AnnotatedClassType.NONE.equals( classType )
+ && clazzToProcess.isAnnotationPresent( org.hibernate.annotations.Entity.class ) ) {
+ log.warn( "Class annotated @org.hibernate.annotations.Entity but not javax.persistence.Entity "
+ + "(most likely a user error): {}", clazzToProcess.getName() );
+ }
+ return false;
+ }
+
+ if ( !classType.equals( AnnotatedClassType.ENTITY ) ) {
+ throw new AnnotationException(
+ "Annotated class should have a @javax.persistence.Entity, @javax.persistence.Embeddable or @javax.persistence.EmbeddedSuperclass annotation: " + clazzToProcess
+ .getName()
+ );
+ }
+
+ return true;
+ }
+
/**
* Get the annotated elements
* Guess the annotated element from @Id or @EmbeddedId presence
@@ -874,19 +897,19 @@
int deep = classesToProcess.size();
boolean hasIdentifier = false;
- assert !inheritanceState.isEmbeddableSuperclass;
+ assert !inheritanceState.isEmbeddableSuperclass();
Boolean isExplicitPropertyAnnotated = null;
String explicitAccessType;
- if ( inheritanceState.hasParents ) {
+ if ( inheritanceState.hasParents() ) {
InheritanceState superEntityState =
- InheritanceState.getSuperEntityInheritanceState(
+ InheritanceState.getInheritanceStateOfSuperEntity(
clazzToProcess, inheritanceStatePerClass, mappings.getReflectionManager()
);
isExplicitPropertyAnnotated = superEntityState != null ?
- superEntityState.isPropertyAnnotated :
+ superEntityState.isPropertyAnnotated() :
null;
explicitAccessType = superEntityState != null ?
- superEntityState.accessType :
+ superEntityState.getAccessType() :
null;
}
else {
@@ -928,7 +951,7 @@
exceptionWhileWalkingElements = e;
}
- if ( !hasIdentifier && !inheritanceState.hasParents ) {
+ if ( !hasIdentifier && !inheritanceState.hasParents() ) {
if ( isExplicitPropertyAnnotated != null ) {
//the original exception is legitimate
if ( exceptionWhileWalkingElements != null) throw exceptionWhileWalkingElements;
@@ -953,9 +976,9 @@
//TODO set the access type here?
entityBinder.setPropertyAnnotated( isPropertyAnnotated );
entityBinder.setPropertyAccessor( accessType );
- inheritanceState.isPropertyAnnotated = isPropertyAnnotated;
- inheritanceState.accessType = accessType;
- return hasIdentifier || inheritanceState.hasParents ?
+ inheritanceState.setPropertyAnnotated( isPropertyAnnotated );
+ inheritanceState.setAccessType( accessType );
+ return hasIdentifier || inheritanceState.hasParents() ?
elements :
null;
}
@@ -983,16 +1006,16 @@
currentClassInHierarchy = superClass;
}
- while ( superclassState != null && superclassState.isEmbeddableSuperclass );
+ while ( superclassState != null && superclassState.isEmbeddableSuperclass() );
//add @MappedSuperclass in the metadata
// classes from 0 to n-1 are @MappedSuperclass and should be linked
org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
final InheritanceState superEntityState =
- InheritanceState.getSuperEntityInheritanceState(annotatedClass, inheritanceStatePerClass, reflectionManager);
+ InheritanceState.getInheritanceStateOfSuperEntity(annotatedClass, inheritanceStatePerClass, reflectionManager);
PersistentClass superEntity =
superEntityState != null ?
- mappings.getClass( superEntityState.clazz.getName() ) :
+ mappings.getClass( superEntityState.getClazz().getName() ) :
null;
final int lastMappedSuperclass = classesToProcess.size() - 1;
for ( int index = 0 ; index < lastMappedSuperclass ; index++ ) {
@@ -1139,7 +1162,7 @@
String localPropertyAccessor = access != null ?
access.value() :
null;
- String accessType = null;
+ String accessType;
if ( "property".equals( localPropertyAccessor ) || "field".equals( localPropertyAccessor ) ) {
accessType = localPropertyAccessor;
}
@@ -1579,8 +1602,6 @@
}
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
- NotFound notFound = property.getAnnotation( NotFound.class );
- boolean ignoreNotFound = notFound != null && notFound.action().equals( NotFoundAction.IGNORE );
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE.equals( onDeleteAnn.action() );
JoinTable assocTable = property.getAnnotation( JoinTable.class );
@@ -1654,7 +1675,7 @@
collectionBinder.setMappings( mappings );
collectionBinder.setPropertyAccessorName( inferredData.getDefaultAccess() );
- Ejb3Column[] elementColumns = null;
+ Ejb3Column[] elementColumns;
PropertyData virtualProperty = new WrappedInferredData( inferredData, "element" );
if ( property.isAnnotationPresent( Column.class ) || property.isAnnotationPresent(
Formula.class
@@ -1860,7 +1881,7 @@
}
else {
//define whether the type is a component or not
- boolean isComponent = false;
+ boolean isComponent;
Embeddable embeddableAnn = returnedClass.getAnnotation( Embeddable.class );
Embedded embeddedAnn = property.getAnnotation( Embedded.class );
isComponent = embeddedAnn != null || embeddableAnn != null;
@@ -2185,9 +2206,7 @@
);
}
RootClass rootClass = (RootClass) persistentClass;
- String persistentClassName = rootClass == null ?
- null :
- rootClass.getClassName();
+ String persistentClassName = rootClass.getClassName();
SimpleValue id;
if ( isComposite ) {
id = fillComponent(
@@ -2620,6 +2639,14 @@
return mappings.getReflectionManager().equals( clazz, void.class );
}
+ /**
+ * For the mapped entities build some temporary data-structure containing information about the
+ * inheritance status of a class.
+ *
+ * @param orderedClasses Order list of all annotated entities and their mapped superclasses
+ * @param reflectionManager Reference to the reflection manager (commons-annotations)
+ * @return A map of {@code InheritanceState}s keyed against their {@code XClass}.
+ */
public static Map<XClass, InheritanceState> buildInheritanceStates(
List<XClass> orderedClasses, ReflectionManager reflectionManager
) {
@@ -2635,22 +2662,22 @@
if ( superclassState != null ) {
//the classes are ordered thus preventing an NPE
//FIXME if an entity has subclasses annotated @MappedSperclass wo sub @Entity this is wrong
- superclassState.hasSons = true;
- InheritanceState superEntityState = InheritanceState.getSuperEntityInheritanceState(
+ superclassState.setHasSiblings( true );
+ InheritanceState superEntityState = InheritanceState.getInheritanceStateOfSuperEntity(
clazz, inheritanceStatePerClass,
reflectionManager
);
- state.hasParents = superEntityState != null;
- final boolean nonDefault = state.type != null && !InheritanceType.SINGLE_TABLE.equals( state.type );
- if ( superclassState.type != null ) {
- final boolean mixingStrategy = state.type != null && !state.type.equals( superclassState.type );
+ state.setHasParents( superEntityState != null );
+ final boolean nonDefault = state.getType() != null && !InheritanceType.SINGLE_TABLE.equals( state.getType() );
+ if ( superclassState.getType() != null ) {
+ final boolean mixingStrategy = state.getType() != null && !state.getType().equals( superclassState.getType() );
if ( nonDefault && mixingStrategy ) {
log.warn(
"Mixing inheritance strategy in a entity hierarchy is not allowed, ignoring sub strategy in: {}",
clazz.getName()
);
}
- state.type = superclassState.type;
+ state.setType( superclassState.getType() );
}
}
inheritanceStatePerClass.put( clazz, state );
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/AnnotationConfiguration.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -123,7 +123,6 @@
private Set<String> defaultNamedGenerators;
private Map<String, Properties> generatorTables;
private Map<Table, List<UniqueConstraintHolder>> uniqueConstraintHoldersByTable;
-// private Map<Table, List<String[]>> tableUniqueConstraints;
private Map<String, String> mappedByResolver;
private Map<String, String> propertyRefResolver;
private Map<String, AnyMetaDef> anyMetaDefs;
@@ -146,10 +145,30 @@
super( sf );
}
+ /**
+ * Takes the list of entities annotated with {@code @Entity} or {@code @MappedSuperclass} and returns them in an
+ * ordered list.
+ *
+ * @param original The list of all entities annotated with {@code @Entity} or {@code @MappedSuperclass}
+ * @return Ordered list of entities including superclasses for entities which have any. Class hierachies are
+ * listed bottom up (starting from the top level base class). There is no indication in the list when a new class
+ * (hierarchy) starts.
+ */
protected List<XClass> orderAndFillHierarchy(List<XClass> original) {
- //TODO remove embeddable
List<XClass> copy = new ArrayList<XClass>( original );
- //for each class, copy all the relevant hierarchy
+ insertMappedSuperclasses( original, copy );
+
+ // order the hierarchy
+ List<XClass> workingCopy = new ArrayList<XClass>( copy );
+ List<XClass> newList = new ArrayList<XClass>( copy.size() );
+ while ( workingCopy.size() > 0 ) {
+ XClass clazz = workingCopy.get( 0 );
+ orderHierarchy( workingCopy, newList, copy, clazz );
+ }
+ return newList;
+ }
+
+ private void insertMappedSuperclasses(List<XClass> original, List<XClass> copy) {
for ( XClass clazz : original ) {
XClass superClass = clazz.getSuperclass();
while ( superClass != null && !reflectionManager.equals( superClass, Object.class ) && !copy.contains(
@@ -162,13 +181,6 @@
superClass = superClass.getSuperclass();
}
}
- List<XClass> workingCopy = new ArrayList<XClass>( copy );
- List<XClass> newList = new ArrayList<XClass>( copy.size() );
- while ( workingCopy.size() > 0 ) {
- XClass clazz = workingCopy.get( 0 );
- orderHierarchy( workingCopy, newList, copy, clazz );
- }
- return newList;
}
private void orderHierarchy(List<XClass> copy, List<XClass> newList, List<XClass> original, XClass clazz) {
@@ -598,6 +610,7 @@
orderedClasses, reflectionManager
);
ExtendedMappings mappings = createExtendedMappings();
+
for ( XClass clazz : orderedClasses ) {
//todo use the same extended mapping
AnnotationBinder.bindClass( clazz, inheritanceStatePerClass, mappings );
@@ -1166,20 +1179,6 @@
}
protected class ExtendedMappingsImpl extends MappingsImpl implements ExtendedMappings {
-// private final Map<String, IdGenerator> namedGenerators;
-// private final Map<String, Map<String, Join>> joins;
-// private final Map<String, AnnotatedClassType> classTypes;
-// private final Map<String, Properties> generatorTables;
-// private final Map<Table, List<String[]>> tableUniqueConstraints;
-// private final Map<String, String> mappedByResolver;
-// private final Map<String, String> propertyRefResolver;
-// private final ReflectionManager reflectionManager;
-// private final Set<String> defaultNamedQueryNames;
-// private final Set<String> defaultNamedNativeQueryNames;
-// private final Set<String> defaultSqlResulSetMappingNames;
-// private final Set<String> defaultNamedGenerators;
-// private final Map<String, AnyMetaDef> anyMetaDefs;
-
public void addDefaultGenerator(IdGenerator generator) {
this.addGenerator( generator );
defaultNamedGenerators.add( generator.getName() );
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/BinderHelper.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -591,7 +591,7 @@
"Declaring class is not found in the inheritance state hierarchy: " + declaringClass
);
}
- if ( inheritanceState.isEmbeddableSuperclass ) {
+ if ( inheritanceState.isEmbeddableSuperclass() ) {
retrieve = true;
}
}
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/ClassPropertyHolder.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/ClassPropertyHolder.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/ClassPropertyHolder.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -113,7 +113,7 @@
"Declaring class is not found in the inheritance state hierarchy: " + declaringClass
);
}
- if ( inheritanceState.isEmbeddableSuperclass ) {
+ if ( inheritanceState.isEmbeddableSuperclass() ) {
persistentClass.addMappedsuperclassProperty(prop);
addPropertyToMappedSuperclass( prop, declaringClass );
}
@@ -141,7 +141,7 @@
"Declaring class is not found in the inheritance state hierarchy: " + declaringClass
);
}
- if ( inheritanceState.isEmbeddableSuperclass ) {
+ if ( inheritanceState.isEmbeddableSuperclass() ) {
join.addMappedsuperclassProperty(prop);
addPropertyToMappedSuperclass( prop, declaringClass );
}
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/InheritanceState.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -33,56 +33,58 @@
import org.hibernate.annotations.common.reflection.XClass;
/**
- * Some extra data to the inheritance position of a class
+ * Some extra data to the inheritance position of a class.
*
* @author Emmanuel Bernard
*/
public class InheritanceState {
public InheritanceState(XClass clazz) {
- this.clazz = clazz;
+ this.setClazz( clazz );
extractInheritanceType();
}
- public XClass clazz;
+ private XClass clazz;
+
/**
- * has son either mappedsuperclass son or entity son
+ * Has sibling (either mappedsuperclass entity)
*/
- public boolean hasSons = false;
+ private boolean hasSiblings = false;
+
/**
* a mother entity is available
*/
- public boolean hasParents = false;
- public InheritanceType type;
- public boolean isEmbeddableSuperclass = false;
+ private boolean hasParents = false;
+ private InheritanceType type;
+ private boolean isEmbeddableSuperclass = false;
/**
* only defined on embedded superclasses
*/
- public String accessType = null;
- public Boolean isPropertyAnnotated;
+ private String accessType = null;
+ private Boolean isPropertyAnnotated;
private void extractInheritanceType() {
- XAnnotatedElement element = clazz;
+ XAnnotatedElement element = getClazz();
Inheritance inhAnn = element.getAnnotation( Inheritance.class );
MappedSuperclass mappedSuperClass = element.getAnnotation( MappedSuperclass.class );
if ( mappedSuperClass != null ) {
- isEmbeddableSuperclass = true;
- type = inhAnn == null ? null : inhAnn.strategy();
+ setEmbeddableSuperclass( true );
+ setType( inhAnn == null ? null : inhAnn.strategy() );
}
else {
- type = inhAnn == null ? InheritanceType.SINGLE_TABLE : inhAnn.strategy();
+ setType( inhAnn == null ? InheritanceType.SINGLE_TABLE : inhAnn.strategy() );
}
}
boolean hasTable() {
- return !hasParents || !InheritanceType.SINGLE_TABLE.equals( type );
+ return !hasParents() || !InheritanceType.SINGLE_TABLE.equals( getType() );
}
boolean hasDenormalizedTable() {
- return hasParents && InheritanceType.TABLE_PER_CLASS.equals( type );
+ return hasParents() && InheritanceType.TABLE_PER_CLASS.equals( getType() );
}
- public static InheritanceState getSuperEntityInheritanceState(
+ public static InheritanceState getInheritanceStateOfSuperEntity(
XClass clazz, Map<XClass, InheritanceState> states,
ReflectionManager reflectionManager
) {
@@ -90,7 +92,9 @@
do {
superclass = superclass.getSuperclass();
InheritanceState currentState = states.get( superclass );
- if ( currentState != null && !currentState.isEmbeddableSuperclass ) return currentState;
+ if ( currentState != null && !currentState.isEmbeddableSuperclass() ) {
+ return currentState;
+ }
}
while ( superclass != null && !reflectionManager.equals( superclass, Object.class ) );
return null;
@@ -109,4 +113,60 @@
while ( superclass != null && !reflectionManager.equals( superclass, Object.class ) );
return null;
}
+
+ public XClass getClazz() {
+ return clazz;
+ }
+
+ public void setClazz(XClass clazz) {
+ this.clazz = clazz;
+ }
+
+ public boolean hasSiblings() {
+ return hasSiblings;
+ }
+
+ public void setHasSiblings(boolean hasSiblings) {
+ this.hasSiblings = hasSiblings;
+ }
+
+ public boolean hasParents() {
+ return hasParents;
+ }
+
+ public void setHasParents(boolean hasParents) {
+ this.hasParents = hasParents;
+ }
+
+ public InheritanceType getType() {
+ return type;
+ }
+
+ public void setType(InheritanceType type) {
+ this.type = type;
+ }
+
+ public boolean isEmbeddableSuperclass() {
+ return isEmbeddableSuperclass;
+ }
+
+ public void setEmbeddableSuperclass(boolean embeddableSuperclass) {
+ isEmbeddableSuperclass = embeddableSuperclass;
+ }
+
+ public String getAccessType() {
+ return accessType;
+ }
+
+ public void setAccessType(String accessType) {
+ this.accessType = accessType;
+ }
+
+ public Boolean isPropertyAnnotated() {
+ return isPropertyAnnotated;
+ }
+
+ public void setPropertyAnnotated(Boolean propertyAnnotated) {
+ isPropertyAnnotated = propertyAnnotated;
+ }
}
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/annotations/EntityBinder.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -294,7 +294,7 @@
persistentClass.addTuplizer( mode, tuplizer.impl().getName() );
}
- if ( !inheritanceState.hasParents ) {
+ if ( !inheritanceState.hasParents() ) {
for ( Map.Entry<String, String> filter : filters.entrySet() ) {
String filterName = filter.getKey();
String cond = filter.getValue();
Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/access/AccessTest.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/access/AccessTest.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/access/AccessTest.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -9,7 +9,7 @@
* @author Emmanuel Bernard
*/
public class AccessTest extends TestCase {
-
+
public void testSuperclassOverriding() throws Exception {
Furniture fur = new Furniture();
fur.setColor( "Black" );
@@ -21,7 +21,7 @@
tx.commit();
s.clear();
tx = s.beginTransaction();
- fur = (Furniture) s.get( Furniture.class, fur.getId() );
+ fur = ( Furniture ) s.get( Furniture.class, fur.getId() );
assertFalse( fur.isAlive );
assertNotNull( fur.getColor() );
s.delete( fur );
@@ -38,7 +38,7 @@
tx.commit();
s.clear();
tx = s.beginTransaction();
- fur = (Furniture) s.get( Furniture.class, fur.getId() );
+ fur = ( Furniture ) s.get( Furniture.class, fur.getId() );
assertNotNull( fur.getGod() );
s.delete( fur );
tx.commit();
@@ -54,12 +54,11 @@
tx.commit();
s.clear();
tx = s.beginTransaction();
- fur = (Furniture) s.get( Furniture.class, fur.getId() );
+ fur = ( Furniture ) s.get( Furniture.class, fur.getId() );
assertEquals( 5, fur.weight );
s.delete( fur );
tx.commit();
s.close();
-
}
public void testNonOverridenSubclass() throws Exception {
@@ -71,12 +70,11 @@
tx.commit();
s.clear();
tx = s.beginTransaction();
- chair = (Chair) s.get( Chair.class, chair.getId() );
+ chair = ( Chair ) s.get( Chair.class, chair.getId() );
assertNull( chair.getPillow() );
s.delete( chair );
tx.commit();
s.close();
-
}
public void testOverridenSubclass() throws Exception {
@@ -89,13 +87,12 @@
tx.commit();
s.clear();
tx = s.beginTransaction();
- bed = (BigBed) s.get( BigBed.class, bed.getId() );
+ bed = ( BigBed ) s.get( BigBed.class, bed.getId() );
assertEquals( 5, bed.size );
assertNull( bed.getQuality() );
s.delete( bed );
tx.commit();
s.close();
-
}
public void testFieldsOverriding() throws Exception {
@@ -107,22 +104,21 @@
tx.commit();
s.clear();
tx = s.beginTransaction();
- gs = (Gardenshed) s.get( Gardenshed.class, gs.getId() );
+ gs = ( Gardenshed ) s.get( Gardenshed.class, gs.getId() );
assertEquals( 4, gs.floors );
assertEquals( 6, gs.getFloors() );
s.delete( gs );
tx.commit();
s.close();
-
}
protected Class[] getMappings() {
- return new Class[]{
+ return new Class[] {
Bed.class,
Chair.class,
Furniture.class,
BigBed.class,
- Gardenshed.class
+ Gardenshed.class,
};
}
}
Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/access/Furniture.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/access/Furniture.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/access/Furniture.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -37,7 +37,6 @@
this.id = id;
}
-
@AccessType("property")
public long weight;
Modified: core/trunk/annotations/src/test/java/org/hibernate/test/annotations/embedded/Country.java
===================================================================
--- core/trunk/annotations/src/test/java/org/hibernate/test/annotations/embedded/Country.java 2009-12-11 16:44:23 UTC (rev 18210)
+++ core/trunk/annotations/src/test/java/org/hibernate/test/annotations/embedded/Country.java 2009-12-11 19:14:01 UTC (rev 18211)
@@ -13,7 +13,6 @@
* @author Emmanuel Bernard
*/
@Embeddable
-//access = AccessType.PROPERTY)
@AccessType("property")
public class Country implements Serializable {
private String iso2;
@@ -35,5 +34,4 @@
public void setName(String name) {
this.name = name;
}
-
}
15 years
Hibernate SVN: r18210 - core/trunk/cache-infinispan.
by hibernate-commits@lists.jboss.org
Author: galder.zamarreno(a)jboss.com
Date: 2009-12-11 11:44:23 -0500 (Fri, 11 Dec 2009)
New Revision: 18210
Modified:
core/trunk/cache-infinispan/pom.xml
Log:
Upgraded to Infinispan 4.0.0.CR3. It no longer depends on a transitive rhq/jopr dependency :)
Modified: core/trunk/cache-infinispan/pom.xml
===================================================================
--- core/trunk/cache-infinispan/pom.xml 2009-12-11 13:23:17 UTC (rev 18209)
+++ core/trunk/cache-infinispan/pom.xml 2009-12-11 16:44:23 UTC (rev 18210)
@@ -17,7 +17,7 @@
<description>Integration of Hibernate with Infinispan</description>
<properties>
- <version.infinispan>4.0.0-SNAPSHOT</version.infinispan>
+ <version.infinispan>4.0.0.CR3</version.infinispan>
<version.hsqldb>1.8.0.2</version.hsqldb>
<version.cglib>2.2</version.cglib>
<version.javassist>3.4.GA</version.javassist>
15 years
Hibernate SVN: r18209 - in core/trunk: core/src/main/java/org/hibernate/exception and 2 other directories.
by hibernate-commits@lists.jboss.org
Author: smarlow(a)redhat.com
Date: 2009-12-11 08:23:17 -0500 (Fri, 11 Dec 2009)
New Revision: 18209
Modified:
core/trunk/core/src/main/java/org/hibernate/PessimisticLockException.java
core/trunk/core/src/main/java/org/hibernate/exception/SQLStateConverter.java
core/trunk/core/src/main/java/org/hibernate/loader/Loader.java
core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
core/trunk/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java
Log:
HHH-4546 JPA-2.0 locking. More pessimistic lock exception support and timeout exception support
Modified: core/trunk/core/src/main/java/org/hibernate/PessimisticLockException.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/PessimisticLockException.java 2009-12-11 12:40:05 UTC (rev 18208)
+++ core/trunk/core/src/main/java/org/hibernate/PessimisticLockException.java 2009-12-11 13:23:17 UTC (rev 18209)
@@ -24,29 +24,33 @@
*/
package org.hibernate;
+import java.sql.SQLException;
+
/**
*
- * Throw when an pessimistic locking conflict occurs.
+ * Thrown when a pessimistic locking conflict occurs.
*
* @author Scott Marlow
*/
-public class PessimisticLockException extends HibernateException {
+public class PessimisticLockException extends JDBCException {
Object entity;
- public PessimisticLockException(String s) {
- super(s);
- }
- public PessimisticLockException(String s, Throwable throwable, Object entity) {
- super(s, throwable);
+ public PessimisticLockException(String s, JDBCException je, Object entity) {
+ super(s, je.getSQLException());
+ this.entity = entity;
+ }
+
+ public PessimisticLockException(String s, SQLException se, Object entity) {
+ super(s, se);
this.entity = entity;
}
- public PessimisticLockException(String s, Object entity) {
- super(s);
- this.entity = entity;
- }
+ public PessimisticLockException(String s, SQLException se, String sql) {
+ super(s, se, sql);
+ this.entity = null;
+ }
public Object getEntity() {
return entity;
Modified: core/trunk/core/src/main/java/org/hibernate/exception/SQLStateConverter.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/exception/SQLStateConverter.java 2009-12-11 12:40:05 UTC (rev 18208)
+++ core/trunk/core/src/main/java/org/hibernate/exception/SQLStateConverter.java 2009-12-11 13:23:17 UTC (rev 18209)
@@ -25,6 +25,7 @@
package org.hibernate.exception;
import org.hibernate.JDBCException;
+import org.hibernate.PessimisticLockException;
import java.sql.SQLException;
import java.util.HashSet;
@@ -109,6 +110,11 @@
// oracle sql-state code for deadlock
return new LockAcquisitionException( message, sqlException, sql );
}
+
+ if ( "40XL1".equals( sqlState ) || "40XL2".equals( sqlState )) {
+ // Derby "A lock could not be obtained within the time requested."
+ return new PessimisticLockException( message, sqlException, sql );
+ }
}
return handledNonSpecificException( sqlException, message, sql );
Modified: core/trunk/core/src/main/java/org/hibernate/loader/Loader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/Loader.java 2009-12-11 12:40:05 UTC (rev 18208)
+++ core/trunk/core/src/main/java/org/hibernate/loader/Loader.java 2009-12-11 13:23:17 UTC (rev 18209)
@@ -214,14 +214,35 @@
return null;
}
+ private Map buildLockMap(Map locks) {
+ Map result = locks;
+ if ( result == null ) {
+ LockOptions[] lockArray = getLockOptions(result);
+ String[] aliases = getAliases();
+ if (aliases != null &&
+ lockArray != null &&
+ lockArray.length > 0 &&
+ lockArray.length == aliases.length &&
+ lockArray[0].getLockMode() != LockMode.NONE ) {
+ result = new HashMap();
+ for ( int looper = 0; looper < lockArray.length; looper++ ) {
+ result.put(aliases[looper], lockArray[looper]);
+ }
+ }
+ }
+
+ return result;
+ }
/**
* Modify the SQL, adding lock hints and comments, if necessary
*/
protected String preprocessSQL(String sql, QueryParameters parameters, Dialect dialect)
throws HibernateException {
+
+ Map locks = buildLockMap(parameters.getLockOptions());
+
+ sql = applyLocks( sql, locks, dialect );
- sql = applyLocks( sql, parameters.getLockOptions(), dialect );
-
return getFactory().getSettings().isCommentsEnabled() ?
prependComment( sql, parameters ) : sql;
}
@@ -700,9 +721,9 @@
//
// Would need to change the way the max-row stuff is handled (i.e. behind an interface) so
// that I could do the control breaking at the means to know when to stop
+
+ final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
final LockOptions[] lockOptionsArray = getLockOptions( queryParameters.getLockOptions() );
- final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
-
final boolean createSubselects = isSubselectLoadingEnabled();
final List subselectResultKeys = createSubselects ? new ArrayList() : null;
final List results = new ArrayList();
Modified: core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java
===================================================================
--- core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java 2009-12-11 12:40:05 UTC (rev 18208)
+++ core/trunk/core/src/main/java/org/hibernate/loader/OuterJoinLoader.java 2009-12-11 13:23:17 UTC (rev 18209)
@@ -25,9 +25,7 @@
package org.hibernate.loader;
import java.util.Map;
-import java.util.Set;
-import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionFactoryImplementor;
Modified: core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java
===================================================================
--- core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java 2009-12-11 12:40:05 UTC (rev 18208)
+++ core/trunk/entitymanager/src/main/java/org/hibernate/ejb/AbstractEntityManagerImpl.java 2009-12-11 13:23:17 UTC (rev 18209)
@@ -257,9 +257,11 @@
}
public <A> A find(Class<A> entityClass, Object primaryKey, LockModeType lockModeType, Map<String, Object> properties) {
+ LockOptions lockOptions = null;
try {
if ( lockModeType != null )
- return ( A ) getSession().get( entityClass, ( Serializable ) primaryKey, getLockRequest(lockModeType, properties) );
+ return ( A ) getSession().get( entityClass, ( Serializable ) primaryKey,
+ ( lockOptions = getLockRequest(lockModeType, properties) ) );
else
return ( A ) getSession().get( entityClass, ( Serializable ) primaryKey );
}
@@ -281,7 +283,7 @@
throw new IllegalArgumentException( e.getMessage(), e );
}
catch ( HibernateException he ) {
- throw convert( he );
+ throw convert( he , lockOptions );
}
}
@@ -351,12 +353,13 @@
public void refresh(Object entity, LockModeType lockModeType, Map<String, Object> properties) {
checkTransactionNeeded();
+ LockOptions lockOptions = null;
try {
if ( !getSession().contains( entity ) ) {
throw new IllegalArgumentException( "Entity not managed" );
}
if(lockModeType != null)
- getSession().refresh( entity, getLockRequest(lockModeType, properties) );
+ getSession().refresh( entity, (lockOptions = getLockRequest(lockModeType, properties) ) );
else
getSession().refresh( entity );
}
@@ -364,7 +367,7 @@
throw new IllegalArgumentException( e.getMessage(), e );
}
catch ( HibernateException he ) {
- throw convert( he );
+ throw convert( he, lockOptions);
}
}
@@ -515,6 +518,7 @@
}
public void lock(Object entity, LockModeType lockModeType, Map<String, Object> properties) {
+ LockOptions lockOptions = null;
try {
if ( !isTransactionInProgress() ) {
throw new TransactionRequiredException( "no transaction is in progress" );
@@ -523,10 +527,10 @@
if ( !contains( entity ) ) {
throw new IllegalArgumentException( "entity not in the persistence context" );
}
- getSession().buildLockRequest(getLockRequest(lockModeType, properties)).lock( entity );
+ getSession().buildLockRequest( (lockOptions = getLockRequest(lockModeType, properties))).lock( entity );
}
catch ( HibernateException he ) {
- throw convert( he );
+ throw convert( he , lockOptions);
}
}
@@ -835,18 +839,25 @@
* {@inheritDoc}
*/
public RuntimeException convert(HibernateException e) {
+ return convert(e, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public RuntimeException convert(HibernateException e, LockOptions lockOptions) {
if ( e instanceof StaleStateException ) {
PersistenceException converted = wrapStaleStateException( ( StaleStateException ) e );
handlePersistenceException( converted );
return converted;
}
else if ( e instanceof org.hibernate.OptimisticLockException ) {
- PersistenceException converted = wrapLockException(e);
+ PersistenceException converted = wrapLockException(e, lockOptions);
handlePersistenceException( converted );
return converted;
}
else if ( e instanceof org.hibernate.PessimisticLockException ) {
- PersistenceException converted = wrapLockException(e);
+ PersistenceException converted = wrapLockException(e, lockOptions);
handlePersistenceException( converted );
return converted;
}
@@ -925,7 +936,7 @@
return pe;
}
- public PersistenceException wrapLockException(HibernateException e) {
+ public PersistenceException wrapLockException(HibernateException e, LockOptions lockOptions) {
PersistenceException pe;
if ( e instanceof org.hibernate.OptimisticLockException ) {
org.hibernate.OptimisticLockException ole = (org.hibernate.OptimisticLockException)e;
@@ -933,7 +944,13 @@
}
else if ( e instanceof org.hibernate.PessimisticLockException ) {
org.hibernate.PessimisticLockException ple = (org.hibernate.PessimisticLockException)e;
- pe = new PessimisticLockException(ple.getMessage(), ple, ple.getEntity());
+ if (lockOptions !=null && lockOptions.getTimeOut() > -1) {
+ // assume lock timeout occurred if a timeout or NO WAIT was specified
+ pe = new LockTimeoutException(ple.getMessage(), ple, ple.getEntity());
+ }
+ else {
+ pe = new PessimisticLockException(ple.getMessage(), ple, ple.getEntity());
+ }
}
else {
pe = new OptimisticLockException( e );
Modified: core/trunk/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java
===================================================================
--- core/trunk/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java 2009-12-11 12:40:05 UTC (rev 18208)
+++ core/trunk/entitymanager/src/main/java/org/hibernate/ejb/HibernateEntityManagerImplementor.java 2009-12-11 13:23:17 UTC (rev 18209)
@@ -27,6 +27,7 @@
import org.hibernate.HibernateException;
import org.hibernate.StaleStateException;
+import org.hibernate.LockOptions;
/**
* Additional internal contracts for the Hibernate {@link javax.persistence.EntityManager} implementation.
@@ -64,8 +65,20 @@
* Any appropriate/needed calls to {@link #handlePersistenceException} are also made.
*
* @param e The Hibernate excepton.
+ * @param lockOptions The lock options in effect at the time of exception (can be null)
* @return The JPA-specified exception
*/
+ public RuntimeException convert(HibernateException e, LockOptions lockOptions);
+
+ /**
+ * Converts a Hibernate-specific exception into a JPA-specified exception; note that the JPA sepcification makes use
+ * of exceptions outside its exception hierarchy, though they are all runtime exceptions.
+ * <p/>
+ * Any appropriate/needed calls to {@link #handlePersistenceException} are also made.
+ *
+ * @param e The Hibernate excepton.
+ * @return The JPA-specified exception
+ */
public RuntimeException convert(HibernateException e);
/**
15 years
Hibernate SVN: r18208 - in core/trunk/envers/src: main/java/org/hibernate/envers/synchronization/work and 1 other directories.
by hibernate-commits@lists.jboss.org
Author: adamw
Date: 2009-12-11 07:40:05 -0500 (Fri, 11 Dec 2009)
New Revision: 18208
Added:
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeDispatcher.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeVisitor.java
Removed:
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckDispatcher.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckResult.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckVisitor.java
Modified:
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditSync.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AbstractAuditWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AddWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AuditWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/CollectionChangeWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/DelWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/ModWorkUnit.java
core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushAddMod.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushModDel.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushModMod.java
core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/ManualFlush.java
Log:
HHH-4670:
- changing the way work unit collision is resolved: now work units are merged, the result is a work unit (possibly new)
- work units should behave as immutable objects
- test
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditSync.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditSync.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/AuditSync.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -86,22 +86,16 @@
if (usedIds.containsKey(usedIdsKey)) {
AuditWorkUnit other = usedIds.get(usedIdsKey);
- // The entity with entityId has two work units; checking which one should be kept.
- switch (vwu.dispatch(other)) {
- case FIRST:
- // Simply not adding the second
- break;
+ AuditWorkUnit result = vwu.dispatch(other);
- case SECOND:
- removeWorkUnit(other);
- usedIds.put(usedIdsKey, vwu);
- workUnits.offer(vwu);
- break;
+ if (result != other) {
+ removeWorkUnit(other);
- case NONE:
- removeWorkUnit(other);
- break;
- }
+ if (result != null) {
+ usedIds.put(usedIdsKey, result);
+ workUnits.offer(result);
+ } // else: a null result means that no work unit should be kept
+ } // else: the result is the same as the work unit already added. No need to do anything.
} else {
usedIds.put(usedIdsKey, vwu);
workUnits.offer(vwu);
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AbstractAuditWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AbstractAuditWorkUnit.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AbstractAuditWorkUnit.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -41,9 +41,8 @@
protected final SessionImplementor sessionImplementor;
protected final AuditConfiguration verCfg;
protected final Serializable id;
+ protected final String entityName;
- private final String entityName;
-
private Object performedData;
protected AbstractAuditWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AddWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AddWorkUnit.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AddWorkUnit.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -38,50 +38,53 @@
* @author Adam Warski (adam at warski dot org)
*/
public class AddWorkUnit extends AbstractAuditWorkUnit implements AuditWorkUnit {
- private final Object[] state;
- private final String[] propertyNames;
+ private final Map<String, Object> data;
public AddWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
Serializable id, EntityPersister entityPersister, Object[] state) {
super(sessionImplementor, entityName, verCfg, id);
- this.state = state;
- this.propertyNames = entityPersister.getPropertyNames();
+ data = new HashMap<String, Object>();
+ verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(sessionImplementor, data,
+ entityPersister.getPropertyNames(), state, null);
}
+ public AddWorkUnit(SessionImplementor sessionImplementor, String entityName, AuditConfiguration verCfg,
+ Serializable id, Map<String, Object> data) {
+ super(sessionImplementor, entityName, verCfg, id);
+
+ this.data = data;
+ }
+
public boolean containsWork() {
return true;
}
public void perform(Session session, Object revisionData) {
- Map<String, Object> data = new HashMap<String, Object>();
fillDataWithId(data, revisionData, RevisionType.ADD);
- verCfg.getEntCfg().get(getEntityName()).getPropertyMapper().map(sessionImplementor, data,
- propertyNames, state, null);
-
session.save(verCfg.getAuditEntCfg().getAuditEntityName(getEntityName()), data);
setPerformed(data);
}
- public KeepCheckResult check(AddWorkUnit second) {
- return KeepCheckResult.FIRST;
+ public AuditWorkUnit merge(AddWorkUnit second) {
+ return second;
}
- public KeepCheckResult check(ModWorkUnit second) {
- return KeepCheckResult.SECOND;
+ public AuditWorkUnit merge(ModWorkUnit second) {
+ return new AddWorkUnit(sessionImplementor, entityName, verCfg, id, second.getData());
}
- public KeepCheckResult check(DelWorkUnit second) {
- return KeepCheckResult.NONE;
+ public AuditWorkUnit merge(DelWorkUnit second) {
+ return null;
}
- public KeepCheckResult check(CollectionChangeWorkUnit second) {
- return KeepCheckResult.FIRST;
+ public AuditWorkUnit merge(CollectionChangeWorkUnit second) {
+ return this;
}
- public KeepCheckResult dispatch(KeepCheckVisitor first) {
- return first.check(this);
+ public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
+ return first.merge(this);
}
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AuditWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AuditWorkUnit.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/AuditWorkUnit.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -28,7 +28,7 @@
/**
* @author Adam Warski (adam at warski dot org)
*/
-public interface AuditWorkUnit extends KeepCheckVisitor, KeepCheckDispatcher {
+public interface AuditWorkUnit extends WorkUnitMergeVisitor, WorkUnitMergeDispatcher {
Object getEntityId();
String getEntityName();
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/CollectionChangeWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/CollectionChangeWorkUnit.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/CollectionChangeWorkUnit.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -62,23 +62,23 @@
setPerformed(data);
}
- public KeepCheckResult check(AddWorkUnit second) {
- return KeepCheckResult.SECOND;
+ public AuditWorkUnit merge(AddWorkUnit second) {
+ return second;
}
- public KeepCheckResult check(ModWorkUnit second) {
- return KeepCheckResult.SECOND;
+ public AuditWorkUnit merge(ModWorkUnit second) {
+ return second;
}
- public KeepCheckResult check(DelWorkUnit second) {
- return KeepCheckResult.SECOND;
+ public AuditWorkUnit merge(DelWorkUnit second) {
+ return second;
}
- public KeepCheckResult check(CollectionChangeWorkUnit second) {
- return KeepCheckResult.FIRST;
+ public AuditWorkUnit merge(CollectionChangeWorkUnit second) {
+ return this;
}
- public KeepCheckResult dispatch(KeepCheckVisitor first) {
- return first.check(this);
+ public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
+ return first.merge(this);
}
}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/DelWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/DelWorkUnit.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/DelWorkUnit.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -67,23 +67,23 @@
setPerformed(data);
}
- public KeepCheckResult check(AddWorkUnit second) {
- return KeepCheckResult.NONE;
+ public AuditWorkUnit merge(AddWorkUnit second) {
+ return null;
}
- public KeepCheckResult check(ModWorkUnit second) {
- return KeepCheckResult.NONE;
+ public AuditWorkUnit merge(ModWorkUnit second) {
+ return null;
}
- public KeepCheckResult check(DelWorkUnit second) {
- return KeepCheckResult.FIRST;
+ public AuditWorkUnit merge(DelWorkUnit second) {
+ return this;
}
- public KeepCheckResult check(CollectionChangeWorkUnit second) {
- return KeepCheckResult.FIRST;
+ public AuditWorkUnit merge(CollectionChangeWorkUnit second) {
+ return this;
}
- public KeepCheckResult dispatch(KeepCheckVisitor first) {
- return first.check(this);
+ public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
+ return first.merge(this);
}
}
\ No newline at end of file
Deleted: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckDispatcher.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckDispatcher.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckDispatcher.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -1,37 +0,0 @@
-/*
- * Hibernate, Relational Persistence for Idiomatic Java
- *
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Middleware LLC.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA 02110-1301 USA
- */
-package org.hibernate.envers.synchronization.work;
-
-/**
- * Visitor patter dispatcher.
- * @author Adam Warski (adam at warski dot org)
- */
-public interface KeepCheckDispatcher {
- /**
- * Shuold be invoked on the second work unit.
- * @param first First work unit (that is, the one added earlier).
- * @return Which work unit should be kept.
- */
- KeepCheckResult dispatch(KeepCheckVisitor first);
-}
Deleted: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckResult.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckResult.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckResult.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -1,35 +0,0 @@
-/*
- * Hibernate, Relational Persistence for Idiomatic Java
- *
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Middleware LLC.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA 02110-1301 USA
- */
-package org.hibernate.envers.synchronization.work;
-
-/**
- * Possible outcomes of selecting which work unit to keep, in case there are two work units for the same entity
- * with the same id.
- * @author Adam Warski (adam at warski dot org)
- */
-public enum KeepCheckResult {
- FIRST,
- SECOND,
- NONE
-}
Deleted: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckVisitor.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckVisitor.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckVisitor.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -1,35 +0,0 @@
-/*
- * Hibernate, Relational Persistence for Idiomatic Java
- *
- * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
- * indicated by the @author tags or express copyright attribution
- * statements applied by the authors. All third-party contributions are
- * distributed under license by Red Hat Middleware LLC.
- *
- * This copyrighted material is made available to anyone wishing to use, modify,
- * copy, or redistribute it subject to the terms and conditions of the GNU
- * Lesser General Public License, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
- * for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this distribution; if not, write to:
- * Free Software Foundation, Inc.
- * 51 Franklin Street, Fifth Floor
- * Boston, MA 02110-1301 USA
- */
-package org.hibernate.envers.synchronization.work;
-
-/**
- * Visitor pattern visitor. All methods should be invoked on the first work unit.
- * @author Adam Warski (adam at warski dot org)
- */
-public interface KeepCheckVisitor {
- KeepCheckResult check(AddWorkUnit second);
- KeepCheckResult check(ModWorkUnit second);
- KeepCheckResult check(DelWorkUnit second);
- KeepCheckResult check(CollectionChangeWorkUnit second);
-}
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/ModWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/ModWorkUnit.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/ModWorkUnit.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -62,23 +62,27 @@
setPerformed(data);
}
- public KeepCheckResult check(AddWorkUnit second) {
- return KeepCheckResult.FIRST;
+ public Map<String, Object> getData() {
+ return data;
}
- public KeepCheckResult check(ModWorkUnit second) {
- return KeepCheckResult.SECOND;
+ public AuditWorkUnit merge(AddWorkUnit second) {
+ return this;
}
- public KeepCheckResult check(DelWorkUnit second) {
- return KeepCheckResult.SECOND;
+ public AuditWorkUnit merge(ModWorkUnit second) {
+ return second;
}
- public KeepCheckResult check(CollectionChangeWorkUnit second) {
- return KeepCheckResult.FIRST;
+ public AuditWorkUnit merge(DelWorkUnit second) {
+ return second;
}
- public KeepCheckResult dispatch(KeepCheckVisitor first) {
- return first.check(this);
+ public AuditWorkUnit merge(CollectionChangeWorkUnit second) {
+ return this;
}
+
+ public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
+ return first.merge(this);
+ }
}
\ No newline at end of file
Modified: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/PersistentCollectionChangeWorkUnit.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -58,6 +58,16 @@
.mapCollectionChanges(referencingPropertyName, collection, snapshot, id);
}
+ public PersistentCollectionChangeWorkUnit(SessionImplementor sessionImplementor, String entityName,
+ AuditConfiguration verCfg, Serializable id,
+ List<PersistentCollectionChangeData> collectionChanges,
+ String referencingPropertyName) {
+ super(sessionImplementor, entityName, verCfg, id);
+
+ this.collectionChanges = collectionChanges;
+ this.referencingPropertyName = referencingPropertyName;
+ }
+
public boolean containsWork() {
return collectionChanges != null && collectionChanges.size() != 0;
}
@@ -83,23 +93,23 @@
return collectionChanges;
}
- public KeepCheckResult check(AddWorkUnit second) {
+ public AuditWorkUnit merge(AddWorkUnit second) {
return null;
}
- public KeepCheckResult check(ModWorkUnit second) {
+ public AuditWorkUnit merge(ModWorkUnit second) {
return null;
}
- public KeepCheckResult check(DelWorkUnit second) {
+ public AuditWorkUnit merge(DelWorkUnit second) {
return null;
}
- public KeepCheckResult check(CollectionChangeWorkUnit second) {
+ public AuditWorkUnit merge(CollectionChangeWorkUnit second) {
return null;
}
- public KeepCheckResult dispatch(KeepCheckVisitor first) {
+ public AuditWorkUnit dispatch(WorkUnitMergeVisitor first) {
if (first instanceof PersistentCollectionChangeWorkUnit) {
PersistentCollectionChangeWorkUnit original = (PersistentCollectionChangeWorkUnit) first;
@@ -115,26 +125,25 @@
persistentCollectionChangeData);
}
- // Storing the current changes
- List<PersistentCollectionChangeData> newChanges = new ArrayList<PersistentCollectionChangeData>();
- newChanges.addAll(collectionChanges);
+ // This will be the list with the resulting (merged) changes.
+ List<PersistentCollectionChangeData> mergedChanges = new ArrayList<PersistentCollectionChangeData>();
- // And building the change list again
- collectionChanges.clear();
+ // Including only those original changes, which are not overshadowed by new ones.
for (PersistentCollectionChangeData originalCollectionChangeData : original.getCollectionChanges()) {
if (!newChangesIdMap.containsKey(getOriginalId(originalCollectionChangeData))) {
- collectionChanges.add(originalCollectionChangeData);
+ mergedChanges.add(originalCollectionChangeData);
}
}
// Finally adding all of the new changes to the end of the list
- collectionChanges.addAll(newChanges);
+ mergedChanges.addAll(getCollectionChanges());
+
+ return new PersistentCollectionChangeWorkUnit(sessionImplementor, entityName, verCfg, id, mergedChanges,
+ referencingPropertyName);
} else {
throw new RuntimeException("Trying to merge a " + first + " with a PersitentCollectionChangeWorkUnit. " +
"This is not really possible.");
}
-
- return KeepCheckResult.SECOND;
}
private Object getOriginalId(PersistentCollectionChangeData persistentCollectionChangeData) {
Copied: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeDispatcher.java (from rev 18113, core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckDispatcher.java)
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeDispatcher.java (rev 0)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeDispatcher.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -0,0 +1,37 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.envers.synchronization.work;
+
+/**
+ * Visitor patter dispatcher.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface WorkUnitMergeDispatcher {
+ /**
+ * Shuold be invoked on the second work unit.
+ * @param first First work unit (that is, the one added earlier).
+ * @return The work unit that is the result of the merge.
+ */
+ AuditWorkUnit dispatch(WorkUnitMergeVisitor first);
+}
Copied: core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeVisitor.java (from rev 18113, core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/KeepCheckVisitor.java)
===================================================================
--- core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeVisitor.java (rev 0)
+++ core/trunk/envers/src/main/java/org/hibernate/envers/synchronization/work/WorkUnitMergeVisitor.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -0,0 +1,35 @@
+/*
+ * Hibernate, Relational Persistence for Idiomatic Java
+ *
+ * Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
+ * indicated by the @author tags or express copyright attribution
+ * statements applied by the authors. All third-party contributions are
+ * distributed under license by Red Hat Middleware LLC.
+ *
+ * This copyrighted material is made available to anyone wishing to use, modify,
+ * copy, or redistribute it subject to the terms and conditions of the GNU
+ * Lesser General Public License, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this distribution; if not, write to:
+ * Free Software Foundation, Inc.
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301 USA
+ */
+package org.hibernate.envers.synchronization.work;
+
+/**
+ * Visitor pattern visitor. All methods should be invoked on the first work unit.
+ * @author Adam Warski (adam at warski dot org)
+ */
+public interface WorkUnitMergeVisitor {
+ AuditWorkUnit merge(AddWorkUnit second);
+ AuditWorkUnit merge(ModWorkUnit second);
+ AuditWorkUnit merge(DelWorkUnit second);
+ AuditWorkUnit merge(CollectionChangeWorkUnit second);
+}
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushAddMod.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushAddMod.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushAddMod.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -24,11 +24,15 @@
package org.hibernate.envers.test.integration.flush;
import java.util.Arrays;
+import java.util.List;
import javax.persistence.EntityManager;
import org.hibernate.envers.test.entities.StrTestEntity;
+import org.hibernate.envers.query.AuditEntity;
+import org.hibernate.envers.RevisionType;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import static org.testng.Assert.*;
import org.hibernate.FlushMode;
@@ -87,4 +91,16 @@
assert getAuditReader().find(StrTestEntity.class, id, 1).equals(ver1);
assert getAuditReader().find(StrTestEntity.class, id, 2).equals(ver2);
}
+
+ @Test
+ public void testRevisionTypes() {
+ @SuppressWarnings({"unchecked"}) List<Object[]> results =
+ getAuditReader().createQuery()
+ .forRevisionsOfEntity(StrTestEntity.class, false, true)
+ .add(AuditEntity.id().eq(id))
+ .getResultList();
+
+ assertEquals(results.get(0)[2], RevisionType.ADD);
+ assertEquals(results.get(1)[2], RevisionType.MOD);
+ }
}
\ No newline at end of file
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushModDel.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushModDel.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushModDel.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -24,11 +24,15 @@
package org.hibernate.envers.test.integration.flush;
import java.util.Arrays;
+import java.util.List;
import javax.persistence.EntityManager;
import org.hibernate.envers.test.entities.StrTestEntity;
+import org.hibernate.envers.query.AuditEntity;
+import org.hibernate.envers.RevisionType;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
import org.hibernate.FlushMode;
@@ -83,4 +87,16 @@
assert getAuditReader().find(StrTestEntity.class, id, 1).equals(ver1);
assert getAuditReader().find(StrTestEntity.class, id, 2) == null;
}
+
+ @Test
+ public void testRevisionTypes() {
+ @SuppressWarnings({"unchecked"}) List<Object[]> results =
+ getAuditReader().createQuery()
+ .forRevisionsOfEntity(StrTestEntity.class, false, true)
+ .add(AuditEntity.id().eq(id))
+ .getResultList();
+
+ assertEquals(results.get(0)[2], RevisionType.ADD);
+ assertEquals(results.get(1)[2], RevisionType.DEL);
+ }
}
\ No newline at end of file
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushModMod.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushModMod.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/DoubleFlushModMod.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -24,11 +24,15 @@
package org.hibernate.envers.test.integration.flush;
import java.util.Arrays;
+import java.util.List;
import javax.persistence.EntityManager;
import org.hibernate.envers.test.entities.StrTestEntity;
+import org.hibernate.envers.query.AuditEntity;
+import org.hibernate.envers.RevisionType;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
import org.hibernate.FlushMode;
@@ -84,4 +88,16 @@
assert getAuditReader().find(StrTestEntity.class, id, 1).equals(ver1);
assert getAuditReader().find(StrTestEntity.class, id, 2).equals(ver2);
}
+
+ @Test
+ public void testRevisionTypes() {
+ @SuppressWarnings({"unchecked"}) List<Object[]> results =
+ getAuditReader().createQuery()
+ .forRevisionsOfEntity(StrTestEntity.class, false, true)
+ .add(AuditEntity.id().eq(id))
+ .getResultList();
+
+ assertEquals(results.get(0)[2], RevisionType.ADD);
+ assertEquals(results.get(1)[2], RevisionType.MOD);
+ }
}
\ No newline at end of file
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/ManualFlush.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/ManualFlush.java 2009-12-11 10:37:54 UTC (rev 18207)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/flush/ManualFlush.java 2009-12-11 12:40:05 UTC (rev 18208)
@@ -24,11 +24,15 @@
package org.hibernate.envers.test.integration.flush;
import java.util.Arrays;
+import java.util.List;
import javax.persistence.EntityManager;
import org.hibernate.envers.test.entities.StrTestEntity;
+import org.hibernate.envers.query.AuditEntity;
+import org.hibernate.envers.RevisionType;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import static org.testng.Assert.assertEquals;
import org.hibernate.FlushMode;
@@ -96,4 +100,16 @@
public void testCurrent() {
assert getEntityManager().find(StrTestEntity.class, id).equals(new StrTestEntity("z", id));
}
+
+ @Test
+ public void testRevisionTypes() {
+ @SuppressWarnings({"unchecked"}) List<Object[]> results =
+ getAuditReader().createQuery()
+ .forRevisionsOfEntity(StrTestEntity.class, false, true)
+ .add(AuditEntity.id().eq(id))
+ .getResultList();
+
+ assertEquals(results.get(0)[2], RevisionType.ADD);
+ assertEquals(results.get(1)[2], RevisionType.MOD);
+ }
}
15 years
Hibernate SVN: r18207 - core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional.
by hibernate-commits@lists.jboss.org
Author: adamw
Date: 2009-12-11 05:37:54 -0500 (Fri, 11 Dec 2009)
New Revision: 18207
Modified:
core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MRelationNotAuditedTarget.java
Log:
- making the test actually assert that the results are correct
Modified: core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MRelationNotAuditedTarget.java
===================================================================
--- core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MRelationNotAuditedTarget.java 2009-12-11 09:12:10 UTC (rev 18206)
+++ core/trunk/envers/src/test/java/org/hibernate/envers/test/integration/manytomany/unidirectional/M2MRelationNotAuditedTarget.java 2009-12-11 10:37:54 UTC (rev 18207)
@@ -37,6 +37,7 @@
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
+import static org.testng.Assert.*;
/**
* A test for auditing a many-to-many relation where the target entity is not audited.
@@ -143,10 +144,10 @@
M2MTargetNotAuditedEntity rev3 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae1_id, 3);
M2MTargetNotAuditedEntity rev4 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae1_id, 4);
- checkList(rev1.getReferences());
- checkList(rev2.getReferences(), uste1);
- checkList(rev3.getReferences(), uste1);
- checkList(rev4.getReferences(), uste1, uste2);
+ assertTrue(checkList(rev1.getReferences()));
+ assertTrue(checkList(rev2.getReferences(), uste1));
+ assertTrue(checkList(rev3.getReferences(), uste1));
+ assertTrue(checkList(rev4.getReferences(), uste1, uste2));
}
@Test
@@ -159,9 +160,9 @@
M2MTargetNotAuditedEntity rev3 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae2_id, 3);
M2MTargetNotAuditedEntity rev4 = getAuditReader().find(M2MTargetNotAuditedEntity.class, tnae2_id, 4);
- checkList(rev1.getReferences(), uste1, uste2);
- checkList(rev2.getReferences(), uste2);
- checkList(rev3.getReferences());
- checkList(rev4.getReferences(), uste1);
+ assertTrue(checkList(rev1.getReferences(), uste1, uste2));
+ assertTrue(checkList(rev2.getReferences(), uste2));
+ assertTrue(checkList(rev3.getReferences()));
+ assertTrue(checkList(rev4.getReferences(), uste1));
}
}
15 years
Hibernate SVN: r18206 - in core/trunk: annotations/src/main/java/org/hibernate/cfg/beanvalidation and 4 other directories.
by hibernate-commits@lists.jboss.org
Author: epbernard
Date: 2009-12-11 04:12:10 -0500 (Fri, 11 Dec 2009)
New Revision: 18206
Added:
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/beanvalidation/
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/beanvalidation/BeanValidationTest.java
core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/beanvalidation/CupHolder.java
Modified:
core/trunk/annotations/pom.xml
core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java
core/trunk/entitymanager/pom.xml
core/trunk/parent/pom.xml
Log:
add test on Bean Validation in HEM
Modified: core/trunk/annotations/pom.xml
===================================================================
--- core/trunk/annotations/pom.xml 2009-12-11 07:47:28 UTC (rev 18205)
+++ core/trunk/annotations/pom.xml 2009-12-11 09:12:10 UTC (rev 18206)
@@ -76,21 +76,6 @@
</dependency>
</dependencies>
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>javax.validation</groupId>
- <artifactId>validation-api</artifactId>
- <version>1.0.0.GA</version>
- </dependency>
- <dependency>
- <groupId>org.hibernate</groupId>
- <artifactId>hibernate-validator</artifactId>
- <version>4.0.2.GA</version>
- </dependency>
- </dependencies>
- </dependencyManagement>
-
<build>
<testResources>
<testResource>
Modified: core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java
===================================================================
--- core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java 2009-12-11 07:47:28 UTC (rev 18205)
+++ core/trunk/annotations/src/main/java/org/hibernate/cfg/beanvalidation/BeanValidationEventListener.java 2009-12-11 09:12:10 UTC (rev 18206)
@@ -125,7 +125,7 @@
StringBuilder builder = new StringBuilder();
builder.append( "validation failed for classes " );
builder.append( classNames );
- builder.append( " during" );
+ builder.append( " during " );
builder.append( operation.getName() );
builder.append( " time for groups " );
builder.append( toString( groups ) );
Modified: core/trunk/entitymanager/pom.xml
===================================================================
--- core/trunk/entitymanager/pom.xml 2009-12-11 07:47:28 UTC (rev 18205)
+++ core/trunk/entitymanager/pom.xml 2009-12-11 09:12:10 UTC (rev 18206)
@@ -40,6 +40,16 @@
<artifactId>jpa-api</artifactId>
</dependency>
<dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
<!--
Only really needed for the antrun plugin defined below (which in turn is only really needed
to work around a missing feature on the compile plugin; lovely no?). However I have not
Added: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/beanvalidation/BeanValidationTest.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/beanvalidation/BeanValidationTest.java (rev 0)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/beanvalidation/BeanValidationTest.java 2009-12-11 09:12:10 UTC (rev 18206)
@@ -0,0 +1,38 @@
+package org.hibernate.ejb.test.beanvalidation;
+
+import java.math.BigDecimal;
+
+import javax.persistence.EntityManager;
+import javax.validation.ConstraintViolationException;
+
+import org.hibernate.ejb.test.TestCase;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class BeanValidationTest extends TestCase {
+
+ public void testBeanValidationIntegration() {
+ CupHolder ch = new CupHolder();
+ ch.setRadius( new BigDecimal( "12" ) );
+ EntityManager em = getOrCreateEntityManager();
+ em.getTransaction().begin();
+ try {
+ em.persist( ch );
+ em.flush();
+ fail("invalid object should not be persisted");
+ }
+ catch ( ConstraintViolationException e ) {
+ assertEquals( 1, e.getConstraintViolations().size() );
+ }
+ em.getTransaction().rollback();
+ em.close();
+ }
+
+ @Override
+ public Class[] getAnnotatedClasses() {
+ return new Class[] {
+ CupHolder.class
+ };
+ }
+}
Added: core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/beanvalidation/CupHolder.java
===================================================================
--- core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/beanvalidation/CupHolder.java (rev 0)
+++ core/trunk/entitymanager/src/test/java/org/hibernate/ejb/test/beanvalidation/CupHolder.java 2009-12-11 09:12:10 UTC (rev 18206)
@@ -0,0 +1,39 @@
+package org.hibernate.ejb.test.beanvalidation;
+
+import java.math.BigDecimal;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.validation.constraints.Max;
+import javax.validation.constraints.NotNull;
+
+/**
+ * @author Emmanuel Bernard
+ */
+@Entity
+public class CupHolder {
+ @Id
+ @GeneratedValue
+ //@GeneratedValue(strategy = GenerationType.SEQUENCE)
+ private Integer id;
+ private BigDecimal radius;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ @Max( value = 10, message = "Radius way out")
+ @NotNull
+ public BigDecimal getRadius() {
+ return radius;
+ }
+
+ public void setRadius(BigDecimal radius) {
+ this.radius = radius;
+ }
+}
Modified: core/trunk/parent/pom.xml
===================================================================
--- core/trunk/parent/pom.xml 2009-12-11 07:47:28 UTC (rev 18205)
+++ core/trunk/parent/pom.xml 2009-12-11 09:12:10 UTC (rev 18206)
@@ -472,6 +472,17 @@
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.2.0.Beta1</version>
</dependency>
+ <!-- set the optional Bean Validation and Hibernate Validator to be used throughout the project -->
+ <dependency>
+ <groupId>javax.validation</groupId>
+ <artifactId>validation-api</artifactId>
+ <version>1.0.0.GA</version>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-validator</artifactId>
+ <version>4.0.2.GA</version>
+ </dependency>
</dependencies>
</dependencyManagement>
15 years