DefaultPersisterBuilder.java
package org.codefilarete.stalactite.engine.configurer.builder;
import java.util.function.Function;
import org.codefilarete.stalactite.dsl.entity.EntityMappingConfiguration;
import org.codefilarete.stalactite.dsl.entity.EntityMappingConfigurationProvider;
import org.codefilarete.stalactite.engine.EntityPersister;
import org.codefilarete.stalactite.engine.PersistenceContext;
import org.codefilarete.stalactite.engine.PersisterRegistry;
import org.codefilarete.stalactite.engine.PersisterRegistry.DefaultPersisterRegistry;
import org.codefilarete.stalactite.engine.runtime.ConfiguredRelationalPersister;
import org.codefilarete.stalactite.engine.runtime.EntityIsManagedByPersisterAsserter;
import org.codefilarete.stalactite.engine.runtime.OptimizedUpdatePersister;
import org.codefilarete.stalactite.sql.ConnectionConfiguration;
import org.codefilarete.stalactite.sql.Dialect;
import org.codefilarete.tool.Reflections;
public class DefaultPersisterBuilder {
private final Dialect dialect;
private final ConnectionConfiguration connectionConfiguration;
private final PersisterRegistry persisterRegistry;
public DefaultPersisterBuilder(PersistenceContext persistenceContext) {
this(persistenceContext.getDialect(), persistenceContext.getConnectionConfiguration(), new DefaultPersisterRegistry(persistenceContext.getPersisters()));
}
/**
*
* @param dialect the dialect to use to adapt SQL to the database
* @param connectionConfiguration connection information to adapt SQL or create Sequence for identifier generation
* @param persisterRegistry any existing {@link PersisterRegistry} that can provide any existing {@link org.codefilarete.stalactite.engine.EntityPersister}
* to be reused
*/
public DefaultPersisterBuilder(Dialect dialect, ConnectionConfiguration connectionConfiguration, PersisterRegistry persisterRegistry) {
this.dialect = dialect;
this.connectionConfiguration = connectionConfiguration;
this.persisterRegistry = persisterRegistry;
}
/**
* Builds a {@link EntityPersister} instance for the entity mapping configuration provided by given argument.
* An exception is thrown if an already existing {@link EntityPersister} for the entity type already exists
* (the check doesn't account for potential differences in mapping configurations of the entity type).
*
* @param <C> the type of the entity being persisted
* @param <I> the type of the identifier for the entity
* @param entityMappingConfiguration the provider for the entity mapping configuration used to construct the persister
* @return the constructed {@link ConfiguredRelationalPersister} instance
* @throws IllegalStateException if a persister for the entity type already exists
*/
public <C, I> ConfiguredRelationalPersister<C, I> build(EntityMappingConfigurationProvider<C, I> entityMappingConfiguration) {
return build(entityMappingConfiguration.getConfiguration());
}
/**
* Builds a {@link EntityPersister} instance for the given entity mapping configuration.
* An exception is thrown if an already existing {@link EntityPersister} for the entity type already exists
* (the check doesn't account for potential differences in mapping configurations of the entity type).
*
* @param <C> the type of the entity being persisted
* @param <I> the type of the identifier for the entity
* @param entityMappingConfiguration the configuration used to map the entity and construct the persister
* @return the constructed {@link ConfiguredRelationalPersister} instance for the given configuration
* @throws IllegalStateException if a persister for the entity type already exists
*/
public <C, I> ConfiguredRelationalPersister<C, I> build(EntityMappingConfiguration<C, I> entityMappingConfiguration) {
// If a persister already exists for the type, then we return it to manage the case of graph that declares twice / several times the same mapped type
// WARN : this does not take mapping configuration differences into account, so if configuration is different from previous one, since
// no check is done, then the very first persister is returned
EntityPersister<C, Object> existingPersister = persisterRegistry.getPersister(entityMappingConfiguration.getEntityType());
if (existingPersister != null) {
// we can cast because all persisters we registered implement the interface
throw new IllegalStateException("Persister already exists for " + Reflections.toString(entityMappingConfiguration.getEntityType()));
} else {
return doBuild(entityMappingConfiguration);
}
}
/**
* Builds a {@link EntityPersister} instance for the given entity mapping configuration or return the existing one if any.
* (the check doesn't account for potential differences in mapping configurations of the entity type).
* This method is not considered to be part of the public API, because the end user should be warned about duplicate
* entity persisters, thus, other official methods {@link #build(EntityMappingConfiguration)} are preferred.
*
* @param <C> the type of the entity being persisted
* @param <I> the type of the identifier for the entity
* @param entityMappingConfiguration the configuration used to map the entity and construct the persister
* @return the constructed {@link ConfiguredRelationalPersister} instance for the given configuration
*/
public <C, I> ConfiguredRelationalPersister<C, I> buildOrGiveExisting(EntityMappingConfiguration<C, I> entityMappingConfiguration) {
// If a persister already exists for the type, then we return it : case of graph that declares twice / several times same mapped type
// WARN : this does not take mapping configuration differences into account, so if configuration is different from previous one, since
// no check is done, then the very first persister is returned
EntityPersister<C, Object> existingPersister = persisterRegistry.getPersister(entityMappingConfiguration.getEntityType());
if (existingPersister != null) {
// we can cast because all persisters we registered implement the interface
return (ConfiguredRelationalPersister<C, I>) existingPersister;
} else {
return doBuild(entityMappingConfiguration);
}
}
private <C, I> ConfiguredRelationalPersister<C, I> doBuild(EntityMappingConfiguration<C, I> entityMappingConfiguration) {
ConfiguredRelationalPersister<C, I> result;
result = decorateWithUpdateOptimization((adaptedConnectionConfiguration) -> {
PersisterBuilderPipeline<C, I> persisterBuilderPipeline = new PersisterBuilderPipeline<>(dialect, adaptedConnectionConfiguration, persisterRegistry);
return persisterBuilderPipeline.build(entityMappingConfiguration);
});
result = decorateWithEntityManagementAsserter(result);
return result;
}
private <C, I> OptimizedUpdatePersister<C, I> decorateWithUpdateOptimization(Function<ConnectionConfiguration, ConfiguredRelationalPersister<C, I>> builderDelegate) {
// we wrap final result with some transversal features
// NB: Order of wrap is important due to invocation of instance methods with code like "this.doSomething(..)" in particular with OptimizedUpdatePersister
// which internally calls update(C, C, boolean) on update(id, Consumer): the latter method is not listened by EntityIsManagedByPersisterAsserter
// (because it has no purpose since entity is not given as argument) but update(C, C, boolean) is and should be, that is not the case if
// EntityIsManagedByPersisterAsserter is done first since OptimizedUpdatePersister invokes itself with "this.update(C, C, boolean)"
ConfiguredRelationalPersister<C, I> concretePersister = builderDelegate.apply(OptimizedUpdatePersister.wrapWithQueryCache(connectionConfiguration));
return new OptimizedUpdatePersister<>(concretePersister);
}
private <C, I> ConfiguredRelationalPersister<C, I> decorateWithEntityManagementAsserter(ConfiguredRelationalPersister<C, I> concretePersister) {
return new EntityIsManagedByPersisterAsserter<>(concretePersister);
}
}