Skip to content

Commit

Permalink
Add the amazing HibernateRepository #444
Browse files Browse the repository at this point in the history
  • Loading branch information
vladmihalcea committed Jul 21, 2022
1 parent 94b8340 commit 44e3319
Show file tree
Hide file tree
Showing 26 changed files with 1,568 additions and 2 deletions.
83 changes: 83 additions & 0 deletions hibernate-types-52/pom.xml
Expand Up @@ -70,6 +70,84 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>${hikari.version}</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring-data.version}</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<type>jar</type>
<scope>test</scope>
</dependency>

</dependencies>

<properties>
Expand All @@ -85,6 +163,11 @@
<guava.version>31.1-jre</guava.version>
<moneta.version>1.4.2</moneta.version>

<hikari.version>4.0.3</hikari.version>
<aspectj.version>1.8.7</aspectj.version>
<spring.version>5.3.18</spring.version>
<spring-data.version>2.6.0</spring-data.version>

</properties>

<build>
Expand Down
@@ -0,0 +1,62 @@
package com.vladmihalcea.spring.repository;

import java.util.List;

/**
* The {@code HibernateRepository} fixes the problems that the default Spring Data {@code JpaRepository}
* suffers from.
*
* For more details about how to use it, check out <a href=
* "https://vladmihalcea.com/best-spring-data-jparepository/">this article</a>.
*
* @author Vlad Mihalcea
* @version 2.17.0
*/
public interface HibernateRepository<T> {

//Save methods will trigger an UnsupportedOperationException

@Deprecated
<S extends T> S save(S entity);

@Deprecated
<S extends T> List<S> saveAll(Iterable<S> entities);

@Deprecated
<S extends T> S saveAndFlush(S entity);

@Deprecated
<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);

//Persist methods are meant to save newly created entities

<S extends T> S persist(S entity);

<S extends T> S persistAndFlush(S entity);

<S extends T> List<S> persistAll(Iterable<S> entities);

<S extends T> List<S> peristAllAndFlush(Iterable<S> entities);

//Merge methods are meant to propagate detached entity state changes
//if they are really needed

<S extends T> S merge(S entity);

<S extends T> S mergeAndFlush(S entity);

<S extends T> List<S> mergeAll(Iterable<S> entities);

<S extends T> List<S> mergeAllAndFlush(Iterable<S> entities);

//Update methods are meant to force the detached entity state changes

<S extends T> S update(S entity);

<S extends T> S updateAndFlush(S entity);

<S extends T> List<S> updateAll(Iterable<S> entities);

<S extends T> List<S> updateAllAndFlush(Iterable<S> entities);

}
@@ -0,0 +1,167 @@
package com.vladmihalcea.spring.repository;

import org.hibernate.Session;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.AbstractSharedSessionContract;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;

/**
* @author Vlad Mihalcea
*/
public class HibernateRepositoryImpl<T> implements HibernateRepository<T> {

@PersistenceContext
private EntityManager entityManager;

@Override
public <S extends T> S save(S entity) {
return unsupported();
}

@Override
public <S extends T> List<S> saveAll(Iterable<S> entities) {
return unsupported();
}

@Override
public <S extends T> S saveAndFlush(S entity) {
return unsupported();
}

@Override
public <S extends T> List<S> saveAllAndFlush(Iterable<S> entities) {
return unsupported();
}

public <S extends T> S persist(S entity) {
entityManager.persist(entity);
return entity;
}

public <S extends T> S persistAndFlush(S entity) {
persist(entity);
entityManager.flush();
return entity;
}

public <S extends T> List<S> persistAll(Iterable<S> entities) {
List<S> result = new ArrayList<>();
for(S entity : entities) {
result.add(persist(entity));
}
return result;
}

public <S extends T> List<S> peristAllAndFlush(Iterable<S> entities) {
return executeBatch(() -> {
List<S> result = new ArrayList<>();
for(S entity : entities) {
result.add(persist(entity));
}
entityManager.flush();
return result;
});
}

public <S extends T> S merge(S entity) {
return entityManager.merge(entity);
}

@Override
public <S extends T> S mergeAndFlush(S entity) {
S result = merge(entity);
entityManager.flush();
return result;
}

@Override
public <S extends T> List<S> mergeAll(Iterable<S> entities) {
List<S> result = new ArrayList<>();
for(S entity : entities) {
result.add(merge(entity));
}
return result;
}

@Override
public <S extends T> List<S> mergeAllAndFlush(Iterable<S> entities) {
return executeBatch(() -> {
List<S> result = new ArrayList<>();
for(S entity : entities) {
result.add(merge(entity));
}
entityManager.flush();
return result;
});
}

public <S extends T> S update(S entity) {
session().update(entity);
return entity;
}

@Override
public <S extends T> S updateAndFlush(S entity) {
update(entity);
entityManager.flush();
return entity;
}

@Override
public <S extends T> List<S> updateAll(Iterable<S> entities) {
List<S> result = new ArrayList<>();
for(S entity : entities) {
result.add(update(entity));
}
return result;
}

@Override
public <S extends T> List<S> updateAllAndFlush(Iterable<S> entities) {
return executeBatch(() -> {
List<S> result = new ArrayList<>();
for(S entity : entities) {
result.add(update(entity));
}
entityManager.flush();
return result;
});
}

protected Integer getBatchSize(Session session) {
SessionFactoryImplementor sessionFactory = session.getSessionFactory().unwrap(SessionFactoryImplementor.class);
final JdbcServices jdbcServices = sessionFactory.getServiceRegistry().getService(JdbcServices.class);
if(!jdbcServices.getExtractedMetaDataSupport().supportsBatchUpdates()) {
return Integer.MIN_VALUE;
}
return session.unwrap(AbstractSharedSessionContract.class).getConfiguredJdbcBatchSize();
}

protected <R> R executeBatch(Supplier<R> callback) {
Session session = session();
Integer jdbcBatchSize = getBatchSize(session);
Integer originalSessionBatchSize = session.getJdbcBatchSize();
try {
if (jdbcBatchSize == null) {
session.setJdbcBatchSize(10);
}
return callback.get();
} finally {
session.setJdbcBatchSize(originalSessionBatchSize);
}
}

protected Session session() {
return entityManager.unwrap(Session.class);
}

protected <S extends T> S unsupported() {
throw new UnsupportedOperationException("There's no such thing as a save method in JPA, so don't use this hack!");
}
}

0 comments on commit 44e3319

Please sign in to comment.