HHH-10398 Allow MOD column naming to be driven by a strategy

In the past the MOD columns were constructed based on the property name,
therefore if users specified a @Column/@JoinColumn like annotation and
changed the underlying schema column, the MOD column would continue to
be derived based on the property name.

This enhancement introduces a new ModifiedColumnNamingStrategy SPI that
comes with two implementations, a default/legacy mode that maintains
the prior naming model and an improved mode that will derive the MOD
name based on the naming strategy ORM used to derive the column name.
This commit is contained in:
Chris Cranford 2019-09-17 23:31:10 -04:00 committed by Chris Cranford
parent 0084f82b1a
commit 3ecdd860a3
20 changed files with 739 additions and 18 deletions

View File

@ -286,6 +286,9 @@ The suffix for columns storing "Modified Flags".
+
For example, a property called "age", will by default get modified flag with column name "age_MOD".
`*org.hibernate.envers.modified_column_naming_strategy*` (default: `org.hibernate.envers.boot.internal.LegacyModifiedColumnNamingStrategy` )::
The naming strategy to be used for modified flag columns in the audit metadata.
`*org.hibernate.envers.embeddable_set_ordinal_field_name*` (default: `SETORDINAL` )::
Name of column used for storing ordinal of the change in sets of embeddable elements.
@ -314,6 +317,7 @@ The following configuration options have been added recently and should be regar
. `org.hibernate.envers.track_entities_changed_in_revision`
. `org.hibernate.envers.using_modified_flag`
. `org.hibernate.envers.modified_flag_suffix`
. `org.hibernate.envers.modified_column_naming_strategy`
. `org.hibernate.envers.original_id_prop_name`
. `org.hibernate.envers.find_by_revision_exact_match`
====
@ -721,6 +725,77 @@ include::{extrasdir}/envers-tracking-properties-changes-example.sql[]
To see how "Modified Flags" can be utilized, check out the very simple query API that uses them: <<envers-tracking-properties-changes-queries>>.
[[envers-tracking-properties-changes-strategy]]
=== Selecting strategy for tracking property level changes
By default, Envers uses the `legacy` modified column naming strategy.
This strategy is designed to add columns based the following rule-set:
. If property is annotated with `@Audited` and the _modifiedColumnName_ attribute is specified, the column will directly be based on the supplied name.
. If property is not annotated with `@Audited` or if no _modifiedColumnName_ attribute is given, the column will be named after the java class property, appended with the configured suffix, the default being `_MOD`.
While this strategy has no performance drawbacks, it does present concerns for users who prefer consistency without verbosity.
Lets take the following entity mapping as an example.
```
@Audited(withModifiedFlags = true)
@Entity
public class Customer {
@Id
private Integer id;
@Column(name = "customer_name")
private String name;
}
```
This mapping will actually lead to some inconsistent naming between columns, see below with how the model's name will be stored in `customer_name` but the modified column that tracks whether this column changes between revisions is named `name_MOD`.
```
CREATE TABLE Customer_AUD (
id bigint not null,
REV integer not null,
REVTYPE tinyint not null,
customer_name varchar(255),
name_MOD boolean,
primary key(id, REV)
)
```
An additional strategy called `improved`, aims to address these consistent column naming concerns.
This strategy uses the following rule-set:
. Property is a Basic type (Single Column valued property)
.. Use the _modifiedColumnName_ directly if one is supplied on the property mapping
.. Otherwise use the resolved ORM column name appended with the modified flag suffix configured value.
. Property is an Association (to-one mapping) with a Foreign Key using a single column
.. Use the _modifiedColumnName_ directly if one is supplied on the property mapping
.. Otherwise use the resolved ORM column name appended with the modified flag suffix configured value.
. Property is an Association (to-one mapping) with a Foreign Key using multiple columns
.. Use the _modifiedColumnName_ directly if one is supplied on the property mapping
.. Otherwise use the property name appended with the modified flag suffix configured value.
. Property is an Embeddable
.. Use the _modifiedColumnName_ directly if one is supplied on the property mapping
.. Otherwise use the property name appended with the modified flag suffix configured value.
While using this strategy, the same `Customer` mapping will generate the following table schema:
```
CREATE TABLE Customer_AUD (
id bigint not null,
REV integer not null,
REVTYPE tinyint not null,
customer_name varchar(255),
customer_name_MOD boolean,
primary key(id, REV)
)
```
When already using Envers in conjunction with the modified columns flag feature, its advised not to enable the new strategy immediately as schema changes would be required.
You will need to either migrate your existing schema manually to adhere to the rules above or use the explicit _modifiedColumnName_ attribute on the `@Audited` annotation for existing columns that use the feature.
To configure a custom strategy implementation or use the improved strategy, the configuration option `org.hibernate.envers.modified_column_naming_strategy` will need to be set.
This option can be the fully qualified class name of a `ModifiedColumnNameStrategy` implementation or `legacy` or `improved` for either of the two provided implementations.
[[envers-queries]]
=== Queries

View File

@ -0,0 +1,80 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.boot.internal;
import org.hibernate.envers.boot.spi.ModifiedColumnNamingStrategy;
import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.configuration.internal.metadata.MetadataTools;
import org.hibernate.envers.configuration.internal.metadata.reader.PropertyAuditingData;
import org.hibernate.envers.internal.tools.StringTools;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Value;
import org.hibernate.type.BasicType;
import org.hibernate.type.ManyToOneType;
import org.hibernate.type.OneToOneType;
import org.dom4j.Element;
/**
* A {@link ModifiedColumnNamingStrategy} that adds modified columns with the following rules:
* <ul>
* <li>For basic types, prioritizes audit annotation naming followed by physical column name appended with suffix.</li>
* <li>For associations with single column foreign keys, behaves like basic types.</li>
* <li>For associations with multiple column foreign keys, prioritizes audit annotation naming followed by using property name.</li>
* <li>For embeddables, behaves like associations with multiple column foreign keys</li>
* </ul>
*
* @author Chris Cranford
* @since 5.4.6
*/
public class ImprovedModifiedColumnNamingStrategy implements ModifiedColumnNamingStrategy {
@Override
public void addModifiedColumns(
GlobalConfiguration globalCfg,
Value value,
Element parent,
PropertyAuditingData propertyAuditingData) {
boolean basicType = value.getType() instanceof BasicType;
boolean toOneType = value.getType() instanceof ManyToOneType || value.getType() instanceof OneToOneType;
if ( basicType || toOneType ) {
if ( value.getColumnSpan() == 1 ) {
Selectable selectable = value.getColumnIterator().next();
if ( selectable instanceof Column ) {
// This should not be applied for formulas
String columnName = propertyAuditingData.getModifiedFlagName();
if ( !propertyAuditingData.isModifiedFlagNameExplicitlySpecified() ) {
columnName = ( (Column) selectable ).getName() + globalCfg.getModifiedFlagSuffix();
}
else {
columnName = propertyAuditingData.getExplicitModifiedFlagName();
}
MetadataTools.addModifiedFlagPropertyWithColumn(
parent,
propertyAuditingData.getName(),
globalCfg.getModifiedFlagSuffix(),
propertyAuditingData.getModifiedFlagName(),
columnName
);
return;
}
}
}
// Default legacy behavior
MetadataTools.addModifiedFlagProperty(
parent,
propertyAuditingData.getName(),
globalCfg.getModifiedFlagSuffix(),
propertyAuditingData.getModifiedFlagName()
);
}
}

View File

@ -0,0 +1,47 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.boot.internal;
import org.hibernate.envers.boot.spi.ModifiedColumnNamingStrategy;
import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.configuration.internal.metadata.MetadataTools;
import org.hibernate.envers.configuration.internal.metadata.reader.PropertyAuditingData;
import org.hibernate.mapping.Value;
import org.dom4j.Element;
/**
* A {@link ModifiedColumnNamingStrategy} that adds modified columns with the following rules:
* <ul>
* <li>If an audit annotation modified column name is supplied, use it directly with no suffix.</li>
* <li>If no audit annotation modified column name is present, use the property name appended with suffix.</li>
* </ul>
*
* This is the default Envers modified column naming behavior.
*
* @author Chris Cranford
* @since 5.4.6
*/
public class LegacyModifiedColumnNamingStrategy implements ModifiedColumnNamingStrategy {
@Override
public void addModifiedColumns(
GlobalConfiguration globalCfg,
Value value,
Element parent,
PropertyAuditingData propertyAuditingData) {
String columnName = propertyAuditingData.getModifiedFlagName();
if ( propertyAuditingData.isModifiedFlagNameExplicitlySpecified() ) {
columnName = propertyAuditingData.getExplicitModifiedFlagName();
}
MetadataTools.addModifiedFlagProperty(
parent,
propertyAuditingData.getName(),
globalCfg.getModifiedFlagSuffix(),
columnName
);
}
}

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.boot.internal;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.boot.registry.selector.SimpleStrategyRegistrationImpl;
import org.hibernate.boot.registry.selector.StrategyRegistration;
import org.hibernate.boot.registry.selector.StrategyRegistrationProvider;
import org.hibernate.envers.boot.spi.ModifiedColumnNamingStrategy;
/**
* A {@link StrategyRegistrationProvider} for {@link ModifiedColumnNamingStrategy}s.
*
* @author Chris Cranford
*/
public class ModifiedColumnNamingStrategyRegistrationProvider implements StrategyRegistrationProvider {
@Override
public Iterable<StrategyRegistration> getStrategyRegistrations() {
final List<StrategyRegistration> registrations = new ArrayList<>();
registrations.add(
new SimpleStrategyRegistrationImpl(
ModifiedColumnNamingStrategy.class,
LegacyModifiedColumnNamingStrategy.class,
"default", "legacy"
)
);
registrations.add(
new SimpleStrategyRegistrationImpl(
ModifiedColumnNamingStrategy.class,
ImprovedModifiedColumnNamingStrategy.class,
"improved"
)
);
return registrations;
}
}

View File

@ -0,0 +1,37 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.boot.spi;
import org.hibernate.Incubating;
import org.hibernate.envers.configuration.internal.GlobalConfiguration;
import org.hibernate.envers.configuration.internal.metadata.reader.PropertyAuditingData;
import org.hibernate.mapping.Value;
import org.dom4j.Element;
/**
* Defines a naming strategy for applying modified columns to the audited entity metamodel.
*
* @author Chris Cranford
* @since 5.4.6
*/
@Incubating
public interface ModifiedColumnNamingStrategy {
/**
* Adds modified columns to the audited entity metamodel.
*
* @param globalCfg the envers global configuration
* @param value the property value
* @param parent the parent audited entity metamodel
* @param propertyAuditingData the property auditing data
*/
void addModifiedColumns(
GlobalConfiguration globalCfg,
Value value,
Element parent,
PropertyAuditingData propertyAuditingData);
}

View File

@ -133,4 +133,13 @@ public interface EnversSettings {
* @since 5.4.4
*/
String FIND_BY_REVISION_EXACT_MATCH = "org.hibernate.envers.find_by_revision_exact_match";
/**
* Specifies the {@link org.hibernate.envers.boot.spi.ModifiedColumnNamingStrategy} to use
*
* Defaults to {@link org.hibernate.envers.boot.internal.LegacyModifiedColumnNamingStrategy}.
*
* @since 5.4.6
*/
String MODIFIED_COLUMN_NAMING_STRATEGY = "org.hibernate.envers.modified_column_naming_strategy";
}

View File

@ -7,13 +7,17 @@
package org.hibernate.envers.configuration.internal;
import java.util.Map;
import java.util.concurrent.Callable;
import org.hibernate.MappingException;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.registry.selector.spi.StrategySelector;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.HSQLDialect;
import org.hibernate.envers.RevisionListener;
import org.hibernate.envers.boot.internal.EnversService;
import org.hibernate.envers.boot.internal.LegacyModifiedColumnNamingStrategy;
import org.hibernate.envers.boot.spi.ModifiedColumnNamingStrategy;
import org.hibernate.envers.configuration.EnversSettings;
import org.hibernate.envers.internal.tools.ReflectionTools;
import org.hibernate.internal.util.config.ConfigurationHelper;
@ -77,11 +81,15 @@ public class GlobalConfiguration {
*/
private final String correlatedSubqueryOperator;
private final ModifiedColumnNamingStrategy modifiedColumnNamingStrategy;
public GlobalConfiguration(
EnversService enversService,
Map properties) {
this.enversService = enversService;
final StrategySelector strategySelector = enversService.getServiceRegistry().getService( StrategySelector.class );
generateRevisionsForCollections = ConfigurationHelper.getBoolean(
EnversSettings.REVISION_ON_COLLECTION_CHANGE,
properties,
@ -156,6 +164,21 @@ public class GlobalConfiguration {
revisionListenerClass = null;
}
modifiedColumnNamingStrategy = strategySelector.resolveDefaultableStrategy(
ModifiedColumnNamingStrategy.class,
properties.get( EnversSettings.MODIFIED_COLUMN_NAMING_STRATEGY ),
new Callable<ModifiedColumnNamingStrategy>() {
@Override
public ModifiedColumnNamingStrategy call() throws Exception {
return strategySelector.resolveDefaultableStrategy(
ModifiedColumnNamingStrategy.class,
"default",
new LegacyModifiedColumnNamingStrategy()
);
}
}
);
allowIdentifierReuse = ConfigurationHelper.getBoolean(
EnversSettings.ALLOW_IDENTIFIER_REUSE, properties, false
);
@ -232,4 +255,8 @@ public class GlobalConfiguration {
public boolean isAuditReaderFindAtRevisionExactMatch() {
return findByRevisionExactMatch;
}
public ModifiedColumnNamingStrategy getModifiedColumnNamingStrategy() {
return modifiedColumnNamingStrategy;
}
}

View File

@ -213,7 +213,7 @@ public final class AuditMetadataGenerator {
}
return;
}
addModifiedFlagIfNeeded( parent, propertyAuditingData, processModifiedFlag );
addModifiedFlagIfNeeded( value, parent, propertyAuditingData, processModifiedFlag );
}
private boolean processedInSecondPass(Type type) {
@ -290,19 +290,20 @@ public final class AuditMetadataGenerator {
else {
return;
}
addModifiedFlagIfNeeded( parent, propertyAuditingData, processModifiedFlag );
addModifiedFlagIfNeeded( value, parent, propertyAuditingData, processModifiedFlag );
}
private void addModifiedFlagIfNeeded(
Value value,
Element parent,
PropertyAuditingData propertyAuditingData,
boolean processModifiedFlag) {
if ( processModifiedFlag && propertyAuditingData.isUsingModifiedFlag() ) {
MetadataTools.addModifiedFlagProperty(
globalCfg.getModifiedColumnNamingStrategy().addModifiedColumns(
globalCfg,
value,
parent,
propertyAuditingData.getName(),
globalCfg.getModifiedFlagSuffix(),
propertyAuditingData.getModifiedFlagName()
propertyAuditingData
);
}
}

View File

@ -95,6 +95,26 @@ public final class MetadataTools {
);
}
public static Element addModifiedFlagPropertyWithColumn(
Element parent,
String propertyName,
String suffix,
String modifiedFlagName,
String columnName) {
final Element property = addProperty(
parent,
(modifiedFlagName != null) ? modifiedFlagName : getModifiedFlagPropertyName( propertyName, suffix ),
"boolean",
true,
false,
false
);
addColumn( property, columnName, null, null, null, null, null, null );
return property;
}
public static String getModifiedFlagPropertyName(String propertyName, String suffix) {
return propertyName + suffix;
}

View File

@ -590,13 +590,9 @@ public class AuditedPropertiesReader {
propertyData.setStore( aud.modStore() );
propertyData.setRelationTargetAuditMode( aud.targetAuditMode() );
propertyData.setUsingModifiedFlag( checkUsingModifiedFlag( aud ) );
propertyData.setModifiedFlagName( MetadataTools.getModifiedFlagPropertyName( propertyName, modifiedFlagSuffix ) );
if( aud.modifiedColumnName() != null && !"".equals( aud.modifiedColumnName() ) ) {
propertyData.setModifiedFlagName( aud.modifiedColumnName() );
}
else {
propertyData.setModifiedFlagName(
MetadataTools.getModifiedFlagPropertyName( propertyName, modifiedFlagSuffix )
);
propertyData.setExplicitModifiedFlagName( aud.modifiedColumnName() );
}
return true;
}

View File

@ -44,13 +44,9 @@ public class ComponentAuditedPropertiesReader extends AuditedPropertiesReader {
propertyData.setStore( aud.modStore() );
propertyData.setRelationTargetAuditMode( aud.targetAuditMode() );
propertyData.setUsingModifiedFlag( checkUsingModifiedFlag( aud ) );
propertyData.setModifiedFlagName( MetadataTools.getModifiedFlagPropertyName( propertyName, modifiedFlagSuffix ) );
if( aud.modifiedColumnName() != null && !"".equals( aud.modifiedColumnName() ) ) {
propertyData.setModifiedFlagName( aud.modifiedColumnName() );
}
else {
propertyData.setModifiedFlagName(
MetadataTools.getModifiedFlagPropertyName( propertyName, modifiedFlagSuffix )
);
propertyData.setExplicitModifiedFlagName( aud.modifiedColumnName() );
}
}
else {

View File

@ -17,6 +17,7 @@ import org.hibernate.envers.AuditOverrides;
import org.hibernate.envers.ModificationStore;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.envers.internal.entities.PropertyData;
import org.hibernate.envers.internal.tools.StringTools;
import org.hibernate.mapping.Value;
import org.hibernate.type.Type;
@ -41,6 +42,7 @@ public class PropertyAuditingData {
private boolean forceInsertable;
private boolean usingModifiedFlag;
private String modifiedFlagName;
private String explicitModifiedFlagName;
private Value value;
// Synthetic properties are ones which are not part of the actual java model.
// They're properties used for bookkeeping by Hibernate
@ -237,6 +239,18 @@ public class PropertyAuditingData {
this.modifiedFlagName = modifiedFlagName;
}
public boolean isModifiedFlagNameExplicitlySpecified() {
return !StringTools.isEmpty( explicitModifiedFlagName );
}
public String getExplicitModifiedFlagName() {
return explicitModifiedFlagName;
}
public void setExplicitModifiedFlagName(String modifiedFlagName) {
this.explicitModifiedFlagName = modifiedFlagName;
}
public void addAuditingOverride(AuditOverride annotation) {
if ( annotation != null ) {
final String overrideName = annotation.name();

View File

@ -0,0 +1 @@
org.hibernate.envers.boot.internal.ModifiedColumnNamingStrategyRegistrationProvider

View File

@ -0,0 +1,52 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.modifiedflags.naming;
import java.util.Map;
import org.hibernate.envers.configuration.EnversSettings;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Table;
import org.junit.Test;
import static org.junit.Assert.assertNotNull;
/**
* @author Chris Cranford
*/
public class ImprovedColumnNamingStrategyTest extends BaseEnversJPAFunctionalTestCase {
@Override
protected void addConfigOptions(Map options) {
super.addConfigOptions( options );
options.put( EnversSettings.MODIFIED_COLUMN_NAMING_STRATEGY, "improved" );
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { TestEntity.class, OtherEntity.class, SingleIdEntity.class };
}
@Test
public void testModifiedColumns() {
final Table table1 = metadata().getEntityBinding( TestEntity.class.getName() + "_AUD" ).getTable();
assertNotNull( table1.getColumn( new Column( "data1_MOD") ) );
assertNotNull( table1.getColumn( new Column( "mydata_MOD" ) ) );
assertNotNull( table1.getColumn( new Column( "data_3" ) ) );
assertNotNull( table1.getColumn( new Column( "the_data_mod" ) ) );
assertNotNull( table1.getColumn( new Column( "embeddable_MOD" ) ) );
assertNotNull( table1.getColumn( new Column( "otherEntity_MOD" ) ) );
assertNotNull( table1.getColumn( new Column( "single_id_MOD" ) ) );
assertNotNull( table1.getColumn( new Column( "singleIdEntity2_id_MOD" ) ) );
final Table table2 = metadata().getEntityBinding( OtherEntity.class.getName() + "_AUD" ).getTable();
assertNotNull( table2.getColumn( new Column( "d_MOD" ) ) );
}
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.modifiedflags.naming;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Table;
import org.junit.Test;
import static org.junit.Assert.assertNotNull;
/**
* @author Chris Cranford
*/
public class LegacyColumnNamingStrategyTest extends BaseEnversJPAFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { TestEntity.class, OtherEntity.class, SingleIdEntity.class };
}
@Test
public void testModifiedColumns() {
final Table table1 = metadata().getEntityBinding( TestEntity.class.getName() + "_AUD" ).getTable();
assertNotNull( table1.getColumn( new Column( "data1_MOD") ) );
assertNotNull( table1.getColumn( new Column( "data2_MOD" ) ) );
assertNotNull( table1.getColumn( new Column( "data_3" ) ) );
assertNotNull( table1.getColumn( new Column( "the_data_mod" ) ) );
assertNotNull( table1.getColumn( new Column( "embeddable_MOD" ) ) );
assertNotNull( table1.getColumn( new Column( "otherEntity_MOD" ) ) );
assertNotNull( table1.getColumn( new Column( "singleIdEntity_MOD" ) ) );
assertNotNull( table1.getColumn( new Column( "singleIdEntity2_MOD" ) ) );
final Table table2 = metadata().getEntityBinding( OtherEntity.class.getName() + "_AUD" ).getTable();
assertNotNull( table2.getColumn( new Column( "data_MOD" ) ) );
}
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.modifiedflags.naming;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import org.hibernate.envers.Audited;
/**
* @author Chris Cranford
*/
@Entity
@Audited(withModifiedFlag = true)
public class OtherEntity {
@EmbeddedId
private OtherEntityId id;
@Column(name = "d")
private String data;
public OtherEntityId getId() {
return id;
}
public void setId(OtherEntityId id) {
this.id = id;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.modifiedflags.naming;
import java.io.Serializable;
import javax.persistence.Embeddable;
/**
* @author Chris Cranford
*/
@Embeddable
public class OtherEntityId implements Serializable {
private Integer id1;
private Integer id2;
public Integer getId1() {
return id1;
}
public void setId1(Integer id1) {
this.id1 = id1;
}
public Integer getId2() {
return id2;
}
public void setId2(Integer id2) {
this.id2 = id2;
}
}

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.modifiedflags.naming;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import org.hibernate.envers.Audited;
/**
* @author Chris Cranford
*/
@Entity
@Audited(withModifiedFlag = true)
public class SingleIdEntity {
@Id
@GeneratedValue
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.modifiedflags.naming;
import javax.persistence.Embeddable;
/**
* @author Chris Cranford
*/
@Embeddable
public class TestEmbeddable {
String value1;
String value2;
public String getValue1() {
return value1;
}
public void setValue1(String value1) {
this.value1 = value1;
}
public String getValue2() {
return value2;
}
public void setValue2(String value2) {
this.value2 = value2;
}
}

View File

@ -0,0 +1,126 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.envers.test.integration.modifiedflags.naming;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import org.hibernate.envers.Audited;
/**
* @author Chris Cranford
*/
@Entity
@Audited(withModifiedFlag = true)
public class TestEntity {
@Id
@GeneratedValue
private Integer id;
private String data1;
@Column(name = "mydata")
private String data2;
@Audited(modifiedColumnName = "data_3", withModifiedFlag = true)
private String data3;
@Column(name = "thedata")
@Audited(modifiedColumnName = "the_data_mod", withModifiedFlag = true)
private String data4;
@Embedded
private TestEmbeddable embeddable;
@ManyToOne
@JoinColumns({
@JoinColumn(name = "other_entity_id1", nullable = false),
@JoinColumn(name = "other_entity_id2", nullable = false)
})
private OtherEntity otherEntity;
@OneToOne
@JoinColumn(name = "single_id")
private SingleIdEntity singleIdEntity;
@OneToOne
private SingleIdEntity singleIdEntity2;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public String getData2() {
return data2;
}
public void setData2(String data2) {
this.data2 = data2;
}
public String getData3() {
return data3;
}
public void setData3(String data3) {
this.data3 = data3;
}
public String getData4() {
return data4;
}
public void setData4(String data4) {
this.data4 = data4;
}
public TestEmbeddable getEmbeddable() {
return embeddable;
}
public void setEmbeddable(TestEmbeddable embeddable) {
this.embeddable = embeddable;
}
public OtherEntity getOtherEntity() {
return otherEntity;
}
public void setOtherEntity(OtherEntity otherEntity) {
this.otherEntity = otherEntity;
}
public SingleIdEntity getSingleIdEntity() {
return singleIdEntity;
}
public void setSingleIdEntity(SingleIdEntity singleIdEntity) {
this.singleIdEntity = singleIdEntity;
}
public SingleIdEntity getSingleIdEntity2() {
return singleIdEntity2;
}
public void setSingleIdEntity2(SingleIdEntity singleIdEntity2) {
this.singleIdEntity2 = singleIdEntity2;
}
}