io.devbench.uibuilder.spring.data.jpa.SpringJpaDataSourceProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of uibuilder-spring-jpa Show documentation
Show all versions of uibuilder-spring-jpa Show documentation
JPA support for the UIBuilder Data module
The newest version!
/*
*
* Copyright © 2018 Webvalto Ltd.
*
* 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 io.devbench.uibuilder.spring.data.jpa;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.jpa.impl.JPAQuery;
import io.devbench.uibuilder.components.util.datasource.DataSourceBindingContext;
import io.devbench.uibuilder.data.api.annotations.TargetDataSource;
import io.devbench.uibuilder.data.api.exceptions.DataSourceNotFoundException;
import io.devbench.uibuilder.spring.data.IdHandler;
import io.devbench.uibuilder.spring.data.SpringCommonDataSourceProvider;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.data.jpa.repository.JpaContext;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component;
import javax.persistence.EntityManager;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@Slf4j
@Component
public class SpringJpaDataSourceProvider extends SpringCommonDataSourceProvider, JpaRepository> implements IdHandler {
private final JpaContext jpaContext;
public Set> getInterestedAnnotationTypes() {
return Collections.singleton(TargetDataSource.class);
}
@Override
@SuppressWarnings("unchecked")
protected void lateInit() {
dataSourceRepositories = Collections.unmodifiableMap(
getAnnotatedClasses().getOrDefault(TargetDataSource.class, Collections.emptySet()).stream()
.filter(JpaRepository.class::isAssignableFrom)
.>>>map(repoClass ->
Pair.of(repoClass.getAnnotation(TargetDataSource.class), (Class extends JpaRepository>) repoClass))
.collect(Collectors.toMap(pair -> pair.getKey().name(), Function.identity()))
);
}
@Autowired
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
public SpringJpaDataSourceProvider(ApplicationContext applicationContext, JpaContext jpaContext) {
super(applicationContext);
this.jpaContext = jpaContext;
}
private Optional> tryToFindQueryByMethodNameInRepository(JpaRepository, ?> repository, String methodName) {
//TODO add an abstraction to the default query by using an annotation to name the method, to hide method names from frontend
try {
Object methodResult = repository.getClass().getDeclaredMethod(methodName).invoke(repository);
if (methodResult instanceof JPAQuery) {
JPAQuery> query = (JPAQuery>) methodResult;
return Optional.of(query);
} else {
log.error("Query type not supported:" + methodResult.getClass());
}
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
log.error(e.getMessage(), e); //TODO implement better error handling
}
return Optional.empty();
}
@Override
@SuppressWarnings("unchecked")
public SpringJpaDataSource createNewDataSource(@NotNull String dataSourceId, @Nullable String optionalDefaultQueryName) {
return findDataSourceNameById(dataSourceId).map(repo -> {
final DataSourceBindingContext bindings = (DataSourceBindingContext)
registeredBindingContexts.get(createBindingsKey(dataSourceId, optionalDefaultQueryName));
final Class entityType = (Class) tryToFindEntityClassByRepositoryClass(repo.getValue())
.orElseThrow(IllegalStateException::new);
final PathBuilder> queryPath = new PathBuilder<>(entityType, entityType.getSimpleName());
final EntityManager entityManager = jpaContext.getEntityManagerByManagedType(entityType);
final Supplier> query = buildJpaQuerySupplier(optionalDefaultQueryName, repo, entityManager, queryPath);
final Supplier> base = buildJpaQuerySupplier(null, repo, entityManager, queryPath);
final List keyPaths = getKeyPaths(repo.getKey(), entityType);
return new SpringJpaDataSource<>(bindings, queryPath, keyPaths, entityType, query, base);
}).orElseThrow(() -> new DataSourceNotFoundException("Couldn't find data source with id:" + dataSourceId
+ " name: " + idToDataSourceName.get(dataSourceId)));
}
private Supplier> buildJpaQuerySupplier(@Nullable String optionalDefaultQueryName,
Pair>> targetDSRepo,
EntityManager entityManager, PathBuilder> queryPath) {
final JpaRepository, ?> repository = applicationContext.getBean(targetDSRepo.getValue());
return () -> Optional
.ofNullable(optionalDefaultQueryName)
.flatMap(defaultQuery -> this.tryToFindQueryByMethodNameInRepository(repository, defaultQuery)
.>map(query -> query.clone(entityManager)))
.orElse(new JPAQuery<>(entityManager).from(queryPath));
}
}