I have made this class which creates bundle from database records.
The records are got from stored procedures and names of this procedures are taken from ServiceConfig instance which is Spring #Configuration.
Now I use java Map to bind each DB entity class with according ServiceConfig method.
I would like to extend it and use Enum instead of procNameSupplier here. Is it possible to access ServiceConfig from enum?
#Component
public class BundleMaker {
#PersistenceContext
private final EntityManager entityManager;
private final Map<Class<? extends IRecord>, Supplier<String>> procNameSupplier = new HashMap<>();
public BundleMaker(EntityManager entityManager, ServiceConfig config) {
this.entityManager = entityManager;
procNameSupplier.put(MainDepRecord.class, config::mainDepProc);
//...
}
public <T extends IRecord> Bundle bundle(Class<T> cl) {
StoredProcedureQuery query;
query = entityManager.createStoredProcedureQuery(procNameSupplier.get(cl).get(), cl)
.registerStoredProcedureParameter(1, cl, REF_CURSOR);
List<T> resultList = query.getResultList();
List<Resource> resources = resultList.stream()
.map(T::toResource)
.collect(Collectors.toList());
return new Bundle(resources);
}
}
Related
I heard about EntityManager is not thread-safe, so we use #PersistenceContext instead of #Autowired.
However, I found about two ways to create JPAQueryFactory on the Internet.
define a bean like this
#Configuration
public class JPAQueryFactoryConfiguration {
#Bean
public JPAQueryFactory jpaQueryFactory(#Autowired EntityManager entityManager) {
return new JPAQueryFactory(entityManager);
}
}
#PersistenceContext on EntityManager ,new JPAQueryFactory(entityManager) every time
#Service
public class TestServiceImpl implements TestService {
#PersistenceContext
private EntityManager entityManager;
#Override
public void test(){
JPAQueryFactory jpaQueryFactory = new JPAQueryFactory(entityManager);
xxxxxxxxxxxx
}
}
I'm counfused. But, I think the first way is not correct according to the difference between #PersistenceContext and #Autowired
i am just biginer in EJB ,
type of JPAQueryFactory is Provider
is not EntityManager , just Cast
JPAQueryFactory query = new JPAQueryFactory((Provider<EntityManager>) em);
or use the declaration of JPAQueryFactory
EntityManagerFactory emf =
Persistence.createEntityManagerFactory("com.baeldung.querydsl.intro");
EntityManager em = entityManagerFactory.createEntityManager();
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
As this article shows, you need to use both the entity manager and JPAQueryFactory as follows:
Extend SimpleJpaRepository and implement BaseRepository
BaseRepository is an interface that extends JpaRepository
public abstract class BaseRepositoryImpl<T, ID>
extends SimpleJpaRepository<T, ID>
implements BaseRepository<T, ID> {
EntityManager em;
JPAQueryFactory queryFactory;
public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
super(domainClass, em);
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
}
and create a class that extends your BaseRepositoryImpl to use JPAQueryFactory in it.
in my session i need to call 2 diffrent procedures which are on db1 and db2. it works fine when i call them separetely but it fails when i call them in same session. it fetches first data correctly but it fails on second call since it looks for the second procedure on db1 although entitiymanager's datasource changed correctly.
what am i missing?
here is code snippet
#Repository
#Transactional
public class DB1Dao {
#PersistenceContext()
private EntityManager entityManager;
#SuppressWarnings("unchecked")
public Model1 getData(String param1) {
.....
}
}
#Repository
#Transactional
public class DB2Dao {
#PersistenceContext()
private EntityManager entityManager;
#SuppressWarnings("unchecked")
public Model2 getData(String param2) {
.....
}
}
#Autowired
private DB1Dao dao1;
#Autowired
private DB2Dao dao2;
#RequestMapping(value = "/inquiry", method = RequestMethod.POST)
public ResponseEntity<Object> inquiryService(#RequestBody InquiryRequest inquiryRequest){
....
Model1 model1 = dao1.getData(param2); // success
....
Model2 model 2 = dao2.getData(param2); // fails since it looks for second procedure on db1
}
You didn't show any persistence-related configuration info, but I'd guess you should differentiate your repositories by properly naming them:
#PersistenceContext(unitName = "db1PersitenceUnitName")
private EntityManager entityManager;
I am trying to execute group by query with Spring Data JPA and Query DSL.
But, I am getting following exception:-
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'japanWHTDaoImpl':
Unsatisfied dependency expressed through field 'wht21940000DataRepo';
nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'japanWHT21940000DataRepository':
Invocation of init method failed; nested exception is java.util.NoSuchElementException
I tried to write custom repository implementation and giving below my interfaces and impl classes:
Custom interface:
public interface JapanWHT21940000DataRepositoryCustom {
List<WHT21940000Royalties> findLocalCcyAmtsByRemarks();
}
Custom Impl class:
#Repository
#Transactional
public class JapanWHT21940000DataRepositoryCustomImpl extends QueryDslRepositorySupport implements JapanWHT21940000DataRepositoryCustom {
public JapanWHT21940000DataRepositoryCustomImpl(Class<?> domainClass) {
super(domainClass);
// TODO Auto-generated constructor stub
}
#PersistenceContext
private EntityManager entityManager;
#Override
public List<WHT21940000Royalties> findLocalCcyAmtsByRemarks() {
QWHT21940000Data wht21940000Data = QWHT21940000Data.wHT21940000Data;
JPAQuery<WHT21940000Royalties> query = new JPAQuery<WHT21940000Royalties>(entityManager);
query.from(wht21940000Data).groupBy(wht21940000Data.remarks).select(wht21940000Data.remarks, wht21940000Data.localCcyAmt.sum());
return null;
}
}
Spring data JPA interface:
public interface JapanWHT21940000DataRepository
extends JpaRepository<WHT21940000Data, Long>,
QueryDslPredicateExecutor<WHT21940000Data>,
JapanWHT21940000DataRepositoryCustom {
}
and in DAO class:
#Repository
#Transactional("japanWhtTransactionManager")
public class JapanWHTDaoImpl implements JapanWHTDao {
#Autowired
JapanWHT21940000DataRepository wht21940000DataRepo;
// more code to follow...
EDIT: Or is there a simpler and better way to do group by query in Spring data JPA + Query DSL than what I am trying?
I think that the real issue is executing custom querydsl queries from JpaRepositories, though I have to suggest using a custom extended base repository class, as following.
First, comes the extended base repository class.
#NoRepositoryBean
public interface ExtendedQueryDslJpaRepository<T, ID extends Serializable> extends JpaRepository<T, ID>, QueryDslPredicateExecutor<T> {
<T1> Page<T1> findAll(JPQLQuery jpqlQuery, Pageable pageable);
}
And related implementation.
public class ExtendedQueryDslJpaRepositoryImpl<T, ID extends Serializable>
extends QueryDslJpaRepository<T, ID> implements ExtendedQueryDslJpaRepository<T, ID> {
private static final EntityPathResolver DEFAULT_ENTITY_PATH_RESOLVER = SimpleEntityPathResolver.INSTANCE;
private final EntityPath<T> path;
private final PathBuilder<T> builder;
private final Querydsl querydsl;
private EntityManager entityManager;
public ExtendedQueryDslJpaRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager) {
this(entityInformation, entityManager, DEFAULT_ENTITY_PATH_RESOLVER);
}
public ExtendedQueryDslJpaRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, EntityManager entityManager, EntityPathResolver
resolver) {
super(entityInformation, entityManager);
this.path = resolver.createPath(entityInformation.getJavaType());
this.builder = new PathBuilder(this.path.getType(), this.path.getMetadata());
this.querydsl = new Querydsl(entityManager, this.builder);
this.entityManager = entityManager;
}
#Override
public <T1> Page<T1> findAll(JPQLQuery jpqlQuery, Pageable pageable) {
final JPQLQuery<?> countQuery = jpqlQuery;
JPQLQuery<T1> query = querydsl.applyPagination(pageable, jpqlQuery);
return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount);
}
}
The above classes could be placed in a configuration package.
Then, we define ExtendedQueryDslJpaRepositoryImpl as default class from which JpaRepository classes should extend, as following:
#Configuration
#EnableJpaRepositories(basePackageClasses = Application.class, repositoryBaseClass = ExtendedQueryDslJpaRepositoryImpl.class)
public class JpaConfig {
#PersistenceContext
private EntityManager entityManager;
#Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
The next step is to define a repository for an Entity of the application, eg. CustomEntity.
public interface CustomRepository extends ExtendedQueryDslJpaRepository<CustomEntity, Long>, CustomRepositorySupport {
}
Next we define interface CustomRepositorySupport for the custom methods definition.
public interface CustomRepositorySupport {
JPQLQuery<CustomListDto> createCustomIndexQuery();
}
And finally the custom repository implementation.
#Repository
public class CustomRepositoryImpl implements CustomRepositorySupport {
private JPAQueryFactory queryFactory;
#Autowired
public CustomRepositoryImpl(JPAQueryFactory queryFactory) {
this.queryFactory = queryFactory;
}
#Override
public JPQLQuery<CustomListDto> createCustomIndexQuery() {
QCustomEntity qCustomEntity = QCustomEntity.customEntity;
BooleanBuilder predicate = new BooleanBuilder();
// Create predicate as desired
// predicate.and(...);
// Create projection of fields
/* FactoryExpression<CustomListDto> factoryExpression = Projections.bean(CustomListDto.class,
qCustomEntity.fieldA,
qCustomEntity.fieldB,
qCustomEntity.fieldC,
qCustomEntity.fieldD,
qCustomEntity.fieldE,
qCustomEntity.fieldF); */
return queryFactory.from(qCustomEntity).select(factoryExpression).where(predicate);
}
}
And the final step is to actually call the method from a Service class as following.
public interface CustomFinder {
Page<CustomListDto> findIndex(Pageable pageable);
}
-
#Service
public class CustomFinderImpl implements CustomFinder {
private CustomRepository customRepository;
#Autowired
public CustomFinderImpl(CustomRepository customRepository) {
this.customRepository = customRepository;
}
#Override
public Page<CustomListDto> findIndex(Pageable pageable) {
JPQLQuery<CustomListDto> query = customRepository.createCustomIndexQuery();
return customRepository.findAll(query, pageable);
}
}
The same way we implemented public <T1> List<T1> findAll(JPQLQuery query, FactoryExpression<T1> factoryExpression), we can simply implement required desirable methods as public <T1> List<T1> findAll(JPQLQuery query, FactoryExpression<T1> factoryExpression), <T1> List<T1> findList(JPQLQuery query, FactoryExpression<T1> factoryExpression) etc.
This way, we add capabilities to all our repositories, in order to take advantage of all types of querydsl queries, not only group by or projections.
It might seem a little over-complicated, but if you choose to implement it, and see how it works in action, you would stick to it. It is being used successfully in real apps, providing access to projection queries (without loading full entities where they are not needed), paging queries, exists subqueries etc.
Finally, using a kind of central base repository class is a kind of reusable code which is written only once.
Hope that helps.
I've setup a basic spring project with a single database connection.
In the application.properties file I have the database settings:
spring.datasource.url = jdbc:mysql://192.168.1.19/ticket
spring.datasource.username = dbusername
spring.datasource.password = dbpassword
I've created a base DAO class which other DAOs extend:
#Transactional
public class Dao<E> {
#PersistenceContext
private EntityManager entityManager;
private Class<E> entityClass;
public Dao(Class<E> entityClass) {
this.entityClass = entityClass;
}
public void create(E object) {
entityManager.persist(object);
return;
}
public void delete(E object) {
if (entityManager.contains(object)) {
entityManager.remove(object);
} else {
entityManager.remove(entityManager.merge(object));
}
return;
}
#SuppressWarnings("unchecked")
public List<E> getAll() {
return entityManager.createQuery("from " + entityClass.getName()).getResultList();
}
public E get(long id) {
return entityManager.find(entityClass, id);
}
public void update(E object) {
entityManager.merge(object);
return;
}
}
Here's a sample entity that extends the base DAO:
#Repository
public class PersonDao extends Dao<Person> {
public PersonDao() {
super(Person.class);
}
}
Currently this uses a single database, but I need to be able to add a second database, and somehow define in each DAO which datasource to use. Each DAO will only use a single database, so there's no requirement for a DAO to be able to connect to multiple databases.
I've done some research, and that seems to suggest I need to use JdbcTemplate? but I can't seem to find a tutorial that matches my need. Also, at the minute the entityManager is injected into the DAO, but the JdbcTemplate examples I've looked at don't seem to use the entityManager, which is slightly confusing.
database.password1=<password1>
database.url1=jdbc\:mysql\://localhost\:3306/twodbone
database.username1=<username1>
database.password2=<password1>
database.url2=jdbc\:mysql\://localhost\:3306/twodbtwo
database.username2=<username2>
database.driverClassName=com.mysql.jdbc.Driver
In this way you can add the multiple databases and configure both hibernate.cfg.xml file and applicationContext.xml file also..
#Repository
public class FooRepository
{
#PersistenceContext
private EntityManager entityManager;
#Autowired(required = true)
private JdbcTemplate jdbcTemplate;
public void saveFoo(Foo foo)
{
this.entityManager.persist(foo);
}
public List<SomeReportPojo> getSomeReport()
{
return this.entityManager.queryForList("SELECT .. ",SomeProjectPojo.class);
}
}
this.jdbcTemplate should be kept rather than this.entityManager for jdbc templetes
this is simple example
Since I got no answer to my previous question I tried to tweak the example given in the Spring documentation for customizing repositories. There ist a Method getRepository(Class repositoryInterface) which looks like It ist the right place to map my repository Overrides:
public class MyRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable>
extends JpaRepositoryFactoryBean<R, T, I> {
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
return new MyRepositoryFactory<>(entityManager);
}
private static class MyRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory {
private EntityManager entityManager;
#Resource
private Map<Class<?>, Class<?>> overrideRepositories;
public MyRepositoryFactory(EntityManager entityManager) {
super(entityManager);
this.entityManager = entityManager;
//Test
overrideRepositories = new HashMap<>();
overrideRepositories.put(CustomerRepository.class, Customer2Repository.class);
}
protected Object getTargetRepository(RepositoryMetadata metadata) {
return super.getTargetRepository(metadata);
// return new MyRepositoryImpl<T, I>((Class<T>)
// metadata.getDomainClass(), entityManager);
}
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
// The RepositoryMetadata can be safely ignored, it is used by the
// JpaRepositoryFactory
// to check for QueryDslJpaRepository's which is out of scope.
return JpaRepository.class;
}
#SuppressWarnings("unchecked")
#Override
public <E> E getRepository(Class<E> repositoryInterface, Object customImplementation) {
if (overrideRepositories != null) {
Class<?> override = overrideRepositories.get(repositoryInterface);
if (override != null) {
repositoryInterface = (Class<E>) override;
}
}
return super.getRepository(repositoryInterface, customImplementation);
}
}
}
I configured it like this: #EnableJpaRepositories(repositoryFactoryBeanClass=MyRepositoryFactoryBean.class)
Normally you would autowire the repositories themselves which doesn't work because there are two Interfaces with the same Type and I don't know how to tell Spring which one to use.
If I autowire the factory instead, I can call getRepository each time I need a specific one. But how do I get this factory? Does Spring Data JPA somehow expose this as a bean? I can't find anything on google concerning this. Or is this approach entirely wrong?
You can use the ApplicationContext instance to get your MyRepositoryFactoryBean bean class. All you have to do is implement the ApplicationContextAware interface in order to get access to the ApplicationContext instance.
public class myClass implements ApplicationContextAware{
private static ApplicationContext ac;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ac = applicationContext;
}
}
Now you can use ac.getBean("MyRepositoryFactoryBean") to get the factory directly from the ApplicationContext. Once you have that bean you can call getRepository on it.