Merge pull request #6943 from amit2103/BAEL-14320

[BAEL-14320] - Moved code for Intro to Spring Data JPA Article
This commit is contained in:
Loredana Crusoveanu 2019-05-12 15:51:43 +03:00 committed by GitHub
commit 5cd0d8e723
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 669 additions and 5 deletions

View File

@ -9,6 +9,7 @@
- [The DAO with Spring and Hibernate](http://www.baeldung.com/persistence-layer-with-spring-and-hibernate)
- [DAO with Spring and Generics](https://www.baeldung.com/simplifying-the-data-access-layer-with-spring-and-java-generics)
- [Transactions with Spring and JPA](https://www.baeldung.com/transaction-configuration-with-jpa-and-spring)
- [Introduction to Spring Data JPA](http://www.baeldung.com/the-persistence-layer-with-spring-data-jpa)
### Eclipse Config

View File

@ -28,9 +28,10 @@ public abstract class AbstractHibernateDao<T extends Serializable> extends Abstr
}
@Override
public void create(final T entity) {
public T create(final T entity) {
Preconditions.checkNotNull(entity);
getCurrentSession().saveOrUpdate(entity);
return entity;
}
@Override

View File

@ -9,7 +9,7 @@ public interface IOperations<T extends Serializable> {
List<T> findAll();
void create(final T entity);
T create(final T entity);
T update(final T entity);

View File

@ -26,8 +26,9 @@ public abstract class AbstractJpaDAO<T extends Serializable> {
return entityManager.createQuery("from " + clazz.getName()).getResultList();
}
public void create(final T entity) {
public T create(final T entity) {
entityManager.persist(entity);
return entity;
}
public T update(final T entity) {

View File

@ -10,7 +10,7 @@ public interface IFooDao {
List<Foo> findAll();
void create(Foo entity);
Foo create(Foo entity);
Foo update(Foo entity);

View File

@ -0,0 +1,85 @@
package org.baeldung.spring.data.persistence.config;
import java.util.Properties;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import com.google.common.base.Preconditions;
@Configuration
@EnableTransactionManagement
@PropertySource({ "classpath:persistence-${envTarget:h2}.properties" })
@ComponentScan({ "org.baeldung.spring.data.persistence" })
// @ImportResource("classpath*:springDataPersistenceConfig.xml")
@EnableJpaRepositories(basePackages = "org.baeldung.spring.data.persistence.dao")
public class PersistenceConfig {
@Autowired
private Environment env;
public PersistenceConfig() {
super();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "org.baeldung.spring.data.persistence.model" });
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
// vendorAdapter.set
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
@Bean
public DataSource dataSource() {
final DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(Preconditions.checkNotNull(env.getProperty("jdbc.driverClassName")));
dataSource.setUrl(Preconditions.checkNotNull(env.getProperty("jdbc.url")));
dataSource.setUsername(Preconditions.checkNotNull(env.getProperty("jdbc.user")));
dataSource.setPassword(Preconditions.checkNotNull(env.getProperty("jdbc.pass")));
return dataSource;
}
@Bean
public PlatformTransactionManager transactionManager() {
final JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
return new PersistenceExceptionTranslationPostProcessor();
}
final Properties additionalProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
// hibernateProperties.setProperty("hibernate.globally_quoted_identifiers", "true");
return hibernateProperties;
}
}

View File

@ -0,0 +1,11 @@
package org.baeldung.spring.data.persistence.dao;
import org.baeldung.spring.data.persistence.model.Foo;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface IFooDao extends JpaRepository<Foo, Long> {
@Query("SELECT f FROM Foo f WHERE LOWER(f.name) = LOWER(:name)")
Foo retrieveByName(@Param("name") String name);
}

View File

@ -0,0 +1,83 @@
package org.baeldung.spring.data.persistence.model;
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Foo implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(nullable = false)
private String name;
public Foo() {
super();
}
public Foo(final String name) {
super();
this.name = name;
}
// API
public long getId() {
return id;
}
public void setId(final long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
//
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
final Foo other = (Foo) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Foo [name=").append(name).append("]");
return builder.toString();
}
}

View File

@ -0,0 +1,11 @@
package org.baeldung.spring.data.persistence.service;
import org.baeldung.spring.data.persistence.model.Foo;
import com.baeldung.persistence.dao.common.IOperations;
public interface IFooService extends IOperations<Foo> {
Foo retrieveByName(String name);
}

View File

@ -0,0 +1,56 @@
package org.baeldung.spring.data.persistence.service.common;
import java.io.Serializable;
import java.util.List;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.transaction.annotation.Transactional;
import com.baeldung.persistence.dao.common.IOperations;
import com.google.common.collect.Lists;
@Transactional
public abstract class AbstractService<T extends Serializable> implements IOperations<T> {
// read - one
@Override
@Transactional(readOnly = true)
public T findOne(final long id) {
return getDao().findById(id).orElse(null);
}
// read - all
@Override
@Transactional(readOnly = true)
public List<T> findAll() {
return Lists.newArrayList(getDao().findAll());
}
// write
@Override
public T create(final T entity) {
return getDao().save(entity);
}
@Override
public T update(final T entity) {
return getDao().save(entity);
}
@Override
public void delete(T entity) {
getDao().delete(entity);
}
@Override
public void deleteById(long entityId) {
T entity = findOne(entityId);
delete(entity);
}
protected abstract PagingAndSortingRepository<T, Long> getDao();
}

View File

@ -0,0 +1,38 @@
package org.baeldung.spring.data.persistence.service.impl;
import org.baeldung.spring.data.persistence.model.Foo;
import org.baeldung.spring.data.persistence.dao.IFooDao;
import org.baeldung.spring.data.persistence.service.IFooService;
import org.baeldung.spring.data.persistence.service.common.AbstractService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class FooService extends AbstractService<Foo> implements IFooService {
@Autowired
private IFooDao dao;
public FooService() {
super();
}
// API
@Override
protected PagingAndSortingRepository<Foo, Long> getDao() {
return dao;
}
// custom methods
@Override
public Foo retrieveByName(final String name) {
return dao.retrieveByName(name);
}
}

View File

@ -0,0 +1,33 @@
package org.baeldung.util;
import java.util.Random;
public final class IDUtil {
private IDUtil() {
throw new AssertionError();
}
// API
public static String randomPositiveLongAsString() {
return Long.toString(randomPositiveLong());
}
public static String randomNegativeLongAsString() {
return Long.toString(randomNegativeLong());
}
public static long randomPositiveLong() {
long id = new Random().nextLong() * 10000;
id = (id < 0) ? (-1 * id) : id;
return id;
}
private static long randomNegativeLong() {
long id = new Random().nextLong() * 10000;
id = (id > 0) ? (-1 * id) : id;
return id;
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"
>
<jpa:repositories base-package="org.baeldung.persistence.dao"/>
</beans>

View File

@ -0,0 +1,256 @@
package org.baeldung.spring.data.persistence.service;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import java.io.Serializable;
import java.util.List;
import org.baeldung.spring.data.persistence.model.Foo;
import org.baeldung.util.IDUtil;
import org.hamcrest.Matchers;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.dao.DataAccessException;
import com.baeldung.persistence.dao.common.IOperations;
public abstract class AbstractServicePersistenceIntegrationTest<T extends Serializable> {
// tests
// find - one
@Test
/**/public final void givenResourceDoesNotExist_whenResourceIsRetrieved_thenNoResourceIsReceived() {
// When
final Foo createdResource = getApi().findOne(IDUtil.randomPositiveLong());
// Then
assertNull(createdResource);
}
@Test
public void givenResourceExists_whenResourceIsRetrieved_thenNoExceptions() {
final Foo existingResource = persistNewEntity();
getApi().findOne(existingResource.getId());
}
@Test
public void givenResourceDoesNotExist_whenResourceIsRetrieved_thenNoExceptions() {
getApi().findOne(IDUtil.randomPositiveLong());
}
@Test
public void givenResourceExists_whenResourceIsRetrieved_thenTheResultIsNotNull() {
final Foo existingResource = persistNewEntity();
final Foo retrievedResource = getApi().findOne(existingResource.getId());
assertNotNull(retrievedResource);
}
@Test
public void givenResourceExists_whenResourceIsRetrieved_thenResourceIsRetrievedCorrectly() {
final Foo existingResource = persistNewEntity();
final Foo retrievedResource = getApi().findOne(existingResource.getId());
assertEquals(existingResource, retrievedResource);
}
// find - one - by name
// find - all
@Test
/**/public void whenAllResourcesAreRetrieved_thenNoExceptions() {
getApi().findAll();
}
@Test
/**/public void whenAllResourcesAreRetrieved_thenTheResultIsNotNull() {
final List<Foo> resources = getApi().findAll();
assertNotNull(resources);
}
@Test
/**/public void givenAtLeastOneResourceExists_whenAllResourcesAreRetrieved_thenRetrievedResourcesAreNotEmpty() {
persistNewEntity();
// When
final List<Foo> allResources = getApi().findAll();
// Then
assertThat(allResources, not(Matchers.<Foo> empty()));
}
@Test
/**/public void givenAnResourceExists_whenAllResourcesAreRetrieved_thenTheExistingResourceIsIndeedAmongThem() {
final Foo existingResource = persistNewEntity();
final List<Foo> resources = getApi().findAll();
assertThat(resources, hasItem(existingResource));
}
@Test
/**/public void whenAllResourcesAreRetrieved_thenResourcesHaveIds() {
persistNewEntity();
// When
final List<Foo> allResources = getApi().findAll();
// Then
for (final Foo resource : allResources) {
assertNotNull(resource.getId());
}
}
// create
@Test(expected = RuntimeException.class)
/**/public void whenNullResourceIsCreated_thenException() {
getApi().create(null);
}
@Test
/**/public void whenResourceIsCreated_thenNoExceptions() {
persistNewEntity();
}
@Test
/**/public void whenResourceIsCreated_thenResourceIsRetrievable() {
final Foo existingResource = persistNewEntity();
assertNotNull(getApi().findOne(existingResource.getId()));
}
@Test
/**/public void whenResourceIsCreated_thenSavedResourceIsEqualToOriginalResource() {
final Foo originalResource = createNewEntity();
final Foo savedResource = getApi().create(originalResource);
assertEquals(originalResource, savedResource);
}
@Test(expected = RuntimeException.class)
public void whenResourceWithFailedConstraintsIsCreated_thenException() {
final Foo invalidResource = createNewEntity();
invalidate(invalidResource);
getApi().create(invalidResource);
}
/**
* -- specific to the persistence engine
*/
@Test(expected = DataAccessException.class)
@Ignore("Hibernate simply ignores the id silently and still saved (tracking this)")
public void whenResourceWithIdIsCreated_thenDataAccessException() {
final Foo resourceWithId = createNewEntity();
resourceWithId.setId(IDUtil.randomPositiveLong());
getApi().create(resourceWithId);
}
// update
@Test(expected = RuntimeException.class)
/**/public void whenNullResourceIsUpdated_thenException() {
getApi().update(null);
}
@Test
/**/public void givenResourceExists_whenResourceIsUpdated_thenNoExceptions() {
// Given
final Foo existingResource = persistNewEntity();
// When
getApi().update(existingResource);
}
/**
* - can also be the ConstraintViolationException which now occurs on the update operation will not be translated; as a consequence, it will be a TransactionSystemException
*/
@Test(expected = RuntimeException.class)
public void whenResourceIsUpdatedWithFailedConstraints_thenException() {
final Foo existingResource = persistNewEntity();
invalidate(existingResource);
getApi().update(existingResource);
}
@Test
/**/public void givenResourceExists_whenResourceIsUpdated_thenUpdatesArePersisted() {
// Given
final Foo existingResource = persistNewEntity();
// When
change(existingResource);
getApi().update(existingResource);
final Foo updatedResource = getApi().findOne(existingResource.getId());
// Then
assertEquals(existingResource, updatedResource);
}
// delete
// @Test(expected = RuntimeException.class)
// public void givenResourceDoesNotExists_whenResourceIsDeleted_thenException() {
// // When
// getApi().delete(IDUtil.randomPositiveLong());
// }
//
// @Test(expected = RuntimeException.class)
// public void whenResourceIsDeletedByNegativeId_thenException() {
// // When
// getApi().delete(IDUtil.randomNegativeLong());
// }
//
// @Test
// public void givenResourceExists_whenResourceIsDeleted_thenNoExceptions() {
// // Given
// final Foo existingResource = persistNewEntity();
//
// // When
// getApi().delete(existingResource.getId());
// }
//
// @Test
// /**/public final void givenResourceExists_whenResourceIsDeleted_thenResourceNoLongerExists() {
// // Given
// final Foo existingResource = persistNewEntity();
//
// // When
// getApi().delete(existingResource.getId());
//
// // Then
// assertNull(getApi().findOne(existingResource.getId()));
// }
// template method
protected Foo createNewEntity() {
return new Foo(randomAlphabetic(6));
}
protected abstract IOperations<Foo> getApi();
private final void invalidate(final Foo entity) {
entity.setName(null);
}
private final void change(final Foo entity) {
entity.setName(randomAlphabetic(6));
}
protected Foo persistNewEntity() {
return getApi().create(createNewEntity());
}
}

View File

@ -0,0 +1,77 @@
package org.baeldung.spring.data.persistence.service;
import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
import static org.junit.Assert.assertNotNull;
import org.baeldung.spring.data.persistence.model.Foo;
import org.baeldung.spring.data.persistence.config.PersistenceConfig;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import com.baeldung.persistence.dao.common.IOperations;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { PersistenceConfig.class }, loader = AnnotationConfigContextLoader.class)
public class FooServicePersistenceIntegrationTest extends AbstractServicePersistenceIntegrationTest<Foo> {
@Autowired
private IFooService service;
// tests
@Test
public final void whenContextIsBootstrapped_thenNoExceptions() {
//
}
@Test
public final void whenEntityIsCreated_thenNoExceptions() {
service.create(new Foo(randomAlphabetic(6)));
}
@Test(expected = DataIntegrityViolationException.class)
public final void whenInvalidEntityIsCreated_thenDataException() {
service.create(new Foo());
}
@Test(expected = DataIntegrityViolationException.class)
public final void whenEntityWithLongNameIsCreated_thenDataException() {
service.create(new Foo(randomAlphabetic(2048)));
}
// custom Query method
@Test
public final void givenUsingCustomQuery_whenRetrievingEntity_thenFound() {
final String name = randomAlphabetic(6);
service.create(new Foo(name));
final Foo retrievedByName = service.retrieveByName(name);
assertNotNull(retrievedByName);
}
// work in progress
@Test(expected = InvalidDataAccessApiUsageException.class)
@Ignore("Right now, persist has saveOrUpdate semantics, so this will no longer fail")
public final void whenSameEntityIsCreatedTwice_thenDataException() {
final Foo entity = new Foo(randomAlphabetic(8));
service.create(entity);
service.create(entity);
}
// API
@Override
protected final IOperations<Foo> getApi() {
return service;
}
}

View File

@ -9,7 +9,6 @@ The "Learn Spring Security" Classes: http://github.learnspringsecurity.com
### Relevant Articles:
- [Integration Testing with the Maven Cargo plugin](http://www.baeldung.com/integration-testing-with-the-maven-cargo-plugin)
- [Introduction to Spring Data JPA](http://www.baeldung.com/the-persistence-layer-with-spring-data-jpa)
- [Project Configuration with Spring](http://www.baeldung.com/project-configuration-with-spring)
- [Metrics for your Spring REST API](http://www.baeldung.com/spring-rest-api-metrics)
- [Spring Security Expressions - hasRole Example](https://www.baeldung.com/spring-security-expressions-basic)