DATAJDBC-99 - Polishing.
Introduced interface for JdbcPersistentEntity to be consistent with other store implementations. Introduced JdbcPersistentEntityInformation.getRequiredId(…) to return a value and throw an exception if it can't be obtained. Added serialVersionUIDs to event implementations. Extracted Unset and SpecifiedIdentifier into top level classes to be able to reduce visibility. Introduced Specified interface and factory methods on Identifier to be able to create Identifier instances in all ways needed (specified / optional). Extracted JdbcEvent interface from the previously thus named class to be able to let WithId and WithEntity extend that interface to avoid the cast in the default method of WithId. WithId now simply redeclares getId returning Specified so that the guarantees of returned object type simply adapt as soon as event types also implement that interface. Reduced the visibility of SimpleJdbcEvent as listeners could now just refer to JdbcEvent. Switched to a nullable field for the entity in SimpleJdbcEvent as ApplicationEvent already implies potential serializability and Optional is not Serializable. Reorganized integration test setup to use configuration classes over FactoryBean implementations. Switched to AssertJ for test assertions. Renamed UnableToSetIdException to UnableToSetId. A bit of Javadoc here and there. Formatting. No abbreviated variable names. Original pull request: #5.
This commit is contained in:
@@ -16,31 +16,38 @@
|
||||
package org.springframework.data.jdbc.mapping.context;
|
||||
|
||||
import org.springframework.data.jdbc.mapping.model.BasicJdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntityImpl;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.context.AbstractMappingContext;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mapping.model.Property;
|
||||
import org.springframework.data.mapping.model.SimpleTypeHolder;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
|
||||
/**
|
||||
* {@link MappingContext} implementation for JDBC.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEntity<?>, JdbcPersistentProperty> {
|
||||
public class JdbcMappingContext extends AbstractMappingContext<JdbcPersistentEntityImpl<?>, JdbcPersistentProperty> {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
|
||||
*/
|
||||
@Override
|
||||
protected <T> JdbcPersistentEntity createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
return new JdbcPersistentEntity(typeInformation);
|
||||
protected <T> JdbcPersistentEntityImpl<?> createPersistentEntity(TypeInformation<T> typeInformation) {
|
||||
return new JdbcPersistentEntityImpl<>(typeInformation);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(org.springframework.data.mapping.model.Property, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)
|
||||
*/
|
||||
@Override
|
||||
protected JdbcPersistentProperty createPersistentProperty( //
|
||||
Property property, //
|
||||
JdbcPersistentEntity<?> owner, //
|
||||
SimpleTypeHolder simpleTypeHolder //
|
||||
) {
|
||||
protected JdbcPersistentProperty createPersistentProperty(Property property, JdbcPersistentEntityImpl<?> owner,
|
||||
SimpleTypeHolder simpleTypeHolder) {
|
||||
return new BasicJdbcPersistentProperty(property, owner, simpleTypeHolder);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
*/
|
||||
public class AfterCreation extends JdbcEventWithIdAndEntity {
|
||||
|
||||
private static final long serialVersionUID = -4185777271143436728L;
|
||||
|
||||
/**
|
||||
* @param id of the entity
|
||||
* @param entity the newly instantiated entity.
|
||||
|
||||
@@ -28,6 +28,8 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
*/
|
||||
public class AfterDelete extends JdbcEventWithId {
|
||||
|
||||
private static final long serialVersionUID = 3594807189931141582L;
|
||||
|
||||
/**
|
||||
* @param id of the entity.
|
||||
* @param instance the deleted entity if it is available.
|
||||
|
||||
@@ -25,6 +25,8 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
*/
|
||||
public class AfterInsert extends AfterSave {
|
||||
|
||||
private static final long serialVersionUID = 1312645717283677063L;
|
||||
|
||||
/**
|
||||
* @param id identifier of the entity triggering the event.
|
||||
* @param instance the newly inserted entity.
|
||||
|
||||
@@ -25,6 +25,8 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
*/
|
||||
public class AfterSave extends JdbcEventWithIdAndEntity {
|
||||
|
||||
private static final long serialVersionUID = 8982085767296982848L;
|
||||
|
||||
/**
|
||||
* @param id identifier of
|
||||
* @param instance the newly saved entity.
|
||||
|
||||
@@ -25,6 +25,8 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
*/
|
||||
public class AfterUpdate extends AfterSave {
|
||||
|
||||
private static final long serialVersionUID = -1765706904721563399L;
|
||||
|
||||
/**
|
||||
* @param id of the entity
|
||||
* @param instance the updated entity.
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
*/
|
||||
public class BeforeDelete extends JdbcEventWithId {
|
||||
|
||||
private static final long serialVersionUID = -5483432053368496651L;
|
||||
|
||||
/**
|
||||
* @param id the id of the entity
|
||||
* @param entity the entity about to get deleted. Might be empty.
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.mapping.event;
|
||||
|
||||
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. <br>
|
||||
@@ -27,6 +25,8 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Unset;
|
||||
*/
|
||||
public class BeforeInsert extends BeforeSave {
|
||||
|
||||
private static final long serialVersionUID = -5350552051337308870L;
|
||||
|
||||
/**
|
||||
* @param instance the entity about to get inserted.
|
||||
*/
|
||||
|
||||
@@ -23,6 +23,8 @@ package org.springframework.data.jdbc.mapping.event;
|
||||
*/
|
||||
public class BeforeSave extends JdbcEventWithEntity {
|
||||
|
||||
private static final long serialVersionUID = -6996874391990315443L;
|
||||
|
||||
/**
|
||||
* @param id of the entity to be saved.
|
||||
* @param instance the entity about to get saved.
|
||||
|
||||
@@ -25,11 +25,27 @@ import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
*/
|
||||
public class BeforeUpdate extends BeforeSave implements WithId {
|
||||
|
||||
private static final long serialVersionUID = 794561215071613972L;
|
||||
|
||||
private final Specified id;
|
||||
|
||||
/**
|
||||
* @param id of the entity about to get updated
|
||||
* @param instance the entity about to get updated.
|
||||
*/
|
||||
public BeforeUpdate(Specified id, Object instance) {
|
||||
|
||||
super(id, instance);
|
||||
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.mapping.event.JdbcEvent#getId()
|
||||
*/
|
||||
@Override
|
||||
public Specified getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,10 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.mapping.event;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Wrapper for an identifier of an entity. Might either be a {@link Specified} or {@link Unset#UNSET}
|
||||
*
|
||||
@@ -28,36 +27,53 @@ import java.util.Optional;
|
||||
*/
|
||||
public interface Identifier {
|
||||
|
||||
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.
|
||||
* Creates a new {@link Specified} identifier for the given, non-null value.
|
||||
*
|
||||
* @param identifier must not be {@literal null}.
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
enum Unset implements Identifier {
|
||||
UNSET {
|
||||
@Override
|
||||
public Optional<Object> getOptionalValue() {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
static Specified of(Object identifier) {
|
||||
|
||||
Assert.notNull(identifier, "Identifier must not be null!");
|
||||
|
||||
return SpecifiedIdentifier.of(identifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* An {@link Identifier} guaranteed to have a non empty value. Since it is guaranteed to exist the value can get
|
||||
* access directly.
|
||||
* Creates a new {@link Identifier} for the given optional source value.
|
||||
*
|
||||
* @param identifier must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@Data
|
||||
class Specified implements Identifier {
|
||||
static Identifier of(Optional<? extends Object> identifier) {
|
||||
|
||||
@NonNull private final Object value;
|
||||
Assert.notNull(identifier, "Identifier must not be null!");
|
||||
|
||||
@Override
|
||||
public Optional<Object> getOptionalValue() {
|
||||
return Optional.of(value);
|
||||
return identifier.map(it -> (Identifier) Identifier.of(it)).orElse(Unset.UNSET);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier value.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
Optional<? extends Object> getOptionalValue();
|
||||
|
||||
/**
|
||||
* A specified identifier that exposes a definitely present identifier value.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
interface Specified extends Identifier {
|
||||
|
||||
/**
|
||||
* Returns the identifier value.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
default Object getValue() {
|
||||
return getOptionalValue().orElseThrow(() -> new IllegalStateException("Should not happen!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,40 +15,26 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.mapping.event;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@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 passed to the delete method.
|
||||
*/
|
||||
private final Optional<Object> optionalEntity;
|
||||
|
||||
JdbcEvent(Identifier id, Optional<Object> optionalEntity) {
|
||||
super(id);
|
||||
this.optionalEntity = optionalEntity;
|
||||
}
|
||||
public interface JdbcEvent {
|
||||
|
||||
/**
|
||||
* The identifier of the entity, triggering this event. Also available via {@link #getSource()}.
|
||||
*
|
||||
* @return the source of the event as an {@link Identifier}.
|
||||
*/
|
||||
public Identifier getId() {
|
||||
return (Identifier) getSource();
|
||||
}
|
||||
Identifier getId();
|
||||
|
||||
/**
|
||||
* Returns the entity the event was triggered for.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
Optional<Object> getOptionalEntity();
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,14 @@ package org.springframework.data.jdbc.mapping.event;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A {@link JdbcEvent} which is guaranteed to have an entity.
|
||||
* A {@link SimpleJdbcEvent} which is guaranteed to have an entity.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JdbcEventWithEntity extends JdbcEvent implements WithEntity {
|
||||
public class JdbcEventWithEntity extends SimpleJdbcEvent implements WithEntity {
|
||||
|
||||
private static final long serialVersionUID = 4891455396602090638L;
|
||||
|
||||
public JdbcEventWithEntity(Identifier id, Object entity) {
|
||||
super(id, Optional.of(entity));
|
||||
|
||||
@@ -20,14 +20,30 @@ import java.util.Optional;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
|
||||
/**
|
||||
* A {@link JdbcEvent} guaranteed to have an identifier.
|
||||
* A {@link SimpleJdbcEvent} guaranteed to have an identifier.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JdbcEventWithId extends JdbcEvent implements WithId {
|
||||
public class JdbcEventWithId extends SimpleJdbcEvent implements WithId {
|
||||
|
||||
private static final long serialVersionUID = -8071323168471611098L;
|
||||
|
||||
private final Specified id;
|
||||
|
||||
public JdbcEventWithId(Specified id, Optional<Object> entity) {
|
||||
|
||||
super(id, entity);
|
||||
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.mapping.event.JdbcEvent#getId()
|
||||
*/
|
||||
@Override
|
||||
public Specified getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,15 @@ import java.util.Optional;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
|
||||
/**
|
||||
* A {@link JdbcEvent} which is guaranteed to have an identifier and an entity.
|
||||
* A {@link SimpleJdbcEvent} which is guaranteed to have an identifier and an entity.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.0
|
||||
*/
|
||||
@Getter
|
||||
public class JdbcEventWithIdAndEntity extends JdbcEvent implements WithId, WithEntity {
|
||||
public class JdbcEventWithIdAndEntity extends JdbcEventWithId implements WithEntity {
|
||||
|
||||
private static final long serialVersionUID = -3194462549552515519L;
|
||||
|
||||
public JdbcEventWithIdAndEntity(Specified id, Object entity) {
|
||||
super(id, Optional.of(entity));
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.mapping.event;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @author Oliver Gierke
|
||||
* @since 2.0
|
||||
*/
|
||||
class SimpleJdbcEvent extends ApplicationEvent implements JdbcEvent {
|
||||
|
||||
private static final long serialVersionUID = -1798807778668751659L;
|
||||
|
||||
private final Object entity;
|
||||
|
||||
SimpleJdbcEvent(Identifier id, Optional<Object> entity) {
|
||||
|
||||
super(id);
|
||||
|
||||
this.entity = entity.orElse(null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.mapping.event.JdbcEvent#getId()
|
||||
*/
|
||||
@Override
|
||||
public Identifier getId() {
|
||||
return (Identifier) getSource();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.mapping.event.JdbcEvent#getOptionalEntity()
|
||||
*/
|
||||
@Override
|
||||
public Optional<Object> getOptionalEntity() {
|
||||
return Optional.ofNullable(entity);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.mapping.event;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
|
||||
/**
|
||||
* Simple value object for {@link Specified}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
* @since 2.0
|
||||
*/
|
||||
@Value(staticConstructor = "of")
|
||||
class SpecifiedIdentifier implements Specified {
|
||||
|
||||
Object value;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.mapping.event.Identifier#getOptionalValue()
|
||||
*/
|
||||
@Override
|
||||
public Optional<? extends Object> getOptionalValue() {
|
||||
return Optional.of(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.mapping.event;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* An unset identifier. Always returns {@link Optional#empty()} as value.
|
||||
*
|
||||
* @author Jens Schaude
|
||||
* @author Oliver Gierke
|
||||
* @since 2.0
|
||||
*/
|
||||
enum Unset implements Identifier {
|
||||
|
||||
UNSET;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.mapping.event.Identifier#getOptionalValue()
|
||||
*/
|
||||
@Override
|
||||
public Optional<Object> getOptionalValue() {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -16,16 +16,18 @@
|
||||
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 SimpleJdbcEvent}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
|
||||
*/
|
||||
public interface WithEntity {
|
||||
public interface WithEntity extends JdbcEvent {
|
||||
|
||||
/**
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
default Object getEntity() {
|
||||
return ((JdbcEvent) this).getOptionalEntity()
|
||||
.orElseThrow(() -> new IllegalStateException("Entity must not be NULL"));
|
||||
return getOptionalEntity().orElseThrow(() -> new IllegalStateException("Entity must not be NULL"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,15 +18,16 @@ 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 SimpleJdbcEvent}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 {
|
||||
public interface WithId extends JdbcEvent {
|
||||
|
||||
default Specified getSpecifiedId() {
|
||||
return (Specified) ((JdbcEvent) this).getId();
|
||||
}
|
||||
/**
|
||||
* Events with an identifier will always return a {@link Specified} one.
|
||||
*/
|
||||
Specified getId();
|
||||
}
|
||||
|
||||
@@ -35,21 +35,26 @@ public class BasicJdbcPersistentProperty extends AnnotationBasedPersistentProper
|
||||
*
|
||||
* @param property must not be {@literal null}.
|
||||
* @param owner must not be {@literal null}.
|
||||
* @param simpleTypeHolder
|
||||
* @param simpleTypeHolder must not be {@literal null}.
|
||||
*/
|
||||
public BasicJdbcPersistentProperty( //
|
||||
Property property, //
|
||||
PersistentEntity<?, JdbcPersistentProperty> owner, //
|
||||
SimpleTypeHolder simpleTypeHolder //
|
||||
) {
|
||||
public BasicJdbcPersistentProperty(Property property, PersistentEntity<?, JdbcPersistentProperty> owner,
|
||||
SimpleTypeHolder simpleTypeHolder) {
|
||||
super(property, owner, simpleTypeHolder);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation()
|
||||
*/
|
||||
@Override
|
||||
protected Association<JdbcPersistentProperty> createAssociation() {
|
||||
return null;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty#getColumnName()
|
||||
*/
|
||||
public String getColumnName() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@@ -15,31 +15,26 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.mapping.model;
|
||||
|
||||
import org.springframework.data.mapping.model.BasicPersistentEntity;
|
||||
import org.springframework.data.util.TypeInformation;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
|
||||
/**
|
||||
* Meta data a repository might need for implementing persistence operations for instances of type {@code T}
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JdbcPersistentEntity<T> extends BasicPersistentEntity<T, JdbcPersistentProperty> {
|
||||
public interface JdbcPersistentEntity<T> extends PersistentEntity<T, JdbcPersistentProperty> {
|
||||
|
||||
private final String tableName;
|
||||
/**
|
||||
* Returns the name of the table backing the given entity.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getTableName();
|
||||
|
||||
public JdbcPersistentEntity(TypeInformation<T> information) {
|
||||
|
||||
super(information);
|
||||
|
||||
tableName = getType().getSimpleName();
|
||||
}
|
||||
|
||||
public String getTableName() {
|
||||
return tableName;
|
||||
}
|
||||
|
||||
public String getIdColumn() {
|
||||
return getRequiredIdProperty().getName();
|
||||
}
|
||||
/**
|
||||
* Returns the column representing the identifier.
|
||||
*
|
||||
* @return will never be {@literal null}.
|
||||
*/
|
||||
String getIdColumn();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.mapping.model;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
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}
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @since 2.0
|
||||
*/
|
||||
public class JdbcPersistentEntityImpl<T> extends BasicPersistentEntity<T, JdbcPersistentProperty>
|
||||
implements JdbcPersistentEntity<T> {
|
||||
|
||||
private final @Getter String tableName;
|
||||
|
||||
/**
|
||||
* Creates a new {@link JdbcPersistentEntityImpl} for the given {@link TypeInformation}.
|
||||
*
|
||||
* @param information must not be {@literal null}.
|
||||
*/
|
||||
public JdbcPersistentEntityImpl(TypeInformation<T> information) {
|
||||
|
||||
super(information);
|
||||
|
||||
tableName = getType().getSimpleName();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity#getIdColumn()
|
||||
*/
|
||||
@Override
|
||||
public String getIdColumn() {
|
||||
return getRequiredIdProperty().getName();
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,18 @@ package org.springframework.data.jdbc.mapping.model;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
|
||||
/**
|
||||
* A {@link PersistentProperty} for JDBC.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface JdbcPersistentProperty extends PersistentProperty<JdbcPersistentProperty> {
|
||||
|
||||
/**
|
||||
* Returns the name of the column backing that property.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
String getColumnName();
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Optional;
|
||||
@@ -26,8 +28,10 @@ import org.springframework.data.convert.EntityInstantiator;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentProperty;
|
||||
import org.springframework.data.mapping.PersistentPropertyAccessor;
|
||||
import org.springframework.data.mapping.PreferredConstructor.Parameter;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.mapping.model.ConvertingPropertyAccessor;
|
||||
import org.springframework.data.mapping.model.MappingException;
|
||||
import org.springframework.data.mapping.model.ParameterValueProvider;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
@@ -36,65 +40,68 @@ import org.springframework.jdbc.core.RowMapper;
|
||||
* Maps a ResultSet to an entity of type {@code T}
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
|
||||
*/
|
||||
@Override
|
||||
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
public T mapRow(ResultSet resultSet, int rowNumber) throws SQLException {
|
||||
|
||||
T t = createInstance(rs);
|
||||
T result = createInstance(resultSet);
|
||||
|
||||
entity.doWithProperties((PropertyHandler<JdbcPersistentProperty>) property -> {
|
||||
setProperty(rs, t, property);
|
||||
|
||||
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(result);
|
||||
ConvertingPropertyAccessor propertyAccessor = new ConvertingPropertyAccessor(accessor, conversions);
|
||||
|
||||
propertyAccessor.setProperty(property, readFrom(resultSet, property));
|
||||
});
|
||||
|
||||
return t;
|
||||
return result;
|
||||
}
|
||||
|
||||
private T createInstance(ResultSet rs) {
|
||||
|
||||
return instantiator.createInstance(entity, new ResultSetParameterValueProvider(rs));
|
||||
return instantiator.createInstance(entity, ResultSetParameterValueProvider.of(rs, conversions));
|
||||
}
|
||||
|
||||
private void setProperty(ResultSet rs, T t, PersistentProperty property) {
|
||||
private static Optional<Object> readFrom(ResultSet resultSet, PersistentProperty<?> property) {
|
||||
|
||||
try {
|
||||
|
||||
Object converted = conversions.convert(rs.getObject(property.getName()), property.getType());
|
||||
entity.getPropertyAccessor(t).setProperty(property, Optional.of(converted));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(String.format("Couldn't set property %s.", property.getName()), e);
|
||||
return Optional.ofNullable(resultSet.getObject(property.getName()));
|
||||
} catch (SQLException o_O) {
|
||||
throw new MappingException(String.format("Could not read property %s from result set!", property), o_O);
|
||||
}
|
||||
}
|
||||
|
||||
private class ResultSetParameterValueProvider implements ParameterValueProvider<JdbcPersistentProperty> {
|
||||
@RequiredArgsConstructor(staticName = "of")
|
||||
private static class ResultSetParameterValueProvider implements ParameterValueProvider<JdbcPersistentProperty> {
|
||||
|
||||
private final ResultSet rs;
|
||||
|
||||
ResultSetParameterValueProvider(ResultSet rs) {
|
||||
this.rs = rs;
|
||||
}
|
||||
private final ResultSet resultSet;
|
||||
private final ConversionService conversionService;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.ParameterValueProvider#getParameterValue(org.springframework.data.mapping.PreferredConstructor.Parameter)
|
||||
*/
|
||||
@Override
|
||||
public <S> Optional<S> getParameterValue(Parameter<S, JdbcPersistentProperty> parameter) {
|
||||
|
||||
return parameter.getName().map(name -> {
|
||||
try {
|
||||
return conversions.convert(rs.getObject(name), parameter.getType().getType());
|
||||
} catch (SQLException e) {
|
||||
|
||||
throw new MappingException( //
|
||||
String.format("Couldn't read column %s from ResultSet.", name) //
|
||||
);
|
||||
try {
|
||||
return conversionService.convert(resultSet.getObject(name), parameter.getType().getType());
|
||||
} catch (SQLException o_O) {
|
||||
throw new MappingException(String.format("Couldn't read column %s from ResultSet.", name), o_O);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.jdbc.mapping.event.AfterCreation;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcPersistentEntityInformation;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
@@ -35,18 +35,22 @@ import org.springframework.jdbc.core.RowMapper;
|
||||
* @since 2.0
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class EventPublishingEntityRowMapper<T, ID extends Serializable> implements RowMapper<T> {
|
||||
public class EventPublishingEntityRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
private final RowMapper<T> delegate;
|
||||
private final JdbcPersistentEntityInformation<T, ID> entityInformation;
|
||||
private final ApplicationEventPublisher publisher;
|
||||
private final @NonNull RowMapper<T> delegate;
|
||||
private final @NonNull JdbcPersistentEntityInformation<T, ?> entityInformation;
|
||||
private final @NonNull ApplicationEventPublisher publisher;
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.jdbc.core.RowMapper#mapRow(java.sql.ResultSet, int)
|
||||
*/
|
||||
@Override
|
||||
public T mapRow(ResultSet resultSet, int i) throws SQLException {
|
||||
|
||||
T instance = delegate.mapRow(resultSet, i);
|
||||
|
||||
publisher.publishEvent(new AfterCreation(new Specified(entityInformation.getId(instance)), instance));
|
||||
publisher.publishEvent(new AfterCreation(Identifier.of(entityInformation.getRequiredId(instance)), instance));
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
@@ -35,13 +34,16 @@ import org.springframework.data.jdbc.mapping.event.AfterUpdate;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeDelete;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeInsert;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeUpdate;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntity;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentEntityImpl;
|
||||
import org.springframework.data.jdbc.mapping.model.JdbcPersistentProperty;
|
||||
import org.springframework.data.jdbc.repository.support.BasicJdbcPersistentEntityInformation;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcPersistentEntityInformation;
|
||||
import org.springframework.data.mapping.PropertyHandler;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.data.util.Streamable;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||
@@ -66,11 +68,15 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
private final ApplicationEventPublisher publisher;
|
||||
private final ConversionService conversions = new DefaultConversionService();
|
||||
|
||||
public SimpleJdbcRepository( //
|
||||
JdbcPersistentEntity<T> persistentEntity, //
|
||||
NamedParameterJdbcOperations jdbcOperations, //
|
||||
ApplicationEventPublisher publisher //
|
||||
) {
|
||||
/**
|
||||
* Creates a new {@link SimpleJdbcRepository} for the given {@link JdbcPersistentEntityImpl}
|
||||
*
|
||||
* @param persistentEntity
|
||||
* @param jdbcOperations
|
||||
* @param 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.");
|
||||
@@ -85,6 +91,10 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
this.sql = new SqlGenerator(persistentEntity);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#save(S)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> S save(S instance) {
|
||||
|
||||
@@ -97,6 +107,10 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
return instance;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#save(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public <S extends T> Iterable<S> save(Iterable<S> entities) {
|
||||
|
||||
@@ -105,6 +119,10 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
return savedEntities;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findOne(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public Optional<T> findOne(ID id) {
|
||||
|
||||
@@ -112,43 +130,69 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
.ofNullable(operations.queryForObject(sql.getFindOne(), new MapSqlParameterSource("id", id), entityRowMapper));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public boolean exists(ID id) {
|
||||
|
||||
return operations.queryForObject(sql.getExists(), new MapSqlParameterSource("id", id), Boolean.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findAll()
|
||||
*/
|
||||
@Override
|
||||
public Iterable<T> findAll() {
|
||||
return operations.query(sql.getFindAll(), entityRowMapper);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#findAll(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public Iterable<T> findAll(Iterable<ID> ids) {
|
||||
return operations.query(sql.getFindAllInList(), new MapSqlParameterSource("ids", ids), entityRowMapper);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#count()
|
||||
*/
|
||||
@Override
|
||||
public long count() {
|
||||
return operations.getJdbcOperations().queryForObject(sql.getCount(), Long.class);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable)
|
||||
*/
|
||||
@Override
|
||||
public void delete(ID id) {
|
||||
doDelete(new Specified(id), Optional.empty());
|
||||
doDelete(Identifier.of(id), Optional.empty());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public void delete(T instance) {
|
||||
doDelete(new Specified(entityInformation.getId(instance).orElse(null)), Optional.of(instance));
|
||||
doDelete(Identifier.of(entityInformation.getRequiredId(instance)), Optional.of(instance));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable)
|
||||
*/
|
||||
@Override
|
||||
public void delete(Iterable<? extends T> entities) {
|
||||
|
||||
List<ID> idList = StreamSupport.stream(entities.spliterator(), false) //
|
||||
.map(e -> entityInformation.getId(e)
|
||||
.orElseThrow(() -> new IllegalArgumentException(String.format("Can't obtain id for %s", e)))) //
|
||||
List<ID> idList = Streamable.of(entities).stream() //
|
||||
.map(e -> entityInformation.getRequiredId(e)) //
|
||||
.collect(Collectors.toList());
|
||||
|
||||
MapSqlParameterSource sqlParameterSource = new MapSqlParameterSource("ids", idList);
|
||||
@@ -189,7 +233,7 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
throw new IllegalStateException(String.format(ENTITY_NEW_AFTER_INSERT, persistentEntity));
|
||||
}
|
||||
|
||||
publisher.publishEvent(new AfterInsert(new Specified(entityInformation.getId(instance)), instance));
|
||||
publisher.publishEvent(new AfterInsert(Identifier.of(entityInformation.getRequiredId(instance)), instance));
|
||||
}
|
||||
|
||||
private <S extends T> ID getIdValueOrNull(S instance) {
|
||||
@@ -211,27 +255,26 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
|
||||
try {
|
||||
|
||||
Object idValueFromJdbc = getIdFromHolder(holder);
|
||||
if (idValueFromJdbc != null) {
|
||||
getIdFromHolder(holder).ifPresent(it -> {
|
||||
|
||||
Class<?> targetType = persistentEntity.getRequiredIdProperty().getType();
|
||||
Object converted = convert(idValueFromJdbc, targetType);
|
||||
Object converted = convert(it, targetType);
|
||||
entityInformation.setId(instance, Optional.of(converted));
|
||||
}
|
||||
});
|
||||
|
||||
} catch (NonTransientDataAccessException e) {
|
||||
throw new UnableToSetIdException("Unable to set id of " + instance, e);
|
||||
throw new UnableToSetId("Unable to set id of " + instance, e);
|
||||
}
|
||||
}
|
||||
|
||||
private Object getIdFromHolder(KeyHolder holder) {
|
||||
private Optional<Object> getIdFromHolder(KeyHolder holder) {
|
||||
|
||||
try {
|
||||
// mysql just returns one value with a special name
|
||||
return holder.getKey();
|
||||
// MySQL just returns one value with a special name
|
||||
return Optional.ofNullable(holder.getKey());
|
||||
} catch (InvalidDataAccessApiUsageException e) {
|
||||
// postgress returns a value for each column
|
||||
return holder.getKeys().get(persistentEntity.getIdColumn());
|
||||
// Postgres returns a value for each column
|
||||
return Optional.ofNullable(holder.getKeys().get(persistentEntity.getIdColumn()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -249,7 +292,7 @@ public class SimpleJdbcRepository<T, ID extends Serializable> implements CrudRep
|
||||
|
||||
private <S extends T> void doUpdate(S instance) {
|
||||
|
||||
Specified specifiedId = new Specified(entityInformation.getId(instance));
|
||||
Specified specifiedId = Identifier.of(entityInformation.getRequiredId(instance));
|
||||
publisher.publishEvent(new BeforeUpdate(specifiedId, instance));
|
||||
operations.update(sql.getUpdate(), getPropertyMap(instance));
|
||||
publisher.publishEvent(new AfterUpdate(specifiedId, instance));
|
||||
|
||||
@@ -51,6 +51,7 @@ class SqlGenerator {
|
||||
private final List<String> nonIdPropertyNames = new ArrayList<>();
|
||||
|
||||
SqlGenerator(JdbcPersistentEntity<?> entity) {
|
||||
|
||||
this.entity = entity;
|
||||
|
||||
initPropertyNames();
|
||||
@@ -72,6 +73,7 @@ class SqlGenerator {
|
||||
}
|
||||
|
||||
private <T> void initPropertyNames() {
|
||||
|
||||
entity.doWithProperties((PropertyHandler<JdbcPersistentProperty>) p -> {
|
||||
propertyNames.add(p.getName());
|
||||
if (!entity.isIdProperty(p)) {
|
||||
@@ -143,8 +145,7 @@ class SqlGenerator {
|
||||
private String createInsertSql() {
|
||||
|
||||
String insertTemplate = "insert into %s (%s) values (%s)";
|
||||
|
||||
String tableColumns = nonIdPropertyNames.stream().collect(Collectors.joining(", "));
|
||||
String tableColumns = String.join(", ", nonIdPropertyNames);
|
||||
String parameterNames = nonIdPropertyNames.stream().collect(Collectors.joining(", :", ":", ""));
|
||||
|
||||
return String.format(insertTemplate, entity.getTableName(), tableColumns, parameterNames);
|
||||
@@ -154,7 +155,8 @@ class SqlGenerator {
|
||||
|
||||
String updateTemplate = "update %s set %s where %s = :%s";
|
||||
|
||||
String setClause = propertyNames.stream().map(n -> String.format("%s = :%s", n, n))
|
||||
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());
|
||||
|
||||
@@ -23,9 +23,11 @@ import org.springframework.dao.NonTransientDataAccessException;
|
||||
* @author Jens Schauder
|
||||
* @since 2.0
|
||||
*/
|
||||
public class UnableToSetIdException extends NonTransientDataAccessException {
|
||||
public class UnableToSetId extends NonTransientDataAccessException {
|
||||
|
||||
public UnableToSetIdException(String message, Throwable cause) {
|
||||
private static final long serialVersionUID = 3285001352389420376L;
|
||||
|
||||
public UnableToSetId(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -31,11 +31,16 @@ public class BasicJdbcPersistentEntityInformation<T, ID extends Serializable> ex
|
||||
private final JdbcPersistentEntity<T> persistentEntity;
|
||||
|
||||
public BasicJdbcPersistentEntityInformation(JdbcPersistentEntity<T> persistentEntity) {
|
||||
|
||||
super(persistentEntity);
|
||||
|
||||
this.persistentEntity = persistentEntity;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.repository.support.JdbcPersistentEntityInformation#setId(java.lang.Object, java.util.Optional)
|
||||
*/
|
||||
@Override
|
||||
public void setId(T instance, Optional<Object> value) {
|
||||
persistentEntity.getPropertyAccessor(instance).setProperty(persistentEntity.getRequiredIdProperty(), value);
|
||||
|
||||
@@ -27,4 +27,16 @@ import org.springframework.data.repository.core.EntityInformation;
|
||||
public interface JdbcPersistentEntityInformation<T, ID extends Serializable> extends EntityInformation<T, ID> {
|
||||
|
||||
void setId(T instance, Optional<Object> value);
|
||||
|
||||
/**
|
||||
* Returns the identifier of the given entity or throws and exception if it can't be obtained.
|
||||
*
|
||||
* @param entity must not be {@literal null}.
|
||||
* @return the identifier of the given entity
|
||||
* @throws IllegalArgumentException in case no identifier can be obtained for the given entity.
|
||||
*/
|
||||
default ID getRequiredId(T entity) {
|
||||
return getId(entity).orElseThrow(() -> new IllegalArgumentException(
|
||||
String.format("Could not obtain required identifier from entity %s!", entity)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,33 @@
|
||||
/*
|
||||
* 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.repository;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.Value;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.jdbc.mapping.event.AfterCreation;
|
||||
@@ -16,13 +35,17 @@ import org.springframework.data.jdbc.repository.support.JdbcPersistentEntityInfo
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link EventPublishingEntityRowMapper}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class EventPublishingEntityRowMapperTest {
|
||||
|
||||
RowMapper<DummyEntity> rowMapperDelegate = mock(RowMapper.class);
|
||||
JdbcPersistentEntityInformation<DummyEntity, Long> entityInformation = mock(JdbcPersistentEntityInformation.class);
|
||||
ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
|
||||
@Mock RowMapper<DummyEntity> rowMapperDelegate;
|
||||
@Mock JdbcPersistentEntityInformation<DummyEntity, Long> entityInformation;
|
||||
@Mock ApplicationEventPublisher publisher;
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void eventGetsPublishedAfterInstantiation() throws SQLException {
|
||||
@@ -30,10 +53,8 @@ public class EventPublishingEntityRowMapperTest {
|
||||
when(rowMapperDelegate.mapRow(any(ResultSet.class), anyInt())).thenReturn(new DummyEntity(1L));
|
||||
when(entityInformation.getId(any())).thenReturn(Optional.of(1L));
|
||||
|
||||
EventPublishingEntityRowMapper rowMapper = new EventPublishingEntityRowMapper<>( //
|
||||
rowMapperDelegate, //
|
||||
entityInformation, //
|
||||
publisher);
|
||||
EventPublishingEntityRowMapper<?> rowMapper = new EventPublishingEntityRowMapper<>(rowMapperDelegate,
|
||||
entityInformation, publisher);
|
||||
|
||||
ResultSet resultSet = mock(ResultSet.class);
|
||||
rowMapper.mapRow(resultSet, 1);
|
||||
@@ -41,8 +62,8 @@ public class EventPublishingEntityRowMapperTest {
|
||||
verify(publisher).publishEvent(isA(AfterCreation.class));
|
||||
}
|
||||
|
||||
@Data
|
||||
@Value
|
||||
static class DummyEntity {
|
||||
@Id private final Long Id;
|
||||
@Id Long Id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,27 +15,21 @@
|
||||
*/
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import lombok.Value;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
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.context.annotation.Import;
|
||||
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.jdbc.testing.TestConfiguration;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
@@ -47,9 +41,31 @@ import org.springframework.test.context.junit4.rules.SpringMethodRule;
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@ContextConfiguration(classes = TestConfiguration.class)
|
||||
@ContextConfiguration
|
||||
public class JdbcRepositoryIdGenerationIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import(TestConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Autowired JdbcRepositoryFactory factory;
|
||||
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
return JdbcRepositoryIdGenerationIntegrationTests.class;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReadOnlyIdEntityRepository readOnlyIdRepository() {
|
||||
return factory.getRepository(ReadOnlyIdEntityRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PrimitiveIdEntityRepository primitiveIdRepository() {
|
||||
return factory.getRepository(PrimitiveIdEntityRepository.class);
|
||||
}
|
||||
}
|
||||
|
||||
@ClassRule public static final SpringClassRule classRule = new SpringClassRule();
|
||||
@Rule public SpringMethodRule methodRule = new SpringMethodRule();
|
||||
|
||||
@@ -60,17 +76,15 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
|
||||
@Test // DATAJDBC-98
|
||||
public void idWithoutSetterGetsSet() {
|
||||
|
||||
ReadOnlyIdEntity entity = new ReadOnlyIdEntity(null);
|
||||
entity.setName("Entity Name");
|
||||
entity = readOnlyIdrepository.save(entity);
|
||||
ReadOnlyIdEntity entity = readOnlyIdrepository.save(new ReadOnlyIdEntity(null, "Entity Name"));
|
||||
|
||||
assertThat(entity.getId()).isNotNull();
|
||||
|
||||
Optional<ReadOnlyIdEntity> reloadedEntity = readOnlyIdrepository.findOne(entity.getId());
|
||||
assertThat(readOnlyIdrepository.findOne(entity.getId())).hasValueSatisfying(it -> {
|
||||
|
||||
assertTrue(reloadedEntity.isPresent());
|
||||
assertEquals(entity.getId(), reloadedEntity.get().getId());
|
||||
assertEquals(entity.getName(), reloadedEntity.get().getName());
|
||||
assertThat(it.getId()).isEqualTo(entity.getId());
|
||||
assertThat(it.getName()).isEqualTo(entity.getName());
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-98
|
||||
@@ -78,26 +92,26 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
|
||||
|
||||
PrimitiveIdEntity entity = new PrimitiveIdEntity(0);
|
||||
entity.setName("Entity Name");
|
||||
entity = primitiveIdRepository.save(entity);
|
||||
|
||||
assertThat(entity.getId()).isNotNull();
|
||||
assertThat(entity.getId()).isNotEqualTo(0L);
|
||||
PrimitiveIdEntity saved = primitiveIdRepository.save(entity);
|
||||
|
||||
Optional<PrimitiveIdEntity> reloadedEntity = primitiveIdRepository.findOne(entity.getId());
|
||||
assertThat(saved.getId()).isNotEqualTo(0L);
|
||||
|
||||
assertTrue(reloadedEntity.isPresent());
|
||||
assertEquals(entity.getId(), reloadedEntity.get().getId());
|
||||
assertEquals(entity.getName(), reloadedEntity.get().getName());
|
||||
assertThat(primitiveIdRepository.findOne(saved.getId())).hasValueSatisfying(it -> {
|
||||
|
||||
assertThat(it.getId()).isEqualTo(saved.getId());
|
||||
assertThat(it.getName()).isEqualTo(saved.getName());
|
||||
});
|
||||
}
|
||||
|
||||
private interface ReadOnlyIdEntityRepository extends CrudRepository<ReadOnlyIdEntity, Long> {}
|
||||
|
||||
private interface PrimitiveIdEntityRepository extends CrudRepository<PrimitiveIdEntity, Long> {}
|
||||
|
||||
@Data
|
||||
public interface ReadOnlyIdEntityRepository extends CrudRepository<ReadOnlyIdEntity, Long> {}
|
||||
|
||||
@Value
|
||||
static class ReadOnlyIdEntity {
|
||||
|
||||
@Id private final Long id;
|
||||
@Id Long id;
|
||||
String name;
|
||||
}
|
||||
|
||||
@@ -107,33 +121,4 @@ public class JdbcRepositoryIdGenerationIntegrationTests {
|
||||
@Id private final long id;
|
||||
String name;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("org.springframework.data.jdbc.testing")
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
return JdbcRepositoryIdGenerationIntegrationTests.class;
|
||||
}
|
||||
|
||||
@Bean
|
||||
NamedParameterJdbcTemplate template(DataSource db) {
|
||||
return new NamedParameterJdbcTemplate(db);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ReadOnlyIdEntityRepository readOnlyIdRepository(DataSource db) {
|
||||
|
||||
return new JdbcRepositoryFactory(new NamedParameterJdbcTemplate(db), mock(ApplicationEventPublisher.class))
|
||||
.getRepository(ReadOnlyIdEntityRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PrimitiveIdEntityRepository primitiveIdRepository(NamedParameterJdbcTemplate template) {
|
||||
|
||||
return new JdbcRepositoryFactory(template, mock(ApplicationEventPublisher.class))
|
||||
.getRepository(PrimitiveIdEntityRepository.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,37 +16,27 @@
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
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.context.annotation.Import;
|
||||
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.jdbc.testing.TestConfiguration;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
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.test.context.ContextConfiguration;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -54,61 +44,58 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
*
|
||||
* @author Jens Schauder
|
||||
*/
|
||||
@ContextConfiguration(classes = TestConfiguration.class)
|
||||
@ContextConfiguration
|
||||
@Transactional
|
||||
public class JdbcRepositoryIntegrationTests {
|
||||
|
||||
@Configuration
|
||||
@Import(TestConfiguration.class)
|
||||
static class Config {
|
||||
|
||||
@Autowired JdbcRepositoryFactory factory;
|
||||
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
return JdbcRepositoryIntegrationTests.class;
|
||||
}
|
||||
|
||||
@Bean
|
||||
DummyEntityRepository dummyEntityRepository() {
|
||||
return factory.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
}
|
||||
|
||||
@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);
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
|
||||
int count = JdbcTestUtils.countRowsInTableWhere( //
|
||||
(JdbcTemplate) template.getJdbcOperations(), //
|
||||
"dummyentity", //
|
||||
"idProp = " + entity.getIdProp() //
|
||||
);
|
||||
|
||||
assertEquals(1, count);
|
||||
assertThat(JdbcTestUtils.countRowsInTableWhere((JdbcTemplate) template.getJdbcOperations(), "dummyentity",
|
||||
"idProp = " + entity.getIdProp())).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-95
|
||||
public void saveAndLoadAnEntity() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
|
||||
Optional<DummyEntity> reloadedEntity = repository.findOne(entity.getIdProp());
|
||||
assertThat(repository.findOne(entity.getIdProp())).hasValueSatisfying(it -> {
|
||||
|
||||
assertTrue(reloadedEntity.isPresent());
|
||||
|
||||
assertEquals(entity.getIdProp(), reloadedEntity.get().getIdProp());
|
||||
assertEquals(entity.getName(), reloadedEntity.get().getName());
|
||||
assertThat(it.getIdProp()).isEqualTo(entity.getIdProp());
|
||||
assertThat(it.getName()).isEqualTo(entity.getName());
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void savesManyEntities() {
|
||||
|
||||
DummyEntity entity = createDummyEntity();
|
||||
DummyEntity other = createDummyEntity();
|
||||
|
||||
repository.save(asList(entity, other));
|
||||
@@ -121,35 +108,34 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Test // DATAJDBC-97
|
||||
public void existsReturnsTrueIffEntityExists() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
|
||||
assertTrue(repository.exists(entity.getIdProp()));
|
||||
assertFalse(repository.exists(entity.getIdProp() + 1));
|
||||
assertThat(repository.exists(entity.getIdProp())).isTrue();
|
||||
assertThat(repository.exists(entity.getIdProp() + 1)).isFalse();
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void findAllFindsAllEntities() {
|
||||
|
||||
DummyEntity other = createDummyEntity();
|
||||
|
||||
other = repository.save(other);
|
||||
entity = repository.save(entity);
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity other = repository.save(createDummyEntity());
|
||||
|
||||
Iterable<DummyEntity> all = repository.findAll();
|
||||
|
||||
assertThat(all).extracting("idProp").containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
assertThat(all)//
|
||||
.extracting(DummyEntity::getIdProp)//
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void findAllFindsAllSpecifiedEntities() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity other = repository.save(createDummyEntity());
|
||||
|
||||
Iterable<DummyEntity> all = repository.findAll(asList(entity.getIdProp(), three.getIdProp()));
|
||||
|
||||
assertThat(all).extracting("idProp").containsExactlyInAnyOrder(entity.getIdProp(), three.getIdProp());
|
||||
assertThat(repository.findAll(asList(entity.getIdProp(), other.getIdProp())))//
|
||||
.extracting(DummyEntity::getIdProp)//
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), other.getIdProp());
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
@@ -157,7 +143,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(entity);
|
||||
repository.save(createDummyEntity());
|
||||
|
||||
assertThat(repository.count()).isEqualTo(3L);
|
||||
}
|
||||
@@ -165,7 +151,7 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteById() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
DummyEntity one = repository.save(createDummyEntity());
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
|
||||
@@ -173,17 +159,17 @@ public class JdbcRepositoryIntegrationTests {
|
||||
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
.containsExactlyInAnyOrder(entity.getIdProp(), three.getIdProp());
|
||||
.containsExactlyInAnyOrder(one.getIdProp(), three.getIdProp());
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteByEntity() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
DummyEntity one = repository.save(createDummyEntity());
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
|
||||
repository.delete(entity);
|
||||
repository.delete(one);
|
||||
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
@@ -193,11 +179,11 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteByList() {
|
||||
|
||||
repository.save(entity);
|
||||
DummyEntity one = repository.save(createDummyEntity());
|
||||
DummyEntity two = repository.save(createDummyEntity());
|
||||
DummyEntity three = repository.save(createDummyEntity());
|
||||
|
||||
repository.delete(asList(entity, three));
|
||||
repository.delete(asList(one, three));
|
||||
|
||||
assertThat(repository.findAll()) //
|
||||
.extracting(DummyEntity::getIdProp) //
|
||||
@@ -207,9 +193,11 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Test // DATAJDBC-97
|
||||
public void deleteAll() {
|
||||
|
||||
repository.save(entity);
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
repository.save(createDummyEntity());
|
||||
|
||||
assertThat(repository.findAll()).isNotEmpty();
|
||||
|
||||
repository.deleteAll();
|
||||
|
||||
@@ -219,22 +207,20 @@ public class JdbcRepositoryIntegrationTests {
|
||||
@Test // DATAJDBC-98
|
||||
public void update() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
|
||||
entity.setName("something else");
|
||||
DummyEntity saved = repository.save(entity);
|
||||
|
||||
entity = repository.save(entity);
|
||||
|
||||
Optional<DummyEntity> reloaded = repository.findOne(entity.getIdProp());
|
||||
|
||||
assertTrue(reloaded.isPresent());
|
||||
assertThat(reloaded.get().getName()).isEqualTo(entity.getName());
|
||||
assertThat(repository.findOne(entity.getIdProp())).hasValueSatisfying(it -> {
|
||||
assertThat(it.getName()).isEqualTo(saved.getName());
|
||||
});
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-98
|
||||
public void updateMany() {
|
||||
|
||||
entity = repository.save(entity);
|
||||
DummyEntity entity = repository.save(createDummyEntity());
|
||||
DummyEntity other = repository.save(createDummyEntity());
|
||||
|
||||
entity.setName("something else");
|
||||
@@ -247,7 +233,14 @@ public class JdbcRepositoryIntegrationTests {
|
||||
.containsExactlyInAnyOrder(entity.getName(), other.getName());
|
||||
}
|
||||
|
||||
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
|
||||
private static DummyEntity createDummyEntity() {
|
||||
|
||||
DummyEntity entity = new DummyEntity();
|
||||
entity.setName("Entity Name");
|
||||
return entity;
|
||||
}
|
||||
|
||||
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
|
||||
|
||||
@Data
|
||||
static class DummyEntity {
|
||||
@@ -255,31 +248,4 @@ public class JdbcRepositoryIntegrationTests {
|
||||
String name;
|
||||
@Id private Long idProp;
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ComponentScan("org.springframework.data.jdbc.testing")
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
Class<?> testClass() {
|
||||
return JdbcRepositoryIntegrationTests.class;
|
||||
}
|
||||
|
||||
@Bean
|
||||
NamedParameterJdbcTemplate template(DataSource db) {
|
||||
return new NamedParameterJdbcTemplate(db);
|
||||
}
|
||||
|
||||
@Bean
|
||||
DummyEntityRepository readOnlyIdRepository(NamedParameterJdbcTemplate template) {
|
||||
|
||||
return new JdbcRepositoryFactory(template, mock(ApplicationEventPublisher.class))
|
||||
.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PlatformTransactionManager transactionManager(DataSource db) {
|
||||
return new DataSourceTransactionManager(db);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package org.springframework.data.jdbc.repository;
|
||||
|
||||
import static java.util.Arrays.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.springframework.util.Assert.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -22,7 +22,7 @@ import org.springframework.data.jdbc.mapping.event.AfterUpdate;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeDelete;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeInsert;
|
||||
import org.springframework.data.jdbc.mapping.event.BeforeUpdate;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier.Specified;
|
||||
import org.springframework.data.jdbc.mapping.event.Identifier;
|
||||
import org.springframework.data.jdbc.mapping.event.JdbcEvent;
|
||||
import org.springframework.data.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
@@ -35,9 +35,9 @@ import org.springframework.jdbc.support.KeyHolder;
|
||||
*/
|
||||
public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
|
||||
private FakePublisher publisher = new FakePublisher();
|
||||
FakePublisher publisher = new FakePublisher();
|
||||
|
||||
private DummyEntityRepository repository;
|
||||
DummyEntityRepository repository;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
@@ -47,7 +47,58 @@ public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
repository = factory.getRepository(DummyEntityRepository.class);
|
||||
}
|
||||
|
||||
private NamedParameterJdbcOperations createIdGeneratingOperations() {
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnSave() {
|
||||
|
||||
DummyEntity entity = new DummyEntity(23L);
|
||||
|
||||
repository.save(entity);
|
||||
|
||||
assertThat(publisher.events.get(0)).isInstanceOf(BeforeUpdate.class);
|
||||
assertThat(publisher.events.get(1)).isInstanceOf(AfterUpdate.class);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnSaveMany() {
|
||||
|
||||
DummyEntity entity1 = new DummyEntity(null);
|
||||
DummyEntity entity2 = new DummyEntity(23L);
|
||||
|
||||
repository.save(asList(entity1, entity2));
|
||||
|
||||
assertThat(publisher.events.get(0)).isInstanceOf(BeforeInsert.class);
|
||||
assertThat(publisher.events.get(1)).isInstanceOf(AfterInsert.class);
|
||||
assertThat(publisher.events.get(2)).isInstanceOf(BeforeUpdate.class);
|
||||
assertThat(publisher.events.get(3)).isInstanceOf(AfterUpdate.class);
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnDelete() {
|
||||
|
||||
DummyEntity entity = new DummyEntity(23L);
|
||||
|
||||
repository.delete(entity);
|
||||
|
||||
assertThat(publisher.events.get(0)).isInstanceOf(BeforeDelete.class);
|
||||
assertThat(publisher.events.get(1)).isInstanceOf(AfterDelete.class);
|
||||
|
||||
assertThat(publisher.events.get(0).getOptionalEntity()).hasValue(entity);
|
||||
assertThat(publisher.events.get(1).getOptionalEntity()).hasValue(entity);
|
||||
|
||||
assertThat(publisher.events.get(0).getId()).isEqualTo(Identifier.of(23L));
|
||||
assertThat(publisher.events.get(1).getId()).isEqualTo(Identifier.of(23L));
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnDeleteById() {
|
||||
|
||||
repository.delete(23L);
|
||||
|
||||
assertThat(publisher.events.get(0)).isInstanceOf(BeforeDelete.class);
|
||||
assertThat(publisher.events.get(1)).isInstanceOf(AfterDelete.class);
|
||||
}
|
||||
|
||||
private static NamedParameterJdbcOperations createIdGeneratingOperations() {
|
||||
|
||||
Answer<Integer> setIdInKeyHolder = invocation -> {
|
||||
|
||||
@@ -65,62 +116,11 @@ public class SimpleJdbcRepositoryEventsUnitTests {
|
||||
return operations;
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnSave() {
|
||||
|
||||
DummyEntity entity = new DummyEntity(23L);
|
||||
|
||||
repository.save(entity);
|
||||
|
||||
isInstanceOf(BeforeUpdate.class, publisher.events.get(0));
|
||||
isInstanceOf(AfterUpdate.class, publisher.events.get(1));
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnSaveMany() {
|
||||
|
||||
DummyEntity entity1 = new DummyEntity(null);
|
||||
DummyEntity entity2 = new DummyEntity(23L);
|
||||
|
||||
repository.save(asList(entity1, entity2));
|
||||
|
||||
isInstanceOf(BeforeInsert.class, publisher.events.get(0));
|
||||
isInstanceOf(AfterInsert.class, publisher.events.get(1));
|
||||
isInstanceOf(BeforeUpdate.class, publisher.events.get(2));
|
||||
isInstanceOf(AfterUpdate.class, publisher.events.get(3));
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnDelete() {
|
||||
|
||||
DummyEntity entity = new DummyEntity(23L);
|
||||
|
||||
repository.delete(entity);
|
||||
|
||||
isInstanceOf(BeforeDelete.class, publisher.events.get(0));
|
||||
isInstanceOf(AfterDelete.class, publisher.events.get(1));
|
||||
|
||||
assertEquals(entity, publisher.events.get(0).getOptionalEntity().get());
|
||||
assertEquals(entity, publisher.events.get(1).getOptionalEntity().get());
|
||||
|
||||
assertEquals(new Specified(23L), publisher.events.get(0).getId());
|
||||
assertEquals(new Specified(23L), publisher.events.get(1).getId());
|
||||
}
|
||||
|
||||
@Test // DATAJDBC-99
|
||||
public void publishesEventsOnDeleteById() {
|
||||
|
||||
repository.delete(23L);
|
||||
|
||||
isInstanceOf(BeforeDelete.class, publisher.events.get(0));
|
||||
isInstanceOf(AfterDelete.class, publisher.events.get(1));
|
||||
}
|
||||
|
||||
private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
|
||||
interface DummyEntityRepository extends CrudRepository<DummyEntity, Long> {}
|
||||
|
||||
@Data
|
||||
private static class DummyEntity {
|
||||
@Id private final Long id;
|
||||
static class DummyEntity {
|
||||
private final @Id Long id;
|
||||
}
|
||||
|
||||
static class FakePublisher implements ApplicationEventPublisher {
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.annotation.Autowired;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
|
||||
/**
|
||||
* Basic configuration expecting subclasses to provide a {@link DataSource} via {@link #createDataSource()} to be
|
||||
* exposed to the {@link ApplicationContext}.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
abstract class DataSourceConfiguration {
|
||||
|
||||
@Autowired Class<?> testClass;
|
||||
@Autowired Environment environment;
|
||||
|
||||
@Bean
|
||||
DataSource dataSource() {
|
||||
return createDataSource();
|
||||
}
|
||||
|
||||
@Bean
|
||||
DataSourceInitializer initializer() {
|
||||
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDataSource(dataSource());
|
||||
|
||||
String[] activeProfiles = environment.getActiveProfiles();
|
||||
String profile = activeProfiles.length == 0 ? "" : activeProfiles[0];
|
||||
|
||||
ClassPathResource script = new ClassPathResource(TestUtils.createScriptName(testClass, profile));
|
||||
ResourceDatabasePopulator populator = new ResourceDatabasePopulator(script);
|
||||
customizePopulator(populator);
|
||||
initializer.setDatabasePopulator(populator);
|
||||
|
||||
return initializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link DataSource} to be exposed as a Spring bean.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract DataSource createDataSource();
|
||||
|
||||
/**
|
||||
* Callback to customize the {@link ResourceDatabasePopulator} before it will be applied to the {@link DataSource}. It
|
||||
* will be pre-populated with a SQL script derived from the name of the current test class and the activated Spring
|
||||
* profile.
|
||||
*
|
||||
* @param populator will never be {@literal null}.
|
||||
*/
|
||||
protected void customizePopulator(ResourceDatabasePopulator populator) {}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -17,36 +17,34 @@ package org.springframework.data.jdbc.testing;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
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;
|
||||
|
||||
/**
|
||||
* {@link DataSource} setup for HSQL.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Component
|
||||
@Configuration
|
||||
@Profile({ "hsql", "default" })
|
||||
class HsqlDataSourceFactoryBean extends DataSourceFactoryBean {
|
||||
class HsqlDataSourceConfiguration {
|
||||
|
||||
HsqlDataSourceFactoryBean(Class<?> testClass) {
|
||||
super(testClass);
|
||||
}
|
||||
@Autowired Class<?> context;
|
||||
|
||||
@Override
|
||||
String scriptSuffix() {
|
||||
return "hsql";
|
||||
}
|
||||
|
||||
@Override
|
||||
DataSource create(String scriptName) {
|
||||
@Bean
|
||||
DataSource dataSource() {
|
||||
|
||||
return new EmbeddedDatabaseBuilder() //
|
||||
.generateUniqueName(true) //
|
||||
.setType(EmbeddedDatabaseType.HSQL) //
|
||||
.setScriptEncoding("UTF-8") //
|
||||
.ignoreFailedDrops(true) //
|
||||
.addScript(scriptName) //
|
||||
.addScript(TestUtils.createScriptName(context, "hsql")) //
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.annotation.PostConstruct;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.jdbc.datasource.init.DataSourceInitializer;
|
||||
import org.springframework.jdbc.datasource.init.DatabasePopulator;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
|
||||
import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
|
||||
|
||||
/**
|
||||
* {@link DataSource} setup for MySQL.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("mysql")
|
||||
class MySqlDataSourceConfiguration extends DataSourceConfiguration {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.testing.DataSourceConfiguration#createDataSource()
|
||||
*/
|
||||
protected DataSource createDataSource() {
|
||||
|
||||
MysqlDataSource dataSource = new MysqlDataSource();
|
||||
dataSource.setUrl("jdbc:mysql:///test?user=root");
|
||||
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initDatabase() {
|
||||
|
||||
MysqlDataSource dataSource = new MysqlDataSource();
|
||||
dataSource.setUrl("jdbc:mysql:///?user=root");
|
||||
|
||||
ClassPathResource createScript = new ClassPathResource("create-mysql.sql");
|
||||
DatabasePopulator databasePopulator = new ResourceDatabasePopulator(createScript);
|
||||
|
||||
DataSourceInitializer initializer = new DataSourceInitializer();
|
||||
initializer.setDatabasePopulator(databasePopulator);
|
||||
initializer.setDataSource(dataSource);
|
||||
initializer.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.postgresql.ds.PGSimpleDataSource;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Profile;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
|
||||
/**
|
||||
* {@link DataSource} setup for PostgreSQL
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
@Profile("postgres")
|
||||
public class PostgresDataSourceConfiguration extends DataSourceConfiguration {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.testing.DataSourceConfiguration#createDataSource()
|
||||
*/
|
||||
protected DataSource createDataSource() {
|
||||
|
||||
PGSimpleDataSource ds = new PGSimpleDataSource();
|
||||
ds.setUrl("jdbc:postgresql:///postgres");
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.jdbc.testing.DataSourceFactoryBean#customizePopulator(org.springframework.jdbc.datasource.init.ResourceDatabasePopulator)
|
||||
*/
|
||||
@Override
|
||||
protected void customizePopulator(ResourceDatabasePopulator populator) {
|
||||
populator.setIgnoreFailedDrops(true);
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.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.jdbc.repository.support.JdbcRepositoryFactory;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
/**
|
||||
* Infrastructure configuration for integration tests.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Configuration
|
||||
@ComponentScan // To pick up configuration classes (per activated profile)
|
||||
public class TestConfiguration {
|
||||
|
||||
@Autowired DataSource dataSource;
|
||||
@Autowired ApplicationEventPublisher publisher;
|
||||
|
||||
@Bean
|
||||
JdbcRepositoryFactory jdbcRepositoryFactory() {
|
||||
return new JdbcRepositoryFactory(namedParameterJdbcTemplate(), publisher);
|
||||
}
|
||||
|
||||
@Bean
|
||||
NamedParameterJdbcTemplate namedParameterJdbcTemplate() {
|
||||
return new NamedParameterJdbcTemplate(dataSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
PlatformTransactionManager transactionManager() {
|
||||
return new DataSourceTransactionManager(dataSource);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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 org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility methods for testing.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface TestUtils {
|
||||
|
||||
/**
|
||||
* Returns the name of the SQL script to be loaded for the given test class and database type.
|
||||
*
|
||||
* @param testClass must not be {@literal null}.
|
||||
* @param databaseType must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
public static String createScriptName(Class<?> testClass, String databaseType) {
|
||||
|
||||
Assert.notNull(testClass, "Test class must not be null!");
|
||||
Assert.hasText(databaseType, "Database type must not be null or empty!");
|
||||
|
||||
return String.format("%s/%s-%s.sql", testClass.getPackage().getName(), testClass.getSimpleName(),
|
||||
databaseType.toLowerCase());
|
||||
}
|
||||
}
|
||||
16
src/test/resources/logback.xml
Normal file
16
src/test/resources/logback.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.data" level="error" />
|
||||
|
||||
<root level="error">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -1,4 +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))
|
||||
CREATE TABLE PrimitiveIdEntity (ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1) PRIMARY KEY, NAME VARCHAR(100))
|
||||
|
||||
@@ -1,2 +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));
|
||||
CREATE TABLE PrimitiveIdEntity (ID BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));
|
||||
|
||||
@@ -2,4 +2,4 @@ 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));
|
||||
CREATE TABLE PrimitiveIdEntity (ID SERIAL PRIMARY KEY, NAME VARCHAR(100));
|
||||
|
||||
@@ -1 +1 @@
|
||||
CREATE TABLE dummyentity ( idProp BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100))
|
||||
CREATE TABLE dummyentity ( idProp BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100))
|
||||
|
||||
@@ -1 +1 @@
|
||||
CREATE TABLE dummyentity (idProp BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));
|
||||
CREATE TABLE dummyentity (idProp BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100));
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
DROP TABLE dummyentity;
|
||||
CREATE TABLE dummyentity (idProp SERIAL PRIMARY KEY, NAME VARCHAR(100));
|
||||
CREATE TABLE dummyentity (idProp SERIAL PRIMARY KEY, NAME VARCHAR(100));
|
||||
|
||||
Reference in New Issue
Block a user