OneToOneRelationConfigurer.java
package org.codefilarete.stalactite.engine.configurer.onetoone;
import org.codefilarete.reflection.AccessorDefinition;
import org.codefilarete.stalactite.dsl.entity.EntityMappingConfiguration;
import org.codefilarete.stalactite.dsl.naming.ForeignKeyNamingStrategy;
import org.codefilarete.stalactite.dsl.naming.JoinColumnNamingStrategy;
import org.codefilarete.stalactite.dsl.naming.TableNamingStrategy;
import org.codefilarete.stalactite.dsl.naming.UniqueConstraintNamingStrategy;
import org.codefilarete.stalactite.engine.configurer.AbstractRelationConfigurer;
import org.codefilarete.stalactite.engine.configurer.EntityMappingConfigurationWithTable;
import org.codefilarete.stalactite.engine.configurer.builder.PersisterBuilderContext;
import org.codefilarete.stalactite.engine.runtime.ConfiguredRelationalPersister;
import org.codefilarete.stalactite.sql.ConnectionConfiguration;
import org.codefilarete.stalactite.sql.Dialect;
import org.codefilarete.stalactite.sql.ddl.structure.Column;
import org.codefilarete.stalactite.sql.ddl.structure.Table;
import org.codefilarete.tool.collection.Iterables;
import static org.codefilarete.tool.Nullable.nullable;
/**
* Wrapper for {@link OneToOneOwnedBySourceConfigurer} and {@link OneToOneOwnedByTargetConfigurer} to make them available for cycle.
*
* @param <C> type of input (left/source entities)
* @param <I> identifier type of source entities
* @param <TRGT> type of output (right/target entities)
* @param <TRGTID>> identifier type of target entities
* @author Guillaume Mary
*/
public class OneToOneRelationConfigurer<C, I, TRGT, TRGTID> extends AbstractRelationConfigurer<C, I, TRGT, TRGTID> {
private final JoinColumnNamingStrategy joinColumnNamingStrategy;
private final ForeignKeyNamingStrategy foreignKeyNamingStrategy;
private final UniqueConstraintNamingStrategy uniqueConstraintNamingStrategy;
public OneToOneRelationConfigurer(Dialect dialect,
ConnectionConfiguration connectionConfiguration,
ConfiguredRelationalPersister<C, I> sourcePersister,
TableNamingStrategy tableNamingStrategy,
JoinColumnNamingStrategy joinColumnNamingStrategy,
ForeignKeyNamingStrategy foreignKeyNamingStrategy,
UniqueConstraintNamingStrategy uniqueConstraintNamingStrategy,
PersisterBuilderContext currentBuilderContext) {
super(dialect, connectionConfiguration, sourcePersister, tableNamingStrategy, currentBuilderContext);
this.joinColumnNamingStrategy = joinColumnNamingStrategy;
this.foreignKeyNamingStrategy = foreignKeyNamingStrategy;
this.uniqueConstraintNamingStrategy = uniqueConstraintNamingStrategy;
}
/**
* Setup given one-to-one relation by adding write cascade as well as creating appropriate joins in
*
* @param oneToOneRelation the relation to be configured
*/
public void configure(OneToOneRelation<C, TRGT, TRGTID> oneToOneRelation) {
OneToOneConfigurerTemplate<C, TRGT, I, TRGTID, ?, ?, I> configurer;
if (oneToOneRelation.isRelationOwnedByTarget()) {
configurer = new OneToOneOwnedByTargetConfigurer<>(sourcePersister, oneToOneRelation, joinColumnNamingStrategy, foreignKeyNamingStrategy, uniqueConstraintNamingStrategy, dialect, connectionConfiguration);
} else {
configurer = new OneToOneOwnedBySourceConfigurer<>(sourcePersister, oneToOneRelation, joinColumnNamingStrategy, foreignKeyNamingStrategy, uniqueConstraintNamingStrategy);
}
String relationName = AccessorDefinition.giveDefinition(oneToOneRelation.getTargetProvider()).getName();
EntityMappingConfiguration<TRGT, TRGTID> targetMappingConfiguration = oneToOneRelation.getTargetMappingConfiguration();
if (currentBuilderContext.isCycling(targetMappingConfiguration)) {
// cycle detected
// we had a second phase load because cycle can hardly be supported by simply joining things together because at one time we will
// fall into infinite loop (think to SQL generation of a cycling graph ...)
Class<TRGT> targetEntityType = targetMappingConfiguration.getEntityType();
// adding the relation to an eventually already existing cycle configurer for the entity
OneToOneCycleConfigurer<TRGT> cycleSolver = (OneToOneCycleConfigurer<TRGT>)
Iterables.find(currentBuilderContext.getBuildLifeCycleListeners(), p -> p instanceof OneToOneCycleConfigurer && ((OneToOneCycleConfigurer<?>) p).getEntityType() == targetEntityType);
if (cycleSolver == null) {
cycleSolver = new OneToOneCycleConfigurer<>(targetEntityType);
currentBuilderContext.addBuildLifeCycleListener(cycleSolver);
}
cycleSolver.addCycleSolver(relationName, configurer);
} else {
// please note that even if no table is found in configuration, build(..) will create one
Table targetTable = determineTargetTable(oneToOneRelation);
ConfiguredRelationalPersister<TRGT, TRGTID> targetPersister = persisterBuilder.buildOrGiveExisting(new EntityMappingConfigurationWithTable<>(targetMappingConfiguration, targetTable));
// we replace dot character by underscore one to take embedded relation properties into account: their accessor is an AccessorChain
// which is printed with dots by AccessorDefinition
String tableAlias = relationName.replace('.', '_');
configurer.configure(tableAlias, targetPersister, oneToOneRelation.isFetchSeparately());
}
}
private Table determineTargetTable(OneToOneRelation<C, TRGT, TRGTID> oneToOneRelation) {
EntityMappingConfiguration<TRGT, TRGTID> targetMappingConfiguration = oneToOneRelation.getTargetMappingConfiguration();
Table targetTable = nullable(targetMappingConfiguration.getTable()).getOr(nullable(oneToOneRelation.getReverseColumn()).map(Column::getTable).get());
if (targetTable == null) {
targetTable = lookupTableInRegisteredPersisters(targetMappingConfiguration.getEntityType());
}
return targetTable;
}
}