StalactiteRepositoryFactoryBean.java
package org.codefilarete.stalactite.spring.repository;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.codefilarete.stalactite.engine.EntityPersister;
import org.codefilarete.stalactite.engine.PersistenceContext;
import org.codefilarete.stalactite.engine.runtime.AdvancedEntityPersister;
import org.codefilarete.stalactite.engine.runtime.ConfiguredPersister;
import org.codefilarete.stalactite.engine.runtime.ConfiguredRelationalPersister;
import org.codefilarete.stalactite.engine.runtime.OptimizedUpdatePersister;
import org.codefilarete.stalactite.engine.runtime.PersisterWrapper;
import org.codefilarete.tool.Reflections;
import org.codefilarete.tool.reflect.MethodDispatcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
/**
* Mimics {@link org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean}
*
* @param <R> repository type
* @param <C> entity type the repository persists
* @param <I> entity identifier type
*/
public class StalactiteRepositoryFactoryBean<R extends Repository<C, I>, C, I>
extends TransactionalRepositoryFactoryBeanSupport<R, C, I> {
public static <C, I> AdvancedEntityPersister<C, I> asInternalPersister(EntityPersister<C, I> foundPersister) {
// Converting found EntityPersister to an AdvancedEntityPersister
// This is hideous : due to the will to not expose AdvancedEntityPersister to the outside world, but combined to the need to use it and
// the fact that its implementing classes are hidden by several layers of interfaces, with "dig" into given result to find them
// and wrap the result into a proxy that dispatch called methods accordingly.
ConfiguredRelationalPersister<C, I> deepestDelegate = ((PersisterWrapper<C, I>) foundPersister).getDeepestDelegate();
MethodDispatcher methodDispatcher = new MethodDispatcher();
// Please note that order of precedence has an impact on getting a working result or not because AdvancedEntityPersister already extends
// ConfiguredPersister (yes, that's awful, but I couldn't find a better way without the constraint of not exposing AdvancedEntityPersister)
methodDispatcher
.redirect(AdvancedEntityPersister.class, (AdvancedEntityPersister<C, I>) deepestDelegate)
.redirect(ConfiguredPersister.class, (ConfiguredPersister<C, I>) foundPersister);
return methodDispatcher.build(AdvancedEntityPersister.class);
}
private Class<?> entityType;
private PersistenceContext persistenceContext;
/**
* Creates a new {@link StalactiteRepositoryFactoryBean} for the given repository interface.
*
* @param repositoryInterface must not be {@literal null}.
*/
protected StalactiteRepositoryFactoryBean(Class<? extends R> repositoryInterface) {
super(repositoryInterface);
for (Type genericInterface : repositoryInterface.getGenericInterfaces()) {
if (genericInterface instanceof ParameterizedType && ((ParameterizedType) genericInterface).getRawType() instanceof Class) {
if (StalactiteRepository.class.isAssignableFrom((Class<?>) ((ParameterizedType) genericInterface).getRawType())) {
Type persistedType = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
if (persistedType instanceof Class) {
this.entityType = (Class<?>) persistedType;
}
}
}
}
if (this.entityType == null) {
throw new UnsupportedOperationException("Entity type can't be deduced : repository class has unsupported generic type, must be a class");
}
}
@Autowired
public void setPersistenceContext(PersistenceContext persistenceContext) {
this.persistenceContext = persistenceContext;
}
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
applicationContext.getBeansOfType(EntityPersister.class);
}
@Override
protected RepositoryFactorySupport doCreateRepositoryFactory() {
EntityPersister<?, Object> foundPersister = persistenceContext.findPersister(this.entityType);
if (foundPersister == null) {
throw new IllegalArgumentException("No persister found for entityType " + Reflections.toString(entityType) + " in persistence context.");
}
return new StalactiteRepositoryFactory(asInternalPersister(foundPersister), persistenceContext.getDialect(), persistenceContext.getConnectionProvider());
}
}