Generic JPA repository for multiple entities - java

I have several entities and use Spring Data JPA repositories with specifications query my database. Therefore I created a generic class SpecBuilder to build my queries based on a query description (MyQueryDescriptor).
public class Specs {
public static <T extends MyEntityIFace> Specification<T> myfind(final MyQueryDescriptor qDesc) {
return new Specification<T>() {
#Override
public Predicate toPredicate(Root<T> root,
CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
try {
return SpecBuilder.mySpec(root, criteriaQuery, criteriaBuilder, qDesc);
} catch (Exception e) {
...handle error...
}
}
};
}
}
My repositories:
public interface Entity1DAO extends Repository<Entity1,Long>,
JpaSpecificationExecutor {
}
and
public interface Entity2DAO extends Repository<Entity2,Long>,
JpaSpecificationExecutor {
}
Now there are 3 things I am not quite sure about:
1)
Is this use of a generic SpecBuilder a clean design?
2)
Is there a way to avoid writing those repository interfaces for each entity? Let's say a generic repository?
3)
The MyQueryDescriptor class has a method to return an instance of an Entity, which will be queried.
What would be a clean way to obtain the according repository based on the entity class, avoiding a switch case? I was thinking about putting an annotation with the specific repository class to each entity but it feels a bit smelly.
Should I create a factory and inject a map like
Entity1.class => Entity1DAO
Entity2.class => Entity2DAO
?

You can use entity inheritance and use Spring Expression Language (SpEL) to make repository issue calls on right entities. Like in my last update here

Is this use of a generic SpecBuilder a clean design?
Depends what criteria you have for clean design. Will the same MyQueryDescriptor work for different entities? Surely they have different properties, so you need to ask yourself whether a given MyQueryDescriptor could be mistakenly used for an incompatible entity and ways in which you could prevent it. We cannot comment on that since we don't know how your SpecBuilder works.
Is there a way to avoid writing those repository interfaces for each entity? Let's say a > generic repository?
Nope. It's not much boilerplate either, though.
The MyQueryDescriptor class has a method to return an instance of
an Entity, which will be queried. What would be a clean way to obtain
the according repository based on the entity class, avoiding a switch
case?
I suppose you could use getBeanProvider at runtime, where you would define resolvableType as CrudRepository<MyEntityType, IdType>.
However, if I were you, I'd consider switching to using JPA Criteria API without the JpaSpecificationExecutor abstraction on top of it. That would probably prove to be more natural. The design of Spring repositories is centered around the idea of the repository organizing queries around the given specific entity, whereas your use case seems to go in exactly the opposite direction - to dynamically pick an entity and then find a repository to fit in, just to satisfy Spring's restrictions. You seem to be fighting the framework in that regard.

Related

with jooq is there a generic way to 'select by id'?

I'm pretty new to using jooq and I'm trying to implement the usual CRUD operations that us Java guys like to have in our DAOs/repositories. I have the following code for selecting a record by id:
public class JooqRepository<ID, E extends BaseObject<ID>, T extends Table<R>, R extends Record> {
...
private final T table; // would be coming from constructor to concrete reference in the generated classes
...
protected Optional<E> findById(ID id) {
final TableField<R, ID> idField = (TableField<R, ID>) table.getIdentity().getField();
return dsl.fetchOptional(table, idField.eq(id)).map(toEntity()); // conversion method omitted here
}
...
}
My question is firstly would this approach work for all kinds of tables/records or only ones that use identity/auto-increment?
What if I use a DBMS that doesn't have this feature (e.g. Oracle)?
What if a table has a composite key?
And lastly: Is it even recommended to use jooq in that way or should we explicitly craft dedicated queries for every table?
While it is possible to use jOOQ as a Spring repository implementation, you could also just use jOOQ's out of the box DAO support, which works in a similar way. The main difference is that jOOQ DAOs are unopinionated auxiliary tools, that do not impose DDD as a modeling paradigm, they just simplify the most common CRUD operations on each of your tables.
You can subclass the generated DAOs in order to add more functionality, and inject them to your services like Spring's repositories.

How to create generic DAO that delivers crud operation without the need to extend it by custom DAO that doesn't deliver others methods

I've generic DAO:
#NoRepositoryBean
interface ICrudDao<T, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
List<T> findAll();
T findOne(ID id);
T save(T persisted);
}
To allow services to work on that I have to create interface that allows custom entities get persistence, f.e.:
interface TodoDao extends ICrudDao<Todo, Long> {
}
I've a lot of daos like TodoDao. Then don't deliver any special methods.
Creating a lot of empty interfaces seems a dumb idea. How can create a Generic one?
Edit:
I don't think what you are trying to do is a good idea. At first to register a repository for each Entity seems like boiler plate code, but as the application grows, it will help you to maintain it. Imagine your application to evolve over time like this:
You create a simple entity Person and the Interface PersonRepository. Luckily all basic CRUD operations are included, so far it fits your needs so there is nothing else to do.
As your application grows, Person gets a lot of associated relations, like Address, Job, Hobbies and it would be very inefficient to fetch all associated data everytime you access it, because not always every association is needed. To encounter that, you create your own method in PersonRepository which executes your own NamedQuery to only load certain fields and store it in your DTO needed for the specific view ("SELECT new package.PersonDto(x,y) FROM PERSON WHERE ...).
As time passes by, you find yourself in a situation where you need queries to get executed in dynamic fashion, like pagination or restrictions that only need to be added on certain conditions. So you create a new interface PersonCustomRepository and PersonCustomRepositoryImpl where you write queries in a programatic way:
#PersistenceContext
private EntityManager entityManager;
#Transactional
public List<Person> foo() {
// example for accessing hibernate directly, you could also use QueryDSL and so on
Criteria basicCriteria = entityManager.unwrap().createCriteria(Person.class);
if (someCondition) {
criteria.add(Restrictions.eq("foo", foo));
...
}
...
return criteria.list();
}
Bottom line: Spring data repositories already do a lot of work for you and they are easy to extend, don't try to fight your framework, even it maybe saves you some clicks in the first place.
You can avoid this by making your entities generic.
//you can annotated with #MappedSuperclass
public class BaseBean{
//you can specify the id here
}
public class Todo extends BaseBean {
}
#NoRepositoryBean
interface ICrudDao<T exntends BaseBean, ID extends Serializable> extends Repository<T, ID> {
void delete(T deleted);
List<T> findAll();
T findOne(ID id);
T save(T persisted);
}
I don't think it's possible. See How to create a Generic DAO class using Hibernate Context sessions and Hibernate: CRUD Generic DAO these might help.
I can also think of the Hibernate Session as an example of a single class that deals with the persistence of all types of objects, it just deals with Object type.

Use Spring Data JPA, QueryDSL to update a bunch of records

I'm refactoring a code base to get rid of SQL statements and primitive access and modernize with Spring Data JPA (backed by hibernate). I do use QueryDSL in the project for other uses.
I have a scenario where the user can "mass update" a ton of records, and select some values that they want to update. In the old way, the code manually built the update statement with an IN statement for the where for the PK (which items to update), and also manually built the SET clauses (where the options in SET clauses can vary depending on what the user wants to update).
In looking at QueryDSL documentation, it shows that it supports what I want to do. http://www.querydsl.com/static/querydsl/4.1.2/reference/html_single/#d0e399
I tried looking for a way to do this with Spring Data JPA, and haven't had any luck. Is there a repostitory interface I'm missing, or another library that is required....or would I need to autowire a queryFactory into a custom repository implementation and very literally implement the code in the QueryDSL example?
You can either write a custom method or use #Query annotation.
For custom method;
public interface RecordRepository extends RecordRepositoryCustom,
CrudRepository<Record, Long>
{
}
public interface RecordRepositoryCustom {
// Custom method
void massUpdateRecords(long... ids);
}
public class RecordRepositoryImpl implements RecordRepositoryCustom {
#Override
public void massUpdateRecords(long... ids) {
//implement using em or querydsl
}
}
For #Query annotation;
public interface RecordRepository extends CrudRepository<Record, Long>
{
#Query("update records set someColumn=someValue where id in :ids")
void massUpdateRecords(#Param("ids") long... ids);
}
There is also #NamedQuery option if you want your model class to be reusable with custom methods;
#Entity
#NamedQuery(name = "Record.massUpdateRecords", query = "update records set someColumn=someValue where id in :ids")
#Table(name = "records")
public class Record {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
//rest of the entity...
}
public interface RecordRepository extends CrudRepository<Record, Long>
{
//this will use the namedquery
void massUpdateRecords(#Param("ids") long... ids);
}
Check repositories.custom-implementations, jpa.query-methods.at-query and jpa.query-methods.named-queries at spring data reference document for more info.
This question is quite interesting for me because I was solving this very problem in my current project with the same technology stack mentioned in your question. Particularly we were interested in the second part of your question:
where the options in SET clauses can vary depending on what the user
wants to update
I do understand this is the answer you probably do not want to get but we did not find anything out there :( Spring data is quite cumbersome for update operations especially when it comes to their flexibility.
After I saw your question I tried to look up something new for spring and QueryDSL integration (you know, maybe something was released during past months) but nothing was released.
The only thing that brought me quite close is .flush in entity manager meaning you could follow the following scenario:
Get ids of entities you want to update
Retrieve all entities by these ids (first actual query to db)
Modify them in any way you want
Call entityManager.flush resulting N separate updates to database.
This approach results N+1 actual queries to database where N = number of ids needed to be updated. Moreover you are moving the data back and forth which is actually not good too.
I would advise to
autowire a queryFactory into a custom repository
implementation
Also, have a look into spring data and querydsl example. However you will find only lookup examples.
Hope my pessimistic answer helps :)

Extend spring data's default syntax

In my current project almost every entity has a field recordStatus which can have 2 values:
A for Active
D for Deleted
In spring data one can normally use:
repository.findByLastName(lastName)
but with the current data model we have to remember about the active part in every repository call, eg.
repository.findByLastNameAndRecordStatus(lastName, A)
The question is: is there any way to extend spring data in such a way it would be able to recognize the following method:
repository.findActiveByLastName(lastName)
and append the
recordStatus = 'A'
automatically?
Spring Data JPA provides 2 additional options for you dealing with circumstances that their DSL can't handle by default.
The first solution is custom queries with an #Query annotation
#Query("select s from MyTable s where s.recordStatus like 'A%'")
public MyObect findActiveByLastName(String lastName);
The second solution is to add a completely custom method the "Old Fashion Way" You can create a new class setup like: MyRepositoryImpl The Impl is important as it is How spring knows to find your new method (Note: you can avoid this, but you will have to manually link things the docs can help you with that)
//Implementation
public class MyRepositoryImpl implements MyCustomMethodInterface {
#PersistenceContext
EntityManager em;
public Object myCustomJPAMethod() {
//TODO custom JPA work similar to this
String myQuery = "TODO";
return em.createQuery(myQuery).execute();
}
}
//Interface
public interface MyCustomMethodInterface {
public Object myCustomJPAMethod();
}
//For clarity update your JPA repository as well so people see your custom work
public interface MySuperEpicRepository extends JPARepository<Object, String>, MyCustomMethodInterface {
}
These are just some quick samples so feel free to go read their Spring Data JPA docs if you would like to get a bit more custom with it.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Finally just a quick note. Technically this isn't a built in feature from Spring Data JPA, but you can also use Predicates. I will link you to a blog on this one since I am not overly familiar on this approach.
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
You can use Spring Data's Specifications. Take a look at this article.
If you create a 'Base'-specification with the recordStatus filter, and deriving all other specifications form this one.
Of course, everybody in your team should use the specifactions api, and not the default spring data api.
I am not sure you can extend the syntax unless you override the base class (SimpleReactiveMongoRepository; this is for reactive mongo but you can find the class for your DB type), what I can suggest you is to extend the base methods and then make your method be aware of what condition you want to execute. If you check this post you get the idea that I did for the patch operation for all entities.
https://medium.com/#ghahremani/extending-default-spring-data-repository-methods-patch-example-a23c07c35bf9

Overloading query in jpa repository

I am looking for a way to overload several queries from my jpa repository.
for example i want to have the "regular" : public Player findPlayerById(Long Id);
but also :
#Lock(LockModeType.PESSIMISTIC_WRITE)
public Player findPlayerById(Long Id);
I found this: How to add custom method to Spring Data JPA
but i don't think its relevant for my case.
I thought about creating PlayerRepositoryPessimestic with the locked annotation.
Is there a way to use the same repository?
I think here your problem is more a Java problem.
Both
#Lock(LockModeType.PESSIMISTIC_WRITE)
public Player findPlayerById(Long Id);
and
public Player findPlayerById(Long Id);
have the same signature. So my guess is that it is not possible using the same repo. But there are plenty options. Different repos as you suggest is the easiest one IMO. But you could write a custom InvocationHandler.

Categories

Resources