Remove us of @SelectBeforeUpdate from tests

This commit is contained in:
Andrea Boriero 2024-07-25 15:17:59 +02:00 committed by Steve Ebersole
parent 83a226c503
commit e3344e0068
6 changed files with 0 additions and 1007 deletions

View File

@ -22,7 +22,6 @@ import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking; import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.annotations.ParamDef; import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.SQLRestriction; import org.hibernate.annotations.SQLRestriction;
import org.hibernate.annotations.SelectBeforeUpdate;
import jakarta.persistence.Convert; import jakarta.persistence.Convert;
import jakarta.persistence.ElementCollection; import jakarta.persistence.ElementCollection;
@ -40,7 +39,6 @@ import jakarta.persistence.Table;
*/ */
@Entity @Entity
@BatchSize(size = 5) @BatchSize(size = 5)
@SelectBeforeUpdate
@DynamicInsert @DynamicUpdate @DynamicInsert @DynamicUpdate
@OptimisticLocking(type = OptimisticLockType.ALL) @OptimisticLocking(type = OptimisticLockType.ALL)
@SQLRestriction("1=1") @SQLRestriction("1=1")

View File

@ -1,324 +0,0 @@
/*
* 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.orm.test.annotations.selectbeforeupdate;
import java.util.HashSet;
import java.util.Set;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
/**
* @author Chris Cranford
*/
@DomainModel(
annotatedClasses = {
MergeDetachedTest.Foo.class,
MergeDetachedTest.Bar.class
}
)
@SessionFactory
public class MergeDetachedTest {
@Test
@JiraKey("HHH-5908")
public void testUpdateDetachedUnchanged(SessionFactoryScope scope) {
final Bar bar = new Bar( 1, "Bar" );
final Foo foo = new Foo( 1, "Foo", bar );
// this should generate versions
scope.inTransaction( session -> {
session.persist( bar );
session.persist( foo );
} );
// this shouldn't generate a new version.
Foo merged = scope.fromTransaction( session ->
session.merge( foo )
);
assertThat( bar.getVersion() ).isEqualTo( 0 );
assertThat( merged.getVersion() ).isEqualTo( 0 );
// this should generate a new version
Foo merged2 = scope.fromTransaction( session -> {
merged.setName( "FooChanged" );
return session.merge( merged );
} );
assertThat( bar.getVersion() ).isEqualTo( 0 );
assertThat( merged2.getVersion() ).isEqualTo( 1 );
}
@Test
@JiraKey("HHH-5908")
public void testUpdateDetachedChanged(SessionFactoryScope scope) {
final Bar bar = new Bar( 2, "Bar" );
final Foo foo = new Foo( 2, "Foo", bar );
// this should generate versions
scope.inTransaction( session -> {
session.persist( bar );
session.persist( foo );
} );
// this should generate a new version
Foo merged = scope.fromTransaction( session -> {
foo.setName( "FooChanged" );
return session.merge( foo );
} );
assertThat( bar.getVersion() ).isEqualTo( 0 );
assertThat( merged.getVersion() ).isEqualTo( 1 );
}
@Test
@JiraKey("HHH-5908")
public void testUpdateDetachedUnchangedAndChanged(SessionFactoryScope scope) {
final Bar bar = new Bar( 3, "Bar" );
final Foo foo = new Foo( 3, "Foo", bar );
// this should generate versions
scope.inTransaction( session -> {
session.persist( bar );
session.persist( foo );
} );
// this shouldn't generate a new version.
Foo merged = scope.fromTransaction(
session ->
session.merge( foo )
);
// this should generate a new version
Foo merged2 = scope.fromTransaction( session -> {
merged.setName( "FooChanged" );
return session.merge( merged );
} );
assertThat( bar.getVersion() ).isEqualTo( 0 );
assertThat( merged2.getVersion() ).isEqualTo( 1 );
}
@Test
@JiraKey("HHH-5908")
public void testUpdateDetachedChangedAndUnchanged(SessionFactoryScope scope) {
final Bar bar = new Bar( 4, "Bar" );
final Foo foo = new Foo( 4, "Foo", bar );
// this should generate versions
scope.inTransaction( session -> {
session.persist( bar );
session.persist( foo );
} );
// this should generate a new version
Foo merged = scope.fromTransaction( session -> {
foo.setName( "FooChanged" );
return session.merge( foo );
} );
// this shouldn't generate a new version.
Foo merged2 = scope.fromTransaction(
session ->
session.merge( merged )
);
assertThat( bar.getVersion() ).isEqualTo( 0 );
assertThat( merged2.getVersion() ).isEqualTo( 1 );
}
@Test
@JiraKey("HHH-14319")
public void testUpdateDetachedWithAttachedPersistentSet(SessionFactoryScope scope) {
final Bar bar = new Bar( 5, "Bar" );
final Set<Comment> comments = new HashSet<>();
comments.add( new Comment( "abc", "me" ) );
bar.comments = comments;
// this should generate versions
scope.inTransaction( session -> {
session.persist( bar );
} );
final Bar loadedBar = scope.fromTransaction( session -> {
// We set the comments to the hash set and leave it "dirty"
Bar b = session.find( Bar.class, bar.getId() );
b.comments = comments;
// During flushing, the comments HashSet becomes the backing collection of new PersistentSet which replaces the old entry
session.flush();
// Replace the persistent collection with the backing collection in the field
b.comments = comments;
// It's vital that we try merging a detached instance
session.detach( b );
return session.merge( b );
} );
assertThat( loadedBar.comments.size() ).isEqualTo( 1 );
}
@Entity(name = "Foo")
@Table(name = "FooSBU")
@SelectBeforeUpdate
public static class Foo {
@Id
private Integer id;
private String name;
@Version
private Integer version;
@ManyToOne
@JoinColumn(updatable = false)
private Bar bar;
Foo() {
}
Foo(Integer id, String name, Bar bar) {
this.id = id;
this.name = name;
this.bar = bar;
}
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;
}
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
}
@Entity(name = "Bar")
@Table(name = "BarSBU")
public static class Bar {
@Id
private Integer id;
private String name;
@Version
private Integer version;
@ElementCollection
private Set<Comment> comments;
Bar() {
}
Bar(Integer id, String name) {
this.id = id;
this.name = 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;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
public Set<Comment> getComments() {
return comments;
}
public void setComments(Set<Comment> comments) {
this.comments = comments;
}
}
@Embeddable
@Table(name = "CommentSBU")
public static class Comment {
@Column(name = "bar_comment")
private String comment;
private String author;
public Comment() {
}
public Comment(String comment, String author) {
this.comment = comment;
this.author = author;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
}

View File

@ -1,204 +0,0 @@
package org.hibernate.orm.test.annotations.selectbeforeupdate;
import java.util.List;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.orm.test.legacy.Child;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@DomainModel(
annotatedClasses = {
SelectBeforeUpdateWithCascadeTest.ChildEntity.class,
SelectBeforeUpdateWithCascadeTest.MappedEntity.class,
SelectBeforeUpdateWithCascadeTest.ParentEntity.class
}
)
@SessionFactory
@JiraKey("HHH-17560")
public class SelectBeforeUpdateWithCascadeTest {
@Test
public void testPersist(SessionFactoryScope scope) {
ChildEntity childEntity = new ChildEntity();
scope.inTransaction(
session -> {
childEntity.setAProperty( "property" );
childEntity.setNonInsertableProperty( "nonInsertable" );
childEntity.setNonUpdatableProperty( "nonUpdatable" );
session.persist( childEntity );
}
);
scope.inTransaction(
session -> {
ParentEntity parent1 = new ParentEntity();
parent1.setChildEntity( session.merge( childEntity ) );
session.persist( parent1 );
}
);
scope.inTransaction(
session -> {
ParentEntity parent2 = new ParentEntity();
parent2.setChildEntity( session.merge( childEntity ) );
session.persist( parent2 );
}
);
scope.inTransaction(
session -> {
List<ChildEntity> children = session.createQuery( "select c from ChildEntity c" ).list();
assertThat(children.size()).isEqualTo( 1 );
List<ParentEntity> parents = session.createQuery( "select p from ParentEntity p" ).list();
assertThat(parents.size()).isEqualTo( 2 );
}
);
}
@Entity(name = "ChildEntity")
@SelectBeforeUpdate
public static class ChildEntity {
@Id
@GeneratedValue
private Long id;
private String aProperty;
@Column(updatable = false)
private String nonUpdatableProperty;
@Column(insertable = false)
private String nonInsertableProperty;
@OneToOne(mappedBy = "childEntity")
private MappedEntity mappedEntity;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getAProperty() {
return aProperty;
}
public void setAProperty(String property) {
this.aProperty = property;
}
public MappedEntity getMappedEntity() {
return mappedEntity;
}
public void setMappedEntity(MappedEntity mappedEntity) {
this.mappedEntity = mappedEntity;
}
public String getNonUpdatableProperty() {
return nonUpdatableProperty;
}
public void setNonUpdatableProperty(String nonUpdatableProperty) {
this.nonUpdatableProperty = nonUpdatableProperty;
}
public String getNonInsertableProperty() {
return nonInsertableProperty;
}
public void setNonInsertableProperty(String nonInsertableProperty) {
this.nonInsertableProperty = nonInsertableProperty;
}
}
@Entity(name = "MappedEntity")
public static class MappedEntity {
@Id
@GeneratedValue
private Long id;
@OneToOne
@JoinColumn(name = "child_entity_id", updatable = false)
private ChildEntity childEntity;
private String property;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public ChildEntity getChildEntity() {
return childEntity;
}
public void setChildEntity(ChildEntity childEntity) {
this.childEntity = childEntity;
}
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
@Entity(name = "ParentEntity")
public static class ParentEntity {
@Id
@GeneratedValue
private Long id;
@ManyToOne
@Cascade({ CascadeType.MERGE, CascadeType.PERSIST})
@JoinColumn(name = "child_entity_id")
private ChildEntity childEntity;
public void setId(Long id) {
this.id = id;
}
public Long getId() {
return id;
}
public ChildEntity getChildEntity() {
return childEntity;
}
public void setChildEntity(ChildEntity childEntity) {
this.childEntity = childEntity;
}
}
}

View File

@ -1,253 +0,0 @@
/*
* 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.orm.test.component.empty;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
import org.hibernate.EmptyInterceptor;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.cfg.Configuration;
import org.hibernate.type.Type;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.transaction.TransactionUtil;
import static org.junit.Assert.assertEquals;
/**
* @author Chris Cranford
*/
public class SelectBeforeUpdateEmbeddedTest extends BaseCoreFunctionalTestCase {
private final OnFlushDirtyInterceptor i = new OnFlushDirtyInterceptor();
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Person.class };
}
@Override
protected void configure(Configuration configuration) {
super.configure( configuration );
// applies the OnFlushDirtyInterceptor that is meant to track whether a Flush event fires
// in situations where it ultimately shouldn't because null composites should equate an
// instantiated component with all null properties.
configuration.setInterceptor( i );
}
@Test
@TestForIssue(jiraKey = "HHH-11237")
public void testSelectBeforeUpdateUsingNullComposites() {
// Legacy behavior test
testSelectBeforeUpdate();
}
/**
* Performs various tests both available attached and detached entities which use
* the {@code @SelectBeforeUpdate} annotation with an {@code @Embedded} component.
*/
private void testSelectBeforeUpdate() {
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
final Person john = new Person( 1, "John", new Address() );
session.persist( john );
final Person mary = new Person( 2, "Mary", null );
session.persist( mary );
} );
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
final Person john = session.find( Person.class, 1 );
i.reset();
john.setAddress( null );
session.flush();
assertEquals( 0, i.getCalls() );
i.reset();
final Person mary = session.find( Person.class, 2 );
mary.setAddress( new Address() );
session.flush();
assertEquals( 0, i.getCalls() );
} );
final Person john = TransactionUtil.doInHibernate( this::sessionFactory, session -> {
return session.get( Person.class, 1 );
} );
final Person mary = TransactionUtil.doInHibernate( this::sessionFactory, session -> {
return session.get( Person.class, 2 );
} );
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
i.reset();
john.setAddress( null );
session.merge( john );
session.flush();
assertEquals( 0, i.getCalls() );
i.reset();
mary.setAddress( new Address() );
session.merge( mary );
session.flush();
assertEquals( 0, i.getCalls() );
} );
}
@Entity(name = "Person")
@SelectBeforeUpdate
public static class Person {
@Id
private Integer id;
private String name;
@Embedded
private Address address;
@Version
private Integer version;
Person() {
}
Person(Integer id, String name, Address address) {
this.id = id;
this.name = name;
this.address = address;
}
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;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Integer getVersion() {
return version;
}
public void setVersion(Integer version) {
this.version = version;
}
}
@Embeddable
public static class Address implements Serializable {
private String postalCode;
private String state;
private String address;
Address() {
}
Address(String postalCode, String state, String address) {
this.postalCode = postalCode;
this.state = state;
this.address = address;
}
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Address address1 = (Address) o;
if ( getPostalCode() != null ? !getPostalCode().equals( address1.getPostalCode() ) : address1.getPostalCode() != null ) {
return false;
}
if ( getState() != null ? !getState().equals( address1.getState() ) : address1.getState() != null ) {
return false;
}
return getAddress() != null ? getAddress().equals( address1.getAddress() ) : address1.getAddress() == null;
}
@Override
public int hashCode() {
int result = getPostalCode() != null ? getPostalCode().hashCode() : 0;
result = 31 * result + ( getState() != null ? getState().hashCode() : 0 );
result = 31 * result + ( getAddress() != null ? getAddress().hashCode() : 0 );
return result;
}
}
public static class OnFlushDirtyInterceptor extends EmptyInterceptor {
private AtomicInteger calls = new AtomicInteger();
@Override
public boolean onFlushDirty(Object entity,
Serializable id,
Object[] currentState,
Object[] previousState,
String[] propertyNames,
Type[] types) {
calls.incrementAndGet();
return false;
}
public int getCalls() {
return calls.get();
}
public void reset() {
calls.set( 0 );
}
}
}

View File

@ -9,7 +9,6 @@ package org.hibernate.orm.test.write;
import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.OptimisticLockType; import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking; import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.testing.jdbc.SQLStatementInspector; import org.hibernate.testing.jdbc.SQLStatementInspector;
@ -319,7 +318,6 @@ public class DynamicUpdateTests {
@Entity( name = "AttachableJob" ) @Entity( name = "AttachableJob" )
@Table( name = "AttachableJob" ) @Table( name = "AttachableJob" )
@DynamicUpdate @DynamicUpdate
@SelectBeforeUpdate
public static class AttachableJob { public static class AttachableJob {
@Id @Id
private Integer id; private Integer id;

View File

@ -1,222 +0,0 @@
/*
* 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.orm.test.envers.integration.update;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.envers.Audited;
import org.hibernate.orm.test.envers.BaseEnversFunctionalTestCase;
import org.hibernate.orm.test.envers.Priority;
import org.hibernate.testing.orm.junit.JiraKey;
import org.hibernate.testing.transaction.TransactionUtil;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* @author Chris Cranford
*/
@JiraKey("HHH-11056")
public class SelectBeforeUpdateTest extends BaseEnversFunctionalTestCase {
@Override
protected Class[] getAnnotatedClasses() {
return new Class<?>[] { Book.class, Author.class };
}
@Test
@Priority(10)
public void initDataUpdateDetachedUnchanged() {
final Author author = new Author( 1, "Author1" );
final Book book = new Book( 1, "Book1", author );
// Revision 1 - insert new entities.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
session.persist( author );
session.persist( book );
} );
// Revision 2 - update detached with no changes.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
session.merge( book );
} );
}
@Test
@Priority(9)
public void initDataUpdateDetachedChanged() {
final Author author = new Author( 2, "Author2" );
final Book book = new Book( 2, "Book2", author );
// Revision 1 - insert new entities.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
session.persist( author );
session.persist( book );
} );
// Revision 2 - update detached with changes.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
book.setName( "Book2Updated" );
session.merge( book );
} );
}
@Test
@Priority(8)
public void initDataUpdateDetachedUnchangedAndChanged() {
final Author author = new Author( 3, "Author3" );
final Book book = new Book( 3, "Book3", author );
// Revision 1 - insert new entities.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
session.persist( author );
session.persist( book );
} );
// Revision 2 - update detached with no changes.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
session.merge( book );
} );
// Revision 3 - update detached with changes.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
book.setName( "Book3Updated" );
session.merge( book );
} );
}
@Test
@Priority(7)
public void initDataUpdateDetachedChangedAndUnchanged() {
final Author author = new Author( 4, "Author4" );
final Book book = new Book( 4, "Book4", author );
// Revision 1 - insert new entities.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
session.persist( author );
session.persist( book );
} );
// Revision 2 - update detached with changes.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
book.setName( "Book4Updated" );
session.merge( book );
} );
// Revision 3 - update detached with no changes.
TransactionUtil.doInHibernate( this::sessionFactory, session -> {
session.merge( book );
} );
}
@Test
public void testRevisionCountsUpdateDetachedUnchanged() {
assertEquals( 1, getAuditReader().getRevisions( Author.class, 1 ).size() );
assertEquals( 1, getAuditReader().getRevisions( Book.class, 1 ).size() );
}
@Test
public void testRevisionCountsUpdateDetachedChanged() {
assertEquals( 1, getAuditReader().getRevisions( Author.class, 2 ).size() );
assertEquals( 2, getAuditReader().getRevisions( Book.class, 2 ).size() );
}
@Test
public void testRevisionCountsUpdateDetachedUnchangedAndChanged() {
assertEquals( 1, getAuditReader().getRevisions( Author.class, 3 ).size() );
assertEquals( 2, getAuditReader().getRevisions( Book.class, 3 ).size() );
}
@Test
public void testRevisionCountsUpdateDetachedChangedAndUnchanged() {
assertEquals( 1, getAuditReader().getRevisions( Author.class, 4 ).size() );
assertEquals( 2, getAuditReader().getRevisions( Book.class, 4 ).size() );
}
@Entity(name = "Book")
@SelectBeforeUpdate
@Audited
public static class Book {
@Id
private Integer id;
private String name;
@ManyToOne
@JoinColumn(updatable = false)
private Author author;
Book() {
}
Book(Integer id, String name, Author author) {
this.id = id;
this.name = name;
this.author = author;
}
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;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
@Entity(name = "Author")
@Audited
public static class Author {
@Id
private Integer id;
private String name;
Author() {
}
Author(Integer id, String name) {
this.id = id;
this.name = 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;
}
}
}