Spring data jpa query methods with criteria api - java

I have been using spring data JPA with mysql. I mostly use query methods as below :
public interface VehicleRepository extends JpaRepository<Vehicle, Long> {
Vehicle findByRegistrationNumber(String registrationNumber);
Vehicle findByDriver(Driver driver);
Vehicle findByNaturalId(String naturalId);
}
But now for some usecase I want to criteria api as below :
#Repository
public class VehicleCriteriaRepository {
private final EntityManager entityManager;
public VehicleCriteriaRepository(EntityManager entityManager) {
this.entityManager = entityManager;
}
public Vehicle find(String naturalId) {
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Vehicle> criteriaQuery = criteriaBuilder.createQuery(Vehicle.class);
Root<Vehicle> vehicleRoot = criteriaQuery.from(Vehicle.class);
CriteriaQuery<Vehicle> registrationNumber = criteriaQuery
.select(vehicleRoot)
.where(criteriaBuilder.equal(vehicleRoot.get("naturalId"), naturalId));
Vehicle singleResult = entityManager.createQuery(registrationNumber).getSingleResult();
return singleResult;
}
}
I am unable to understand that how can I use both of them together. Because if I want to use criteria api, I'll have to make a concrete class. And in case I make a concrete class I could not understand how will I be able to use jpa query methods, as if I implement the interface, I'll have to provide an implementation.
Can anyone please help me on this.

You can use Spring JPA Specification. They will provide you the flexibility of using the Spring repository with JPA's criteria builder.
It is as simple as making your repository extending the interface JpaSpecificationExecutor.
public interface VehicleRepository extends JpaRepository<Vehicle, Long>, JpaSpecificationExecutor<Vehicle> {
....
}
Then you can query your repository using anonymous implementation of an specification or you can implement it as well, whatever suits you best.
An anonymous implementation will be something like below.
#Autowired
VehicleRepository vehicleRepository;
// somewhere in some methods
vehicleRepository.findOne((root, criteriaQuery, criteriaBuilder) ->
criteriaBuilder.equal(root.get("naturalId"), naturalId)
);

Related

Any way to use the `#Procedure` annotation without an entity?

So I'd like a "Void-Repository" through which to gain access to stored procedures that are not necessarily operation on entities.
#Repository
public interface StoredProceduresRepository extends CrudRepository<Void, Long> {
#Procedure("my_answer_giver")
String getMyAnswer(#Param("input") String input);
}
But that does, of course, not work because the CrudRepository expects Void to be an entity.
Is there a way to use the #Procedure annotation without having to create dummy entities or am I stuck with an implemented class that makes use of the EntityManager to query via prepared statements?
Because let's be honest, that's fugly:
#Repository
public class StoredProceduresRepository {
#PersistenceContext
EntityManager em;
public String getMyAnswer(String input) {
Query myAnswerGiver = em
.createStoredProcedureQuery("my_answer_giver")
.registerStoredProcedureParameter("input", String.class, ParameterMode.IN)
.setParameter("input", input);
Object result = ((Object[]) myAnswerGiver.getSingleResult())[0];
return (String) result;
}
}
If it is ok for you you can use any Entity you have, in place of this Void. The Entity provided there should not matter.
public interface StoredProceduresRepository extends JpaRepository<SomeUnrelatedEntity, Long> {
#Procedure("my_answer_giver")
String getMyAnswer(#Param("input") String input);
}
I have been using it like this with database views.

Spring Data JPA - Creating custom query method generator

In Spring Data JPA we can define a repository interface extending Repository and write a custom method.
If this method follows special syntax, Spring Data will generate the method body automatically.
For example (from the documentation):
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByLastname(String lastname);
}
Is there a way to customize the method generation code to introduce new keywords into the syntax?
For example:
Person findExactlyOneById(Long id);
This method would either return the entity or throw a custom exception.
I know I can customize specific repositories as well as the base repository and achieve the effect from the above example, but I'm specifically asking for the automatic method of body generation.
Is there an extension point designed in the framework? Or is the only option to change the source code?
In your case, you can always use CrudRepository.findById(Long id) or JpaRepository.getOne(Long id).
I would suggest inheriting from the JpaRepository class because all types of repositories are included.
You can set nativeQuery = true in the #Query annotation from a Repository class like this:
public static final String FIND_PROJECTS = "SELECT projectId, projectName FROM projects";
#Query(value = FIND_PROJECTS, nativeQuery = true)
public List<Object[]> findProjects();
It's probably worth looking at the Spring data docs as well.
Some more example
1.
public interface UserRepository extends JpaRepository<User, Long> {
#Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?1", nativeQuery = true)
User findByEmailAddress(String emailAddress);
}

Spring Data JPA: Generate dynamic query

I have an entity that hold some logic data :
#Entity
public class Person {
private Long id.
private String name;
private int age;
private String address;
...
}
I create my Spring data interface
#Repository
public interface CardInventoryRepository extends JpaRepository<Person , Long> {
}
My purpose is to create a dynamic query based on the exist values of my entity for example
if the name is null the query is :
select * from Person p Where p.age=12 AND p.address="adress.."
When the address is null the query should be :
select * from Person p Where p.age=12 AND p.name="ALI"
I want to extract data using only the non empty fields ?
is there any solution suing spring data for building dynamic queries ?
Thanks in advance
Based on Spring doc https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#query-by-example
Query by Example (QBE) is a user-friendly querying technique with a
simple interface. It allows dynamic query creation and does not
require you to write queries that contain field names. In fact, Query
by Example does not require you to write queries by using
store-specific query languages at all.
DEFINITION:
An Example takes a data object (usually the entity object or a sub-type of it) and a specification how to match properties. You can use Query by Example with JPA
Repositories.
To do so, let your repository interface extend QueryByExampleExecutor<T>, for example:
public interface PersonRepository extends CrudRepository<Person, String>, QueryByExampleExecutor<Person> {
}
Here are the available methods in QueryByExampleExecutor :
public interface QueryByExampleExecutor<T> {
<S extends T> S findOne(Example<S> example);
<S extends T> Iterable<S> findAll(Example<S> example);
// … more functionality omitted.
}
USAGES:
Example<Person> example = Example.of(new Person("Jon", "Snow"));
repo.findAll(example);
ExampleMatcher matcher = ExampleMatcher.matching().
.withMatcher("firstname", endsWith())
.withMatcher("lastname", startsWith().ignoreCase());
Example<Person> example = Example.of(new Person("Jon", "Snow"), matcher);
repo.count(example);
MORE INFO
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#query-by-example
https://github.com/spring-projects/spring-data-examples/tree/master/jpa/query-by-example
Spring Data JPA: Query by Example?
Yes, please take a look at the QueryDSL support for Spring Data. Your use case can be implemented via a Predicate. In a nutshell, you have to create a predicate in which you would pass the non null fields, and then pass that predicate to a findAll method that takes a Predicate as argument. Your repository interface also has to extend QueryDslPredicateExecutor
Need to extend repository from JpaSpecificationExecutor
#Repository
#Transactional
public interface EmployeeDAO extends CrudRepository<Employee,Long>,JpaSpecificationExecutor<Employee>{
}
Use specification and predicate like below
public List<Employee> findByCriteria(String employeeName,String employeeRole){
return employeeDAO.findAll(new Specification<Employee>() {
#Override
public Predicate toPredicate(Root<Employee> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
if(employeeName!=null) {
predicates.add(criteriaBuilder.and(criteriaBuilder.like(root.get("employeeName"), "%"+employeeName+"%")));
}
if(employeeRole!=null){
predicates.add(criteriaBuilder.and(criteriaBuilder.equal(root.get("employeeRole"), employeeRole)));
}
return criteriaBuilder.and(predicates.toArray(new Predicate[predicates.size()]));
}
});
}

Customize a Spring MongoRepository - filter by class

I´ve got several Java documents that are stored in the same collection "app" in mongodb. My intention is use it for the common persistence actions (insert, find, delete...). The problem comes when I try to define repositories for only one of the classes as the repository that I´ve defined will search all the entities, and what I need is a repository where I can call to all the mongorepository standard functions (find, findAll, findby...). My explanation can be hard to understand, this is what I have now:
Base document:
#Document(collection = "app")
#NoRepositoryBean
public abstract class AbstractApplicationDocument {
#Id
public String mongoId;
// more variables, getters, setters...
}
One of the subdocuments (there will be many).
#EqualsAndHashCode(callSuper=true)
public class ClientApplicationDocument extends AbstractApplicationDocument
{
#Id
public String mongoId;
}
Base Repository
#NoRepositoryBean
public interface ApplicationRepository<T, ID extends Serializable> extends MongoRepository<T, ID> {
}
Extended repository (where I want to filter by class)
public interface HttpClientRepository extends ApplicationRepository<HttpClientDocument, String> {
}
Sample of temporary solution that I'd like to avoid if I can reuse the MongoDb utilities
#Component
public class HttpClientRepositoryImpl {
#Autowired
private HttpClientRepository clientRepo;
#Autowired
private MongoTemplate mongo;
public List<HttpClientDocument> findAll() {
Query query = new Query();
query.addCriteria(Criteria.where("_class").is(HttpClientDocument.class.getName()));
mongo.getConverter();
return mongo.find(query, HttpClientDocument.class,"app");
}
}
Relevant information in gradle. I add this because I've seen some solutions that are not valid for me. Probably because they are using different libraries:
compile("org.springframework.boot:spring-boot-starter-data-mongodb:${springBootVersion}")
compile ("org.springframework:spring-context-support:4.1.8.RELEASE");
Is there any simple solution?
You can customize Spring Data repositories in many ways. You can provide custom repository base classes or provide custom implementation classes.
For your question in particular, SimpleMongoRepository does not restrict query on the _class field. You can see below an example for a custom repository base class that restricts the findAll query method to use only the declared entity type.
You would be required to apply that pattern also for other methods which are declared on the MongoRepository level that you want to restrict to the particular class type.
#EnableMongoRepositories(repositoryBaseClass = MyMongoRepository.class)
static class Config {
}
static class MyMongoRepository<T, ID extends Serializable> extends SimpleMongoRepository<T, ID> {
private final MongoEntityInformation<T, ID> metadata;
private final MongoOperations mongoOperations;
public MyMongoRepository(MongoEntityInformation<T, ID> metadata, MongoOperations mongoOperations) {
super(metadata, mongoOperations);
this.metadata = metadata;
this.mongoOperations = mongoOperations;
}
#Override
public List<T> findAll() {
Query query = new Query();
query.restrict(this.metadata.getJavaType());
return this.mongoOperations.find(query, this.metadata.getJavaType(), this.metadata.getCollectionName());
}
}
findAll queries restrict now the class type and queries for a e.g. ModelRepository extends MongoRepository<Model, String> would look like:
{ "_class" : { "$in" : [ "com.example.Model"]}}
Query methods with #Query can be used to pass in the class type (#Query("{'_class': ?0}")) but there's no way to contrain the class type for derived query methods (List<Model> findByFirstnameAndLastname(…)).
There's an open ticket in our Jira related to restricting types of the repository. That particular ticket is related to polymorphic queries but in its essence, it's related to type restrictions on query methods.

How to extract "find or create" method to abstract class? (Spring Data Jpa)

I'm using Spring Data JPA and I have a bunch of repositories like this one:
public interface CustomerRepository extends JpaRepository<Customer, Long> {}
Under repositories I have services and a lot of them need to have implemented method findOrCreate(String name) like this one:
#Override
#Transactional
public List<Customer> findOrCreate(final String name) {
checkNotNull(name);
List<Customer> result = this.customerRepository.findByName(name);
if (result.isEmpty()) {
LOGGER.info("Cannot find customer. Creating a new customer. [name={}]", name);
Customer customer = new Customer(name);
return Arrays.asList(this.customerRepository.save(customer));
}
return result;
}
I would like to extract method to the abstract class or somewhere to avoid implementing it for each services, testing and so on.
Abstract class can be look like this:
public abstract class AbstractManagementService<T, R extends JpaRepository<T, Serializable>> {
protected List<T> findOrCreate(T entity, R repository) {
checkNotNull(entity);
checkNotNull(repository);
return null;
}
}
And the problem is it due to the fact that I need to find object by name as a string before creating a new one. Of course interface JpaRepository doesn't offer this method.
How can I solve this problem?
Best Regards
Create a custom JpaRepository implementation that includes this behaviour. See this post for an example of writing a custom JpaRepository implementation.

Categories