io.crnk.jpa.JpaModule Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of crnk-jpa Show documentation
Show all versions of crnk-jpa Show documentation
JSON API framework for Java
package io.crnk.jpa;
import io.crnk.jpa.internal.JpaRepositoryBase;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import io.crnk.core.engine.dispatcher.Response;
import io.crnk.core.engine.error.ExceptionMapper;
import io.crnk.core.engine.filter.AbstractDocumentFilter;
import io.crnk.core.engine.filter.DocumentFilterChain;
import io.crnk.core.engine.filter.DocumentFilterContext;
import io.crnk.core.engine.information.resource.ResourceField;
import io.crnk.core.engine.information.resource.ResourceFieldType;
import io.crnk.core.engine.information.resource.ResourceInformation;
import io.crnk.core.engine.information.resource.ResourceInformationProvider;
import io.crnk.core.engine.internal.utils.ClassUtils;
import io.crnk.core.engine.internal.utils.ExceptionUtil;
import io.crnk.core.engine.internal.utils.PreconditionUtil;
import io.crnk.core.engine.properties.PropertiesProvider;
import io.crnk.core.engine.transaction.TransactionRunner;
import io.crnk.core.module.InitializingModule;
import io.crnk.core.queryspec.QuerySpec;
import io.crnk.core.repository.RelationshipRepositoryV2;
import io.crnk.core.repository.ResourceRepositoryV2;
import io.crnk.core.repository.decorate.RelationshipRepositoryDecorator;
import io.crnk.core.repository.decorate.RepositoryDecoratorFactory;
import io.crnk.core.repository.decorate.ResourceRepositoryDecorator;
import io.crnk.core.resource.meta.DefaultHasMoreResourcesMetaInformation;
import io.crnk.core.resource.meta.DefaultPagedMetaInformation;
import io.crnk.jpa.internal.JpaRequestContext;
import io.crnk.jpa.internal.JpaResourceInformationProvider;
import io.crnk.jpa.internal.OptimisticLockExceptionMapper;
import io.crnk.jpa.internal.PersistenceExceptionMapper;
import io.crnk.jpa.internal.PersistenceRollbackExceptionMapper;
import io.crnk.jpa.internal.query.backend.querydsl.QuerydslQueryImpl;
import io.crnk.jpa.meta.JpaMetaProvider;
import io.crnk.jpa.meta.MetaEntity;
import io.crnk.jpa.meta.internal.JpaMetaEnricher;
import io.crnk.jpa.query.JpaQueryFactory;
import io.crnk.jpa.query.JpaQueryFactoryContext;
import io.crnk.jpa.query.querydsl.QuerydslQueryFactory;
import io.crnk.jpa.query.querydsl.QuerydslRepositoryFilter;
import io.crnk.jpa.query.querydsl.QuerydslTranslationContext;
import io.crnk.jpa.query.querydsl.QuerydslTranslationInterceptor;
import io.crnk.meta.MetaLookup;
import io.crnk.meta.MetaModuleExtension;
import io.crnk.meta.provider.MetaPartition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Crnk module that adds support to expose JPA entities as repositories. It
* supports:
*
*
* - Sorting
* - Filtering
* - Access to relationships for any operation (sorting, filtering, etc.)
* - Includes for relationships
* - Paging
* - Mapping to DTOs
* - Criteria API and QueryDSL support
* - Computed attributes that map JPA Criteria/QueryDSL expressions to DTO
* attributes
* - JpaRepositoryFilter to customize the repositories
* - Client and server support
* - No need for crnk annotations by default. Reads the entity
* annotations.
*
*
*
* Not supported so far:
*
*
* - Selection of fields, always all fields are returned.
* - Sorting and filtering on related resources. Consider doing separate
* requests on the relations where necessary.
*
*/
public class JpaModule implements InitializingModule {
private static final String MODULE_NAME = "jpa";
private Logger logger = LoggerFactory.getLogger(JpaModule.class);
private EntityManagerFactory emFactory;
private EntityManager em;
private ResourceInformationProvider resourceInformationProvider;
private TransactionRunner transactionRunner;
private ModuleContext context;
private JpaModuleConfig config;
private JpaMetaEnricher metaEnricher;
private MetaLookup jpaMetaLookup;
private JpaMetaProvider jpaMetaProvider;
/**
* Constructor used on client side.
*/
// protected for CDI
protected JpaModule() {
}
/**
* Constructor used on server side.
*/
private JpaModule(JpaModuleConfig config, EntityManagerFactory emFactory, EntityManager em, TransactionRunner
transactionRunner) {
this();
this.config = config;
this.emFactory = emFactory;
this.em = em;
this.transactionRunner = transactionRunner;
}
/**
* Creates a new JpaModule for a Crnk client.
*
* @return module
*/
public static JpaModule newClientModule() {
return new JpaModule();
}
/**
* Creates a new JpaModule for a Crnk server. No entities are by
* default exposed as JSON API resources. Make use of
* {@link #addRepository(JpaRepositoryConfig)} to add resources.
*
* @param em to use
* @param transactionRunner to use
* @return created module
* @deprecated use with JpaModuleConfig
*/
@Deprecated
public static JpaModule newServerModule(EntityManager em, TransactionRunner transactionRunner) {
return new JpaModule(new JpaModuleConfig(), null, em, transactionRunner);
}
/**
* Creates a new JpaModule for a Crnk server. All entities managed by
* the provided EntityManagerFactory are registered to the module and
* exposed as JSON API resources if not later configured otherwise.
*
* @param emFactory to retrieve the managed entities.
* @param em to use
* @param transactionRunner to use
* @return created module
* @deprecated use with JpaModuleConfig
*/
@Deprecated
public static JpaModule newServerModule(EntityManagerFactory emFactory, EntityManager em,
TransactionRunner transactionRunner) {
JpaModuleConfig config = new JpaModuleConfig();
config.exposeAllEntities(emFactory);
return new JpaModule(config, emFactory, em, transactionRunner);
}
/**
* Creates a new JpaModule for a Crnk server. No entities are by
* default exposed as JSON API resources. Make use of
* {@link #addRepository(JpaRepositoryConfig)} to add resources.
*
* @param em to use
* @param transactionRunner to use
* @return created module
*/
public static JpaModule createServerModule(JpaModuleConfig config, EntityManager em, TransactionRunner transactionRunner) {
return new JpaModule(config, null, em, transactionRunner);
}
/**
* Adds the given filter to this module. Filter will be used by all
* repositories managed by this module.
*
* @param filter to add
* @deprecated use {@link JpaModuleConfig}
*/
@Deprecated
public void addFilter(JpaRepositoryFilter filter) {
config.addFilter(filter);
}
/**
* @deprecated use {@link JpaModuleConfig}
*/
@Deprecated
public void removeFilter(JpaRepositoryFilter filter) {
config.removeFilter(filter);
}
/**
* @deprecated use {@link JpaModuleConfig}
*/
@Deprecated
public List getFilters() {
return config.getFilters();
}
/**
* @deprecated use {@link JpaModuleConfig}
*/
@Deprecated
public void setRepositoryFactory(JpaRepositoryFactory repositoryFactory) {
checkNotInitialized();
this.config.setRepositoryFactory(repositoryFactory);
}
/**
* @deprecated use {@link JpaModuleConfig}
*/
@Deprecated
public Set> getResourceClasses() {
return config.getResourceClasses();
}
/**
* @deprecated use {@link JpaModuleConfig}
*/
@Deprecated
public void addRepository(JpaRepositoryConfig config) {
checkNotInitialized();
this.config.addRepository(config);
}
/**
* Removes the resource with the given type from this module.
*
* @param resourse class (entity or mapped dto)
* @param resourceClass to remove
*/
public void removeRepository(Class resourceClass) {
checkNotInitialized();
config.removeRepository(resourceClass);
}
/**
* Removes all entity classes registered by default. Use
* {@link #addRepository(JpaRepositoryConfig)} (Class)} or
* classes manually.
*/
public void removeRepositories() {
checkNotInitialized();
config.removeRepositories();
}
@Override
public String getModuleName() {
return MODULE_NAME;
}
private void checkNotInitialized() {
PreconditionUtil.assertNull("module is already initialized, no further changes can be performed", context);
}
@Override
public void setupModule(ModuleContext context) {
this.context = context;
Set jpaTypes = new HashSet<>();
if (config != null) {
for (JpaRepositoryConfig> config : config.getRepositories()) {
jpaTypes.add(config.getEntityClass());
}
}
jpaMetaProvider = new JpaMetaProvider(jpaTypes);
jpaMetaLookup = new MetaLookup();
jpaMetaLookup.addProvider(jpaMetaProvider);
jpaMetaLookup.setModuleContext(context);
jpaMetaLookup.initialize();
if (config != null) {
initQueryFactory();
}
context.addResourceInformationBuilder(getResourceInformationProvider(context.getPropertiesProvider()));
context.addExceptionMapper(new OptimisticLockExceptionMapper());
context.addExceptionMapper(new PersistenceExceptionMapper(context));
context.addExceptionMapper(new PersistenceRollbackExceptionMapper(context));
addHibernateConstraintViolationExceptionMapper();
addTransactionRollbackExceptionMapper();
context.addRepositoryDecoratorFactory(new JpaRepositoryDecoratorFactory());
if (em != null) {
metaEnricher = new JpaMetaEnricher();
// enrich resource meta model with JPA information where incomplete
MetaModuleExtension metaModuleExtension = new MetaModuleExtension();
metaModuleExtension.addProvider(metaEnricher.getProvider());
context.addExtension(metaModuleExtension);
setupTransactionMgmt();
}
}
private void initQueryFactory() {
JpaQueryFactory queryFactory = config.getQueryFactory();
queryFactory.initalize(new JpaQueryFactoryContext() {
@Override
public EntityManager getEntityManager() {
return em;
}
@Override
public MetaPartition getMetaPartition() {
return jpaMetaProvider.getPartition();
}
});
if (queryFactory instanceof QuerydslQueryFactory) {
QuerydslQueryFactory querydslFactory = (QuerydslQueryFactory) queryFactory;
querydslFactory.addInterceptor(new JpaQuerydslTranslationInterceptor());
}
}
@Override
public void init() {
if (em != null) {
setupServerRepositories();
}
}
private void addHibernateConstraintViolationExceptionMapper() {
// may not be available depending on environment
if (ClassUtils.existsClass("org.hibernate.exception.ConstraintViolationException")) {
ExceptionUtil.wrapCatchedExceptions(new Callable
© 2015 - 2025 Weber Informatics LLC | Privacy Policy