BAEL-2043: An Intro to Hibernate Entity Lifecycle (#4960)

* "An Intro to Hibernate Entity Lifecycle"

"An Intro to Hibernate Entity Lifecycle"
by Rudi

* Revision from Predrag's feedback

* Another revision from Predrag's feedback

* Another feedback.
This commit is contained in:
bungrudi 2018-08-15 20:23:19 +07:00 committed by Predrag Maric
parent 96e1728d7f
commit 3f2271f51b
7 changed files with 355 additions and 1 deletions

View File

@ -0,0 +1,26 @@
package com.baeldung.hibernate.lifecycle;
import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
public class DirtyDataInspector extends EmptyInterceptor {
private static final ArrayList<FootballPlayer> dirtyEntities = new ArrayList<>();
@Override
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
dirtyEntities.add((FootballPlayer) entity);
return true;
}
public static List<FootballPlayer> getDirtyEntities() {
return dirtyEntities;
}
public static void clearDirtyEntitites() {
dirtyEntities.clear();
}
}

View File

@ -0,0 +1,35 @@
package com.baeldung.hibernate.lifecycle;
import javax.persistence.*;
@Entity
@Table(name = "Football_Player")
public class FootballPlayer {
@Id
@GeneratedValue
private long id;
@Column
private String name;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "FootballPlayer{" + "id=" + id + ", name='" + name + '\'' + '}';
}
}

View File

@ -0,0 +1,96 @@
package com.baeldung.hibernate.lifecycle;
import org.h2.tools.RunScript;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.service.ServiceRegistry;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
public class HibernateLifecycleUtil {
private static SessionFactory sessionFactory;
private static Connection connection;
public static void init() throws Exception {
Properties hbConfigProp = getHibernateProperties();
Class.forName(hbConfigProp.getProperty("hibernate.connection.driver_class"));
connection = DriverManager.getConnection(hbConfigProp.getProperty("hibernate.connection.url"), hbConfigProp.getProperty("hibernate.connection.username"), hbConfigProp.getProperty("hibernate.connection.password"));
try (InputStream h2InitStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("lifecycle-init.sql");
InputStreamReader h2InitReader = new InputStreamReader(h2InitStream)) {
RunScript.execute(connection, h2InitReader);
}
ServiceRegistry serviceRegistry = configureServiceRegistry();
sessionFactory = getSessionFactoryBuilder(serviceRegistry).applyInterceptor(new DirtyDataInspector()).build();
}
public static void tearDown() throws Exception {
sessionFactory.close();
connection.close();
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
private static SessionFactoryBuilder getSessionFactoryBuilder(ServiceRegistry serviceRegistry) {
MetadataSources metadataSources = new MetadataSources(serviceRegistry);
metadataSources.addAnnotatedClass(FootballPlayer.class);
Metadata metadata = metadataSources.buildMetadata();
return metadata.getSessionFactoryBuilder();
}
private static ServiceRegistry configureServiceRegistry() throws IOException {
Properties properties = getHibernateProperties();
return new StandardServiceRegistryBuilder().applySettings(properties).build();
}
private static Properties getHibernateProperties() throws IOException {
Properties properties = new Properties();
URL propertiesURL = Thread.currentThread().getContextClassLoader().getResource("hibernate-lifecycle.properties");
try (FileInputStream inputStream = new FileInputStream(propertiesURL.getFile())) {
properties.load(inputStream);
}
return properties;
}
public static List<EntityEntry> getManagedEntities(Session session) {
Map.Entry<Object, EntityEntry>[] entries = ((SessionImplementor) session).getPersistenceContext().reentrantSafeEntityEntries();
return Arrays.stream(entries).map(e -> e.getValue()).collect(Collectors.toList());
}
public static Transaction startTransaction(Session s) {
Transaction tx = s.getTransaction();
tx.begin();
return tx;
}
public static int queryCount(String query) throws Exception {
try (ResultSet rs = connection.createStatement().executeQuery(query)) {
rs.next();
return rs.getInt(1);
}
}
}

View File

@ -58,5 +58,4 @@ public class HibernateInterceptorUnitTest {
transaction.commit();
session.close();
}
}

View File

@ -0,0 +1,164 @@
package com.baeldung.hibernate.lifecycle;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.engine.spi.Status;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
import static com.baeldung.hibernate.lifecycle.DirtyDataInspector.getDirtyEntities;
import static com.baeldung.hibernate.lifecycle.HibernateLifecycleUtil.*;
import static org.assertj.core.api.Assertions.assertThat;
public class HibernateLifecycleUnitTest {
@BeforeClass
public static void setup() throws Exception {
HibernateLifecycleUtil.init();
}
@AfterClass
public static void tearDown() throws Exception {
HibernateLifecycleUtil.tearDown();
}
@Before
public void beforeMethod() {
DirtyDataInspector.clearDirtyEntitites();
}
@Test
public void whenEntityLoaded_thenEntityManaged() throws Exception {
SessionFactory sessionFactory = HibernateLifecycleUtil.getSessionFactory();
try (Session session = sessionFactory.openSession()) {
Transaction transaction = startTransaction(session);
assertThat(getManagedEntities(session)).isEmpty();
List<FootballPlayer> players = session.createQuery("from FootballPlayer").getResultList();
assertThat(getManagedEntities(session)).size().isEqualTo(3);
assertThat(getDirtyEntities()).isEmpty();
FootballPlayer gigiBuffon = players.stream().filter(p -> p.getId() == 3).findFirst().get();
gigiBuffon.setName("Gianluigi Buffon");
transaction.commit();
assertThat(getDirtyEntities()).size().isEqualTo(1);
assertThat(getDirtyEntities().get(0).getId()).isEqualTo(3);
assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Gianluigi Buffon");
}
}
@Test
public void whenDetached_thenNotTracked() throws Exception {
SessionFactory sessionFactory = HibernateLifecycleUtil.getSessionFactory();
try (Session session = sessionFactory.openSession()) {
Transaction transaction = startTransaction(session);
FootballPlayer cr7 = session.get(FootballPlayer.class, 1L);
assertThat(getManagedEntities(session)).size().isEqualTo(1);
assertThat(getManagedEntities(session).get(0).getId()).isEqualTo(cr7.getId());
session.evict(cr7);
assertThat(getManagedEntities(session)).size().isEqualTo(0);
cr7.setName("CR7");
transaction.commit();
assertThat(getDirtyEntities()).isEmpty();
}
}
@Test
public void whenReattached_thenTrackedAgain() throws Exception {
SessionFactory sessionFactory = HibernateLifecycleUtil.getSessionFactory();
try (Session session = sessionFactory.openSession()) {
Transaction transaction = startTransaction(session);
FootballPlayer messi = session.get(FootballPlayer.class, 2L);
session.evict(messi);
messi.setName("Leo Messi");
transaction.commit();
assertThat(getDirtyEntities()).isEmpty();
transaction = startTransaction(session);
session.update(messi);
transaction.commit();
assertThat(getDirtyEntities()).size().isEqualTo(1);
assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Leo Messi");
}
}
@Test
public void givenNewEntityWithID_whenReattached_thenManaged() throws Exception {
SessionFactory sessionFactory = HibernateLifecycleUtil.getSessionFactory();
try (Session session = sessionFactory.openSession()) {
Transaction transaction = startTransaction(session);
FootballPlayer gigi = new FootballPlayer();
gigi.setId(3);
gigi.setName("Gigi the Legend");
session.update(gigi);
assertThat(getManagedEntities(session)).size().isEqualTo(1);
transaction.commit();
assertThat(getDirtyEntities()).size().isEqualTo(1);
}
}
@Test
public void givenTransientEntity_whenSave_thenManaged() throws Exception {
SessionFactory sessionFactory = HibernateLifecycleUtil.getSessionFactory();
try (Session session = sessionFactory.openSession()) {
Transaction transaction = startTransaction(session);
FootballPlayer neymar = new FootballPlayer();
neymar.setName("Neymar");
session.save(neymar);
assertThat(getManagedEntities(session)).size().isEqualTo(1);
assertThat(neymar.getId()).isNotNull();
int count = queryCount("select count(*) from Football_Player where name='Neymar'");
assertThat(count).isEqualTo(0);
transaction.commit();
count = queryCount("select count(*) from Football_Player where name='Neymar'");
assertThat(count).isEqualTo(1);
transaction = startTransaction(session);
session.delete(neymar);
transaction.commit();
}
}
@Test()
public void whenDelete_thenMarkDeleted() throws Exception {
SessionFactory sessionFactory = HibernateLifecycleUtil.getSessionFactory();
try (Session session = sessionFactory.openSession()) {
Transaction transaction = startTransaction(session);
FootballPlayer neymar = new FootballPlayer();
neymar.setName("Neymar");
session.save(neymar);
transaction.commit();
transaction = startTransaction(session);
session.delete(neymar);
assertThat(getManagedEntities(session).get(0).getStatus()).isEqualTo(Status.DELETED);
transaction.commit();
}
}
}

View File

@ -0,0 +1,9 @@
hibernate.connection.driver_class=org.h2.Driver
hibernate.connection.url=jdbc:h2:mem:lifecycledb;DB_CLOSE_DELAY=-1;
hibernate.connection.username=sa
hibernate.connection.password=
hibernate.connection.autocommit=true
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.show_sql=true
hibernate.hbm2ddl.auto=validate

View File

@ -0,0 +1,25 @@
create sequence hibernate_sequence start with 1 increment by 1;
create table Football_Player (
id bigint not null,
name varchar(255),
primary key (id)
);
insert into
Football_Player
(name, id)
values
('Cristiano Ronaldo', next value for hibernate_sequence);
insert into
Football_Player
(name, id)
values
('Lionel Messi', next value for hibernate_sequence);
insert into
Football_Player
(name, id)
values
('Gigi Buffon', next value for hibernate_sequence);