Browse Source

DATAJDBC-105 - Test with multiple databases.

Different databases are now supported by means of Maven Profiles and Spring Profiles. There is one Profile for each supported database. The default Profile is equivalent to hql. There is a Maven Profile all-dbs, which runs the integration tests against all databases.

The databases need to be started outside the build. The build assumes the default configuration as I found it after `brew install <database>`

For now we support the databases mysql, postgres and hsqldb.

In order to make the new databases work setting of properties and reading generated ids was improved to do some simple conversions. This might be considered a first step towards DATAJDBC-104.

The project root contains some basic scripts for starting and stopping databases, as well as running a build against all supported databases. Integration tests using a database now use Rules instead of JUnit runners. This gives more flexibility when adding fancy stuff to the Tests in the form of other Rules.

Related issue: DATAJDBC-104.
Original pull request: #5.
pull/6/merge
Jens Schauder 9 years ago committed by Oliver Gierke
parent
commit
1582a4d475
  1. 78
      pom.xml
  2. 29
      readme.md
  3. 3
      run-tests-against-all-dbs.sh
  4. 3
      src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreation.java
  5. 7
      src/main/java/org/springframework/data/jdbc/mapping/event/AfterDelete.java
  6. 2
      src/main/java/org/springframework/data/jdbc/mapping/event/AfterSave.java
  7. 5
      src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsert.java
  8. 2
      src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSave.java
  9. 12
      src/main/java/org/springframework/data/jdbc/mapping/event/Identifier.java
  10. 21
      src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEvent.java
  11. 2
      src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithId.java
  12. 3
      src/main/java/org/springframework/data/jdbc/mapping/event/WithEntity.java
  13. 9
      src/main/java/org/springframework/data/jdbc/mapping/event/WithId.java
  14. 2
      src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentEntity.java
  15. 17
      src/main/java/org/springframework/data/jdbc/repository/EntityRowMapper.java
  16. 7
      src/main/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapper.java
  17. 62
      src/main/java/org/springframework/data/jdbc/repository/SimpleJdbcRepository.java
  18. 78
      src/main/java/org/springframework/data/jdbc/repository/SqlGenerator.java
  19. 6
      src/test/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapperTest.java
  20. 49
      src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java
  21. 96
      src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java
  22. 42
      src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java
  23. 59
      src/test/java/org/springframework/data/jdbc/testing/DataSourceFactoryBean.java
  24. 52
      src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceFactoryBean.java
  25. 84
      src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceFactoryBean.java
  26. 69
      src/test/java/org/springframework/data/jdbc/testing/PostgresDataSourceFactoryBean.java
  27. 2
      src/test/resources/create-mysql.sql
  28. 1
      src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-hsql.sql
  29. 2
      src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-mysql.sql
  30. 5
      src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-postgres.sql
  31. 1
      src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql
  32. 1
      src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql
  33. 2
      src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql
  34. 1
      src/test/resources/org.springframework.data.jdbc.repository/jdbc-repository-integration-tests.sql
  35. 7
      start-all-dbs.sh
  36. 7
      stop-all-dbs.sh

78
pom.xml

@ -42,6 +42,50 @@ @@ -42,6 +42,50 @@
</plugins>
</build>
</profile>
<profile>
<id>all-dbs</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>mysql-test</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>
<include>**/*IntegrationTests.java</include>
</includes>
<systemPropertyVariables>
<spring.profiles.active>mysql</spring.profiles.active>
</systemPropertyVariables>
</configuration>
</execution>
<execution>
<id>postgres-test</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<includes>
<include>**/*IntegrationTests.java</include>
</includes>
<systemPropertyVariables>
<spring.profiles.active>postgres</spring.profiles.active>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<dependencies>
@ -97,10 +141,33 @@ @@ -97,10 +141,33 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<!--
@ -127,7 +194,16 @@ @@ -127,7 +194,16 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.12</version>
<executions>
<execution>
<id>default-test</id>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

29
readme.md

@ -9,6 +9,35 @@ The primary goal of the [Spring Data](http://projects.spring.io/spring-data) pro @@ -9,6 +9,35 @@ The primary goal of the [Spring Data](http://projects.spring.io/spring-data) pro
## Quick Start ##
## Execute Tests ##
### Fast running tests
Fast running tests can executed with a simple
mvn test
This will execute unit tests and integration tests using an in-memory database.
### Running tests with a real database
To run the integration tests against a specific database you nned to have the database running on your local machine and then execute.
mvn test -Dspring.profiles.active=<databasetype>
This will also execute the unit tests.
Currently the following *databasetypes* are available:
* hsql (default, does not need to be running)
* mysql
### Run tests with all databases
mvn test -Pall-dbs
This will execute the unit tests, and all the integration tests with all the databases we currently support for testing. The databases must be running.
## Contributing to Spring Data JDBC ##
Here are some ways for you to get involved in the community:

3
run-tests-against-all-dbs.sh

@ -0,0 +1,3 @@ @@ -0,0 +1,3 @@
#!/bin/sh
./start-all-dbs.sh && mvn clean install -Pall-dbs && ./stop-all-dbs.sh

3
src/main/java/org/springframework/data/jdbc/mapping/event/AfterCreation.java

@ -18,7 +18,8 @@ package org.springframework.data.jdbc.mapping.event; @@ -18,7 +18,8 @@ package org.springframework.data.jdbc.mapping.event;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
* Gets published after instantiation and setting of all the properties of an entity. This allows to do some postprocessing of entities.
* Gets published after instantiation and setting of all the properties of an entity. This allows to do some
* postprocessing of entities.
*
* @author Jens Schauder
* @since 2.0

7
src/main/java/org/springframework/data/jdbc/mapping/event/AfterDelete.java

@ -20,14 +20,13 @@ import java.util.Optional; @@ -20,14 +20,13 @@ import java.util.Optional;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
* Gets published after deletion of an entity. It will have a {@link Specified} identifier.
*
* If the entity is empty or not depends on the delete method used.
* Gets published after deletion of an entity. It will have a {@link Specified} identifier. If the entity is empty or
* not depends on the delete method used.
*
* @author Jens Schauder
* @since 2.0
*/
public class AfterDelete extends JdbcEventWithId{
public class AfterDelete extends JdbcEventWithId {
/**
* @param id of the entity.

2
src/main/java/org/springframework/data/jdbc/mapping/event/AfterSave.java

@ -15,8 +15,6 @@ @@ -15,8 +15,6 @@
*/
package org.springframework.data.jdbc.mapping.event;
import java.util.Optional;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**

5
src/main/java/org/springframework/data/jdbc/mapping/event/BeforeInsert.java

@ -19,9 +19,8 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Unset; @@ -19,9 +19,8 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Unset;
/**
* Gets published before an entity gets inserted into the database. When the id-property of the entity must get set
* manually, an event listener for this event may do so.
*
* The {@link Identifier} is {@link org.springframework.data.jdbc.mapping.event.Identifier.Unset#UNSET}
* manually, an event listener for this event may do so. <br>
* The {@link Identifier} is {@link Unset#UNSET}
*
* @author Jens Schauder
* @since 2.0

2
src/main/java/org/springframework/data/jdbc/mapping/event/BeforeSave.java

@ -15,8 +15,6 @@ @@ -15,8 +15,6 @@
*/
package org.springframework.data.jdbc.mapping.event;
import java.util.Optional;
/**
* Subclasses of this get published before an entity gets saved to the database.
*

12
src/main/java/org/springframework/data/jdbc/mapping/event/Identifier.java

@ -28,12 +28,12 @@ import java.util.Optional; @@ -28,12 +28,12 @@ import java.util.Optional;
*/
public interface Identifier {
Optional<Object> getOptionalValue();
static Identifier fromNullable(Object value) {
return (value != null) ? new Specified(value) : Unset.UNSET;
}
Optional<Object> getOptionalValue();
/**
* An unset identifier. Always returns {@link Optional#empty()} as value.
*/
@ -47,15 +47,13 @@ public interface Identifier { @@ -47,15 +47,13 @@ public interface Identifier {
}
/**
* An {@link Identifier} guaranteed to have a non empty value.
*
* Since it is guaranteed to exist the value can get access directly.
* An {@link Identifier} guaranteed to have a non empty value. Since it is guaranteed to exist the value can get
* access directly.
*/
@Data
class Specified implements Identifier {
@NonNull
private final Object value;
@NonNull private final Object value;
@Override
public Optional<Object> getOptionalValue() {

21
src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEvent.java

@ -15,15 +15,15 @@ @@ -15,15 +15,15 @@
*/
package org.springframework.data.jdbc.mapping.event;
import lombok.Getter;
import java.util.Optional;
import org.springframework.context.ApplicationEvent;
import lombok.Getter;
/**
* The common superclass for all events published by JDBC repositories.
* {@link #getSource} contains the {@link Identifier} of the entity triggering the event.
* The common superclass for all events published by JDBC repositories. {@link #getSource} contains the
* {@link Identifier} of the entity triggering the event.
*
* @author Jens Schauder
* @since 2.0
@ -32,23 +32,20 @@ import lombok.Getter; @@ -32,23 +32,20 @@ import lombok.Getter;
public class JdbcEvent extends ApplicationEvent {
/**
* The optional entity for which this event was published. Might be empty in cases of delete events where only the identifier
* was provided to the delete method.
*
* @return The entity triggering this event or empty.
* The optional entity for which this event was published. Might be empty in cases of delete events where only the
* identifier was passed to the delete method.
*/
private final Optional<Object> optionalEntity;
public JdbcEvent(Identifier id, Optional<Object> optionalEntity) {
JdbcEvent(Identifier id, Optional<Object> optionalEntity) {
super(id);
this.optionalEntity = optionalEntity;
}
/**
* The identifier of the entity, triggering this event. Also available via
* {@link #getSource()}.
* The identifier of the entity, triggering this event. Also available via {@link #getSource()}.
*
* @return
* @return the source of the event as an {@link Identifier}.
*/
public Identifier getId() {
return (Identifier) getSource();

2
src/main/java/org/springframework/data/jdbc/mapping/event/JdbcEventWithId.java

@ -25,7 +25,7 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified; @@ -25,7 +25,7 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
* @author Jens Schauder
* @since 2.0
*/
public class JdbcEventWithId extends JdbcEvent implements WithId{
public class JdbcEventWithId extends JdbcEvent implements WithId {
public JdbcEventWithId(Specified id, Optional<Object> entity) {
super(id, entity);

3
src/main/java/org/springframework/data/jdbc/mapping/event/WithEntity.java

@ -16,7 +16,8 @@ @@ -16,7 +16,8 @@
package org.springframework.data.jdbc.mapping.event;
/**
* Interface for {@link JdbcEvent}s which are guaranteed to have an entity. Allows direct access to that entity, without going through an {@link java.util.Optional}
* Interface for {@link JdbcEvent}s which are guaranteed to have an entity. Allows direct access to that entity, without
* going through an {@link java.util.Optional}
*
* @author Jens Schauder
* @since 2.0

9
src/main/java/org/springframework/data/jdbc/mapping/event/WithId.java

@ -18,16 +18,15 @@ package org.springframework.data.jdbc.mapping.event; @@ -18,16 +18,15 @@ package org.springframework.data.jdbc.mapping.event;
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
/**
* Interface for {@link JdbcEvent}s which are guaranteed to have a {@link Specified} identifier.
*
* Offers direct access to the {@link Specified} identifier.
* Interface for {@link JdbcEvent}s which are guaranteed to have a {@link Specified} identifier. Offers direct access to
* the {@link Specified} identifier.
*
* @author Jens Schauder
* @since 2.0
*/
public interface WithId {
default Specified getSpecifiedId(){
return (Specified) ((JdbcEvent)this).getId();
default Specified getSpecifiedId() {
return (Specified) ((JdbcEvent) this).getId();
}
}

2
src/main/java/org/springframework/data/jdbc/mapping/model/JdbcPersistentEntity.java

@ -19,7 +19,7 @@ import org.springframework.data.mapping.model.BasicPersistentEntity; @@ -19,7 +19,7 @@ import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
/**
* meta data a repository might need for implementing persistence operations for instances of type {@code T}
* Meta data a repository might need for implementing persistence operations for instances of type {@code T}
*
* @author Jens Schauder
* @since 2.0

17
src/main/java/org/springframework/data/jdbc/repository/EntityRowMapper.java

@ -18,6 +18,8 @@ package org.springframework.data.jdbc.repository; @@ -18,6 +18,8 @@ package org.springframework.data.jdbc.repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.convert.ClassGeneratingEntityInstantiator;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
@ -30,7 +32,7 @@ import org.springframework.data.mapping.model.ParameterValueProvider; @@ -30,7 +32,7 @@ import org.springframework.data.mapping.model.ParameterValueProvider;
import org.springframework.jdbc.core.RowMapper;
/**
* maps a ResultSet to an entity of type {@code T}
* Maps a ResultSet to an entity of type {@code T}
*
* @author Jens Schauder
* @since 2.0
@ -38,8 +40,8 @@ import org.springframework.jdbc.core.RowMapper; @@ -38,8 +40,8 @@ import org.springframework.jdbc.core.RowMapper;
class EntityRowMapper<T> implements RowMapper<T> {
private final JdbcPersistentEntity<T> entity;
private final EntityInstantiator instantiator = new ClassGeneratingEntityInstantiator();
private final ConversionService conversions = new DefaultConversionService();
EntityRowMapper(JdbcPersistentEntity<T> entity) {
this.entity = entity;
@ -58,13 +60,16 @@ class EntityRowMapper<T> implements RowMapper<T> { @@ -58,13 +60,16 @@ class EntityRowMapper<T> implements RowMapper<T> {
}
private T createInstance(ResultSet rs) {
return instantiator.createInstance(entity, new ParameterValueProvider<JdbcPersistentProperty>() {
@SuppressWarnings("unchecked")
@Override
public <T> T getParameterValue(PreferredConstructor.Parameter<T, JdbcPersistentProperty> parameter) {
try {
return (T) rs.getObject(parameter.getName());
return conversions.convert(rs.getObject(parameter.getName()), parameter.getType().getType());
} catch (SQLException e) {
throw new MappingException( //
String.format("Couldn't read column %s from ResultSet.", parameter.getName()) //
);
@ -76,7 +81,9 @@ class EntityRowMapper<T> implements RowMapper<T> { @@ -76,7 +81,9 @@ class EntityRowMapper<T> implements RowMapper<T> {
private void setProperty(ResultSet rs, T t, PersistentProperty property) {
try {
entity.getPropertyAccessor(t).setProperty(property, rs.getObject(property.getName()));
Object converted = conversions.convert(rs.getObject(property.getName()), property.getType());
entity.getPropertyAccessor(t).setProperty(property, converted);
} catch (Exception e) {
throw new RuntimeException(String.format("Couldn't set property %s.", property.getName()), e);
}

7
src/main/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapper.java

@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
*/
package org.springframework.data.jdbc.repository;
import lombok.RequiredArgsConstructor;
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
@ -25,10 +27,9 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified; @@ -25,10 +27,9 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
import org.springframework.data.jdbc.repository.support.JdbcPersistentEntityInformation;
import org.springframework.jdbc.core.RowMapper;
import lombok.RequiredArgsConstructor;
/**
* a RowMapper that publishes events after a delegate, did the actual work of mapping a {@link ResultSet} to an entityInformation.
* A {@link RowMapper} that publishes events after a delegate, did the actual work of mapping a {@link ResultSet} to an
* entityInformation.
*
* @author Jens Schauder
* @since 2.0

62
src/main/java/org/springframework/data/jdbc/repository/SimpleJdbcRepository.java

@ -25,6 +25,9 @@ import java.util.stream.Collectors; @@ -25,6 +25,9 @@ import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.NonTransientDataAccessException;
import org.springframework.data.jdbc.mapping.event.AfterDelete;
import org.springframework.data.jdbc.mapping.event.AfterInsert;
@ -59,12 +62,15 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep @@ -59,12 +62,15 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
private final JdbcPersistentEntityInformation<T, ID> entityInformation;
private final NamedParameterJdbcOperations operations;
private final SqlGenerator sql;
private final EntityRowMapper<T> entityRowMapper;
private final ApplicationEventPublisher publisher;
private final ConversionService conversions = new DefaultConversionService();
public SimpleJdbcRepository(JdbcPersistentEntity<T> persistentEntity, NamedParameterJdbcOperations jdbcOperations,
ApplicationEventPublisher publisher) {
public SimpleJdbcRepository( //
JdbcPersistentEntity<T> persistentEntity, //
NamedParameterJdbcOperations jdbcOperations, //
ApplicationEventPublisher publisher //
) {
Assert.notNull(persistentEntity, "PersistentEntity must not be null.");
Assert.notNull(jdbcOperations, "JdbcOperations must not be null.");
@ -75,8 +81,8 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep @@ -75,8 +81,8 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
this.operations = jdbcOperations;
this.publisher = publisher;
entityRowMapper = new EntityRowMapper<>(persistentEntity);
sql = new SqlGenerator(persistentEntity);
this.entityRowMapper = new EntityRowMapper<>(persistentEntity);
this.sql = new SqlGenerator(persistentEntity);
}
@Override
@ -95,9 +101,7 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep @@ -95,9 +101,7 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
public <S extends T> Iterable<S> save(Iterable<S> entities) {
List<S> savedEntities = new ArrayList<>();
entities.forEach(e -> savedEntities.add(save(e)));
return savedEntities;
}
@ -141,8 +145,12 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep @@ -141,8 +145,12 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
@Override
public void delete(Iterable<? extends T> entities) {
operations.update(sql.getDeleteByList(), new MapSqlParameterSource("ids", StreamSupport
.stream(entities.spliterator(), false).map(entityInformation::getId).collect(Collectors.toList())));
List<ID> idList = StreamSupport.stream(entities.spliterator(), false) //
.map(entityInformation::getId) //
.collect(Collectors.toList());
MapSqlParameterSource sqlParameterSource = new MapSqlParameterSource("ids", idList);
operations.update(sql.getDeleteByList(), sqlParameterSource);
}
@Override
@ -154,11 +162,11 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep @@ -154,11 +162,11 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
Map<String, Object> parameters = new HashMap<>();
this.persistentEntity.doWithProperties((PropertyHandler<JdbcPersistentProperty>) //
property -> parameters.put( //
property.getColumnName(), //
persistentEntity.getPropertyAccessor(instance).getProperty(property)) //
);
this.persistentEntity.doWithProperties((PropertyHandler<JdbcPersistentProperty>) property -> {
Object value = persistentEntity.getPropertyAccessor(instance).getProperty(property);
parameters.put(property.getColumnName(), value);
});
return parameters;
}
@ -195,16 +203,38 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep @@ -195,16 +203,38 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
}
private <S extends T> void setIdFromJdbc(S instance, KeyHolder holder) {
try {
Number idValueFromJdbc = holder.getKey();
Object idValueFromJdbc = getIdFromHolder(holder);
if (idValueFromJdbc != null) {
entityInformation.setId(instance, idValueFromJdbc);
Class<?> targetType = persistentEntity.getIdProperty().getType();
Object converted = convert(idValueFromJdbc, targetType);
entityInformation.setId(instance, converted);
}
} catch (NonTransientDataAccessException e) {
throw new UnableToSetIdException("Unable to set id of " + instance, e);
}
}
private Object getIdFromHolder(KeyHolder holder) {
try {
// mysql just returns one value with a special name
return holder.getKey();
} catch (InvalidDataAccessApiUsageException e) {
// postgress returns a value for each column
return holder.getKeys().get(persistentEntity.getIdColumn());
}
}
private <V> V convert(Object idValueFromJdbc, Class<V> targetType) {
return conversions.convert(idValueFromJdbc, targetType);
}
private void doDelete(Specified specifiedId, Optional<Object> optionalEntity) {
publisher.publishEvent(new BeforeDelete(specifiedId, optionalEntity));

78
src/main/java/org/springframework/data/jdbc/repository/SqlGenerator.java

@ -17,12 +17,15 @@ package org.springframework.data.jdbc.repository; @@ -17,12 +17,15 @@ package org.springframework.data.jdbc.repository;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
import org.springframework.data.mapping.PropertyHandler;
/**
* Generates SQL statements to be used by {@Link SimpleJdbcRepository}
*
* @author Jens Schauder
* @since 2.0
*/
@ -36,29 +39,45 @@ class SqlGenerator { @@ -36,29 +39,45 @@ class SqlGenerator {
private final String countSql;
private final String insertSql;
private final String updateSql;
private final String deleteByIdSql;
private final String deleteAllSql;
private final String deleteByListSql;
private final String updateSql;
private final JdbcPersistentEntity<?> entity;
private final List<String> propertyNames = new ArrayList<>();
private final List<String> nonIdPropertyNames = new ArrayList<>();
SqlGenerator(JdbcPersistentEntity<?> entity) {
this.entity = entity;
initPropertyNames();
<T> SqlGenerator(JdbcPersistentEntity<T> entity) {
findOneSql = createFindOneSelectSql();
findAllSql = createFindAllSql();
findAllInListSql = createFindAllInListSql();
entity.doWithProperties((PropertyHandler) persistentProperty -> propertyNames.add(persistentProperty.getName()));
existsSql = createExistsSql();
countSql = createCountSql();
findOneSql = createFindOneSelectSql(entity);
findAllSql = createFindAllSql(entity);
findAllInListSql = createFindAllInListSql(entity);
insertSql = createInsertSql();
existsSql = createExistsSql(entity);
countSql = createCountSql(entity);
updateSql = createUpdateSql();
insertSql = createInsertSql(entity);
updateSql = createUpdateSql(entity);
deleteByIdSql = createDeleteSql();
deleteAllSql = createDeleteAllSql();
deleteByListSql = createDeleteByListSql();
}
deleteByIdSql = createDeleteSql(entity);
deleteAllSql = createDeleteAllSql(entity);
deleteByListSql = createDeleteByListSql(entity);
private <T> void initPropertyNames() {
entity.doWithProperties((PropertyHandler<JdbcPersistentProperty>) p -> {
propertyNames.add(p.getName());
if (!entity.isIdProperty(p)) {
nonIdPropertyNames.add(p.getName());
}
});
}
String getFindAllInList() {
@ -100,55 +119,56 @@ class SqlGenerator { @@ -100,55 +119,56 @@ class SqlGenerator {
String getDeleteByList() {
return deleteByListSql;
}
private String createFindOneSelectSql(JdbcPersistentEntity<?> entity) {
private String createFindOneSelectSql() {
return String.format("select * from %s where %s = :id", entity.getTableName(), entity.getIdColumn());
}
private String createFindAllSql(JdbcPersistentEntity<?> entity) {
private String createFindAllSql() {
return String.format("select * from %s", entity.getTableName());
}
private String createFindAllInListSql(JdbcPersistentEntity<?> entity) {
return String.format(String.format("select * from %s where %s in (:ids)", entity.getTableName(), entity.getIdColumn()), entity.getTableName());
private String createFindAllInListSql() {
return String.format("select * from %s where %s in (:ids)", entity.getTableName(), entity.getIdColumn());
}
private String createExistsSql(JdbcPersistentEntity<?> entity) {
private String createExistsSql() {
return String.format("select count(*) from %s where %s = :id", entity.getTableName(), entity.getIdColumn());
}
private <T> String createCountSql(JdbcPersistentEntity<T> entity) {
private <T> String createCountSql() {
return String.format("select count(*) from %s", entity.getTableName());
}
private String createInsertSql(JdbcPersistentEntity<?> entity) {
private String createInsertSql() {
String insertTemplate = "insert into %s (%s) values (%s)";
String tableColumns = propertyNames.stream().collect(Collectors.joining(", "));
String parameterNames = propertyNames.stream().collect(Collectors.joining(", :", ":", ""));
String tableColumns = nonIdPropertyNames.stream().collect(Collectors.joining(", "));
String parameterNames = nonIdPropertyNames.stream().collect(Collectors.joining(", :", ":", ""));
return String.format(insertTemplate, entity.getTableName(), tableColumns, parameterNames);
}
private <T> String createUpdateSql(JdbcPersistentEntity<T> entity) {
private <T> String createUpdateSql() {
String updateTemplate = "update %s set %s where %s = :%s";
String setClause = propertyNames.stream().map(n -> String.format("%s = :%s", n, n)).collect(Collectors.joining(", "));
String setClause = propertyNames.stream().map(n -> String.format("%s = :%s", n, n))
.collect(Collectors.joining(", "));
return String.format(updateTemplate, entity.getTableName(), setClause, entity.getIdColumn(), entity.getIdColumn());
}
private String createDeleteSql(JdbcPersistentEntity entity) {
private String createDeleteSql() {
return String.format("delete from %s where %s = :id", entity.getTableName(), entity.getIdColumn());
}
private String createDeleteAllSql(JdbcPersistentEntity entity) {
private String createDeleteAllSql() {
return String.format("delete from %s", entity.getTableName());
}
private String createDeleteByListSql(JdbcPersistentEntity entity) {
private String createDeleteByListSql() {
return String.format("delete from %s where %s in (:ids)", entity.getTableName(), entity.getIdColumn());
}
}

6
src/test/java/org/springframework/data/jdbc/repository/EventPublishingEntityRowMapperTest.java

@ -19,16 +19,17 @@ import org.springframework.jdbc.core.RowMapper; @@ -19,16 +19,17 @@ import org.springframework.jdbc.core.RowMapper;
*/
public class EventPublishingEntityRowMapperTest {
RowMapper rowMapperDelegate = mock(RowMapper.class);
RowMapper<DummyEntity> rowMapperDelegate = mock(RowMapper.class);
JdbcPersistentEntityInformation<DummyEntity, Long> entityInformation = mock(JdbcPersistentEntityInformation.class);
ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
@Test // DATAJDBC-99
public void eventGetsPublishedAfterInstantiation() throws SQLException {
when(rowMapperDelegate.mapRow(any(ResultSet.class), anyInt())).thenReturn(new DummyEntity(1L));
when(entityInformation.getId(any())).thenReturn(1L);
EventPublishingEntityRowMapper<DummyEntity, Long> rowMapper = new EventPublishingEntityRowMapper<>( //
EventPublishingEntityRowMapper rowMapper = new EventPublishingEntityRowMapper<>( //
rowMapperDelegate, //
entityInformation, //
publisher);
@ -41,7 +42,6 @@ public class EventPublishingEntityRowMapperTest { @@ -41,7 +42,6 @@ public class EventPublishingEntityRowMapperTest {
@Data
static class DummyEntity {
@Id private final Long Id;
}
}

49
src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIdGenerationIntegrationTests.java

@ -21,44 +21,45 @@ import static org.mockito.Mockito.*; @@ -21,44 +21,45 @@ import static org.mockito.Mockito.*;
import lombok.Data;
import javax.sql.DataSource;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.repository.JdbcRepositoryIdGenerationIntegrationTests.TestConfiguration;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
/**
* Testing special cases for id generation with {@link SimpleJdbcRepository}.
*
* @author Jens Schauder
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
public class JdbcRepositoryIdGenerationIntegrationTests {
@Autowired NamedParameterJdbcTemplate template;
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
@Autowired NamedParameterJdbcTemplate template;
@Autowired ReadOnlyIdEntityRepository readOnlyIdrepository;
@Autowired PrimitiveIdEntityRepository primitiveIdRepository;
@Test // DATAJDBC-98
public void idWithoutSetterGetsSet() {
ReadOnlyIdEntity entity1 = new ReadOnlyIdEntity(null);
entity1.setName("Entity Name");
ReadOnlyIdEntity entity = entity1;
ReadOnlyIdEntity entity = new ReadOnlyIdEntity(null);
entity.setName("Entity Name");
entity = readOnlyIdrepository.save(entity);
assertThat(entity.getId()).isNotNull();
@ -77,6 +78,7 @@ public class JdbcRepositoryIdGenerationIntegrationTests { @@ -77,6 +78,7 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
entity = primitiveIdRepository.save(entity);
assertThat(entity.getId()).isNotNull();
assertThat(entity.getId()).isNotEqualTo(0L);
PrimitiveIdEntity reloadedEntity = primitiveIdRepository.findOne(entity.getId());
@ -84,9 +86,9 @@ public class JdbcRepositoryIdGenerationIntegrationTests { @@ -84,9 +86,9 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
assertEquals(entity.getName(), reloadedEntity.getName());
}
private interface ReadOnlyIdEntityRepository extends CrudRepository<ReadOnlyIdEntity, Long> {
private interface ReadOnlyIdEntityRepository extends CrudRepository<ReadOnlyIdEntity, Long> {}
}
private interface PrimitiveIdEntityRepository extends CrudRepository<PrimitiveIdEntity, Long> {}
@Data
static class ReadOnlyIdEntity {
@ -95,10 +97,6 @@ public class JdbcRepositoryIdGenerationIntegrationTests { @@ -95,10 +97,6 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
String name;
}
private interface PrimitiveIdEntityRepository extends CrudRepository<PrimitiveIdEntity, Long> {
}
@Data
static class PrimitiveIdEntity {
@ -107,28 +105,21 @@ public class JdbcRepositoryIdGenerationIntegrationTests { @@ -107,28 +105,21 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
}
@Configuration
@ComponentScan("org.springframework.data.jdbc.testing")
static class TestConfiguration {
@Bean
EmbeddedDatabase dataSource() {
System.out.println(" creating datasource");
return new EmbeddedDatabaseBuilder() //
.generateUniqueName(true) //
.setType(EmbeddedDatabaseType.HSQL) //
.setScriptEncoding("UTF-8") //
.ignoreFailedDrops(true) //
.addScript("org.springframework.data.jdbc.repository/jdbc-repository-id-generation-integration-tests.sql")
.build();
Class<?> testClass() {
return JdbcRepositoryIdGenerationIntegrationTests.class;
}
@Bean
NamedParameterJdbcTemplate template(EmbeddedDatabase db) {
NamedParameterJdbcTemplate template(DataSource db) {
return new NamedParameterJdbcTemplate(db);
}
@Bean
ReadOnlyIdEntityRepository readOnlyIdRepository(EmbeddedDatabase db) {
ReadOnlyIdEntityRepository readOnlyIdRepository(DataSource db) {
return new JdbcRepositoryFactory(new NamedParameterJdbcTemplate(db), mock(ApplicationEventPublisher.class))
.getRepository(ReadOnlyIdEntityRepository.class);

96
src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java

@ -24,24 +24,26 @@ import lombok.Data; @@ -24,24 +24,26 @@ import lombok.Data;
import javax.sql.DataSource;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.jdbc.repository.JdbcRepositoryIntegrationTests.TestConfiguration;
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
import org.springframework.data.repository.CrudRepository;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabase;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
import org.springframework.test.jdbc.JdbcTestUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.Transactional;
@ -50,26 +52,40 @@ import org.springframework.transaction.annotation.Transactional; @@ -50,26 +52,40 @@ import org.springframework.transaction.annotation.Transactional;
*
* @author Jens Schauder
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
@Transactional
public class JdbcRepositoryIntegrationTests {
@Autowired NamedParameterJdbcTemplate template;
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
@Autowired NamedParameterJdbcTemplate template;
@Autowired DummyEntityRepository repository;
private DummyEntity entity = createDummyEntity();
private static DummyEntityRepository createRepository(EmbeddedDatabase db) {
return new JdbcRepositoryFactory(new NamedParameterJdbcTemplate(db), mock(ApplicationEventPublisher.class))
.getRepository(DummyEntityRepository.class);
}
private static DummyEntity createDummyEntity() {
DummyEntity entity = new DummyEntity();
entity.setName("Entity Name");
return entity;
}
@Test // DATAJDBC-95
public void savesAnEntity() {
entity = repository.save(entity);
int count = template.queryForObject( //
"SELECT count(*) FROM dummyentity WHERE idProp = :id", //
new MapSqlParameterSource("id", entity.getIdProp()), //
Integer.class //
int count = JdbcTestUtils.countRowsInTableWhere( //
(JdbcTemplate) template.getJdbcOperations(), //
"dummyentity", //
"idProp = " + entity.getIdProp() //
);
assertEquals(1, count);
@ -95,9 +111,7 @@ public class JdbcRepositoryIntegrationTests { @@ -95,9 +111,7 @@ public class JdbcRepositoryIntegrationTests {
assertThat(repository.findAll()) //
.extracting(DummyEntity::getIdProp) //
.containsExactlyInAnyOrder( //
entity.getIdProp(), other.getIdProp() //
);
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
}
@Test // DATAJDBC-97
@ -125,9 +139,9 @@ public class JdbcRepositoryIntegrationTests { @@ -125,9 +139,9 @@ public class JdbcRepositoryIntegrationTests {
@Test // DATAJDBC-97
public void findAllFindsAllSpecifiedEntities() {
entity = repository.save(entity);
DummyEntity two = repository.save(createDummyEntity());
DummyEntity three = repository.save(createDummyEntity());
entity = repository.save(entity);
Iterable<DummyEntity> all = repository.findAll(asList(entity.getIdProp(), three.getIdProp()));
@ -155,9 +169,7 @@ public class JdbcRepositoryIntegrationTests { @@ -155,9 +169,7 @@ public class JdbcRepositoryIntegrationTests {
assertThat(repository.findAll()) //
.extracting(DummyEntity::getIdProp) //
.containsExactlyInAnyOrder( //
entity.getIdProp(), three.getIdProp() //
);
.containsExactlyInAnyOrder(entity.getIdProp(), three.getIdProp());
}
@Test // DATAJDBC-97
@ -171,9 +183,7 @@ public class JdbcRepositoryIntegrationTests { @@ -171,9 +183,7 @@ public class JdbcRepositoryIntegrationTests {
assertThat(repository.findAll()) //
.extracting(DummyEntity::getIdProp) //
.containsExactlyInAnyOrder( //
two.getIdProp(), three.getIdProp() //
);
.containsExactlyInAnyOrder(two.getIdProp(), three.getIdProp());
}
@Test // DATAJDBC-97
@ -185,7 +195,9 @@ public class JdbcRepositoryIntegrationTests { @@ -185,7 +195,9 @@ public class JdbcRepositoryIntegrationTests {
repository.delete(asList(entity, three));
assertThat(repository.findAll()).extracting(DummyEntity::getIdProp).containsExactlyInAnyOrder(two.getIdProp());
assertThat(repository.findAll()) //
.extracting(DummyEntity::getIdProp) //
.containsExactlyInAnyOrder(two.getIdProp());
}
@Test // DATAJDBC-97
@ -225,52 +237,31 @@ public class JdbcRepositoryIntegrationTests { @@ -225,52 +237,31 @@ public class JdbcRepositoryIntegrationTests {
repository.save(asList(entity, other));
assertThat(repository.findAll()).extracting(DummyEntity::getName).containsExactlyInAnyOrder(entity.getName(),
other.getName());
}
private static DummyEntityRepository createRepository(EmbeddedDatabase db) {
return new JdbcRepositoryFactory(new NamedParameterJdbcTemplate(db), mock(ApplicationEventPublisher.class))
.getRepository(DummyEntityRepository.class);
}
private static DummyEntity createDummyEntity() {
DummyEntity entity = new DummyEntity();
entity.setName("Entity Name");
return entity;
assertThat(repository.findAll()) //
.extracting(DummyEntity::getName) //
.containsExactlyInAnyOrder(entity.getName(), other.getName());
}
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
}
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
@Data
static class DummyEntity {
@Id private Long idProp;
String name;
@Id private Long idProp;
}
@Configuration
@ComponentScan("org.springframework.data.jdbc.testing")
static class TestConfiguration {
@Bean
EmbeddedDatabase dataSource() {
System.out.println(" creating datasource");
return new EmbeddedDatabaseBuilder() //
.generateUniqueName(true) //
.setType(EmbeddedDatabaseType.HSQL) //
.setScriptEncoding("UTF-8") //
.ignoreFailedDrops(true) //
.addScript("org.springframework.data.jdbc.repository/jdbc-repository-integration-tests.sql") //
.build();
Class<?> testClass() {
return JdbcRepositoryIntegrationTests.class;
}
@Bean
NamedParameterJdbcTemplate template(EmbeddedDatabase db) {
NamedParameterJdbcTemplate template(DataSource db) {
return new NamedParameterJdbcTemplate(db);
}
@ -285,6 +276,5 @@ public class JdbcRepositoryIntegrationTests { @@ -285,6 +276,5 @@ public class JdbcRepositoryIntegrationTests {
PlatformTransactionManager transactionManager(DataSource db) {
return new DataSourceTransactionManager(db);
}
}
}

42
src/test/java/org/springframework/data/jdbc/repository/SimpleJdbcRepositoryEventsUnitTests.java

@ -1,17 +1,18 @@ @@ -1,17 +1,18 @@
package org.springframework.data.jdbc.repository;
import static java.util.Arrays.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.util.Assert.*;
import lombok.Data;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.annotation.Id;
@ -29,8 +30,6 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; @@ -29,8 +30,6 @@ import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.support.KeyHolder;
import lombok.Data;
/**
* @author Jens Schauder
*/
@ -42,22 +41,27 @@ public class SimpleJdbcRepositoryEventsUnitTests { @@ -42,22 +41,27 @@ public class SimpleJdbcRepositoryEventsUnitTests {
@Before
public void before() {
NamedParameterJdbcOperations operations = createIdGeneratingOperations();
JdbcRepositoryFactory factory = new JdbcRepositoryFactory(operations, publisher);
repository = factory.getRepository(DummyEntityRepository.class);
}
private NamedParameterJdbcOperations createIdGeneratingOperations() {
Answer<Integer> setIdInKeyHolder = invocation -> {
HashMap<String, Object> keys = new HashMap<>();
keys.put("id", 4711L);
KeyHolder keyHolder = invocation.getArgumentAt(2, KeyHolder.class);
keyHolder.getKeyList().add(keys);
return 1;
};
NamedParameterJdbcOperations operations = mock(NamedParameterJdbcOperations.class);
when(operations.update(anyString(), any(SqlParameterSource.class), any(KeyHolder.class))).thenAnswer(new Answer<Integer>() {
@Override
public Integer answer(InvocationOnMock invocation) throws Throwable {
HashMap<String, Object> keys = new HashMap<>();
keys.put("id", 4711L);
invocation.getArgumentAt(2, KeyHolder.class).getKeyList().add(keys);
return 1;
}
});
when(operations.update(anyString(), any(SqlParameterSource.class), any(KeyHolder.class)))
.thenAnswer(setIdInKeyHolder);
return operations;
}
@ -75,8 +79,6 @@ public class SimpleJdbcRepositoryEventsUnitTests { @@ -75,8 +79,6 @@ public class SimpleJdbcRepositoryEventsUnitTests {
@Test // DATAJDBC-99
public void publishesEventsOnSaveMany() {
DummyEntity entity1 = new DummyEntity(null);
DummyEntity entity2 = new DummyEntity(23L);
@ -88,7 +90,6 @@ public class SimpleJdbcRepositoryEventsUnitTests { @@ -88,7 +90,6 @@ public class SimpleJdbcRepositoryEventsUnitTests {
isInstanceOf(AfterUpdate.class, publisher.events.get(3));
}
@Test // DATAJDBC-99
public void publishesEventsOnDelete() {
@ -115,16 +116,13 @@ public class SimpleJdbcRepositoryEventsUnitTests { @@ -115,16 +116,13 @@ public class SimpleJdbcRepositoryEventsUnitTests {
isInstanceOf(AfterDelete.class, publisher.events.get(1));
}
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
@Data
private static class DummyEntity {
@Id private final Long id;
}
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {
}
static class FakePublisher implements ApplicationEventPublisher {
List<JdbcEvent> events = new ArrayList<>();
@ -134,4 +132,4 @@ public class SimpleJdbcRepositoryEventsUnitTests { @@ -134,4 +132,4 @@ public class SimpleJdbcRepositoryEventsUnitTests {
events.add((JdbcEvent) o);
}
}
}
}

59
src/test/java/org/springframework/data/jdbc/testing/DataSourceFactoryBean.java

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.testing;
import javax.sql.DataSource;
import org.springframework.beans.factory.FactoryBean;
/**
* @author Jens Schauder
*/
abstract class DataSourceFactoryBean implements FactoryBean<DataSource> {
private final Class<?> testClass;
DataSourceFactoryBean(Class<?> testClass) {
this.testClass = testClass;
}
private DataSource createDataSource() {
return create(createScriptName(testClass, scriptSuffix()));
}
abstract String scriptSuffix();
abstract DataSource create(String scriptName);
private String createScriptName(Class<?> testClass, String databaseType) {
return String.format( //
"%s/%s-%s.sql", //
testClass.getPackage().getName(), //
testClass.getSimpleName(), //
databaseType.toLowerCase());
}
@Override
public DataSource getObject() throws Exception {
return createDataSource();
}
@Override
public Class<?> getObjectType() {
return DataSource.class;
}
}

52
src/test/java/org/springframework/data/jdbc/testing/HsqlDataSourceFactoryBean.java

@ -0,0 +1,52 @@ @@ -0,0 +1,52 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.testing;
import javax.sql.DataSource;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;
import org.springframework.stereotype.Component;
/**
* @author Jens Schauder
*/
@Component
@Profile({ "hsql", "default" })
class HsqlDataSourceFactoryBean extends DataSourceFactoryBean {
HsqlDataSourceFactoryBean(Class<?> testClass) {
super(testClass);
}
@Override
String scriptSuffix() {
return "hsql";
}
@Override
DataSource create(String scriptName) {
return new EmbeddedDatabaseBuilder() //
.generateUniqueName(true) //
.setType(EmbeddedDatabaseType.HSQL) //
.setScriptEncoding("UTF-8") //
.ignoreFailedDrops(true) //
.addScript(scriptName) //
.build();
}
}

84
src/test/java/org/springframework/data/jdbc/testing/MySqlDataSourceFactoryBean.java

@ -0,0 +1,84 @@ @@ -0,0 +1,84 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.testing;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.stereotype.Component;
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
/**
* @author Jens Schauder
*/
@Component
@Profile("mysql")
class MySqlDataSourceFactoryBean extends DataSourceFactoryBean {
public static final String ROOT_URL = "jdbc:mysql:///?user=root";
public static final String COULDN_T_CREATE_DATABASE = //
"Couldn't create database. Maybe you don't have a MySql database running, reachable at %s";
private static final DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
private static final String TEST_URL = "jdbc:mysql:///test?user=root";
MySqlDataSourceFactoryBean(Class<?> testClass) {
super(testClass);
}
@Override
String scriptSuffix() {
return "mysql";
}
@Override
DataSource create(String scriptName) {
createDatabase();
return setupDatabase(scriptName);
}
private MysqlDataSource setupDatabase(String scriptName) {
MysqlDataSource ds = new MysqlDataSource();
ds.setUrl(TEST_URL);
try (Connection connection = ds.getConnection()) {
ScriptUtils.executeSqlScript(connection, resourceLoader.getResource(scriptName));
} catch (SQLException e) {
throw new RuntimeException("Couldn't setup database", e);
}
return ds;
}
private void createDatabase() {
MysqlDataSource initialDs = new MysqlDataSource();
initialDs.setUrl(ROOT_URL);
try (Connection connection = initialDs.getConnection()) {
ScriptUtils.executeSqlScript(connection, resourceLoader.getResource("create-mysql.sql"));
} catch (SQLException e) {
throw new RuntimeException(String.format(COULDN_T_CREATE_DATABASE, ROOT_URL), e);
}
}
}

69
src/test/java/org/springframework/data/jdbc/testing/PostgresDataSourceFactoryBean.java

@ -0,0 +1,69 @@ @@ -0,0 +1,69 @@
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.testing;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.postgresql.ds.PGSimpleDataSource;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.stereotype.Component;
/**
* @author Jens Schauder
*/
@Component
@Profile("postgres")
public class PostgresDataSourceFactoryBean extends DataSourceFactoryBean {
public static final String URL = "jdbc:postgresql:///postgres";
private static final DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
PostgresDataSourceFactoryBean(Class<?> testClass) {
super(testClass);
}
@Override
protected String scriptSuffix() {
return "postgres";
}
@Override
DataSource create(String scriptName) {
return setupDatabase(scriptName);
}
private DataSource setupDatabase(String scriptName) {
PGSimpleDataSource ds = new PGSimpleDataSource();
ds.setUrl(URL);
try (Connection connection = ds.getConnection()) {
ScriptUtils.executeSqlScript(connection, new EncodedResource(resourceLoader.getResource(scriptName)), false, true,
"--", ";", "/*", "*/");
} catch (SQLException e) {
throw new RuntimeException("Couldn't setup database", e);
}
return ds;
}
}

2
src/test/resources/create-mysql.sql

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
DROP DATABASE test;
CREATE DATABASE test;

1
src/test/resources/org.springframework.data.jdbc.repository/jdbc-repository-id-generation-integration-tests.sql → src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-hsql.sql

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
-- noinspection SqlNoDataSourceInspectionForFile
CREATE TABLE ReadOnlyIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100))
CREATE TABLE PrimitiveIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100))

2
src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-mysql.sql

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
CREATE TABLE ReadOnlyIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE PrimitiveIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));

5
src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIdGenerationIntegrationTests-postgres.sql

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
DROP TABLE ReadOnlyIdEntity;
DROP TABLE PrimitiveIdEntity;
CREATE TABLE ReadOnlyIdEntity (ID SERIAL PRIMARY KEY, NAME VARCHAR(100));
CREATE TABLE PrimitiveIdEntity (ID SERIAL PRIMARY KEY, NAME VARCHAR(100));

1
src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql

@ -0,0 +1 @@ @@ -0,0 +1 @@
CREATE TABLE dummyentity ( idProp BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100))

1
src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql

@ -0,0 +1 @@ @@ -0,0 +1 @@
CREATE TABLE dummyentity (idProp BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));

2
src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql

@ -0,0 +1,2 @@ @@ -0,0 +1,2 @@
DROP TABLE dummyentity;
CREATE TABLE dummyentity (idProp SERIAL PRIMARY KEY, NAME VARCHAR(100));

1
src/test/resources/org.springframework.data.jdbc.repository/jdbc-repository-integration-tests.sql

@ -1 +0,0 @@ @@ -1 +0,0 @@
CREATE TABLE dummyentity (idProp BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100))

7
start-all-dbs.sh

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
#!/bin/sh
# postgres
pg_ctl -D /usr/local/var/postgres start
# mysql
mysql.server start

7
stop-all-dbs.sh

@ -0,0 +1,7 @@ @@ -0,0 +1,7 @@
#!/bin/sh
# postgres
pg_ctl -D /usr/local/var/postgres stop
# mysql
mysql.server stop
Loading…
Cancel
Save