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

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.

Related

Spring data jpa query methods with criteria api

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)
);

Spring data jpa calling stored procedure with lots of parameters

Is it possible to map an object to all the parameters inside a NamedStoredProcedureQuery? I need to call a stored procedure that has over 40 parameters that it takes in. The following code becomes really long and hard to read:
#NamedStoredProcedureQuery(
name="SomeSPName",
procedureName="SomeSPName",
resultClasses={ MyEntity.class },
parameters = {
#StoredProcedureParameter(name="PACKAGE", type=String.class, mode=ParameterMode.IN),
#StoredProcedureParameter(name="APPNM", type=String.class, mode=ParameterMode.IN),
// 40 more required parameters here...
)
public interface MyRepository extends JpaRepository<MyEntity, Long> {
#Query
#Procedure(name="SomeSPName")
long getResult(#Param("PACKAGE") String package, #Param("APPNM") String appnm, /* The rest of the params here */);
}
So my question is can I replace the arguments inside getResult with an object that has all the getters and setters for the parameters and Hibernate will automatically assign the parameters correctly. For example:
public interface MyRepository extends JpaRepository<MyEntity, Long> {
#Query
#Procedure(name="SomeSPName")
long getResult(MyObject params);
}
Here, MyObject will have fields corresponding to each of the StoredProcedureParameter defined up top. Hopefully, this makes sense. I just don't want to put all the 40+ parameters in the getResult definition. I'm using DB2 if it makes a difference. Any help would be appreciated!
EntityManager entityManager;
public Long getResult(Params params) {
StoredProcedureQuery storedProcedure = entityManager
.createStoredProcedureQuery("SomeSPName", Long.class)
.registerStoredProcedureParameter("PACKAGE", String.class, ParameterMode.IN)
.registerStoredProcedureParameter("APPNM", String.class, ParameterMode.IN)
//...
.setParameter("PACKAGE", params.getPackage())
.setParameter("APPNM", params.getAppNm())
//...
;
return storedProcedure.getSingleResult();
}

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()]));
}
});
}

Getting Entity Class in JPA and Repository Interface

I am applying repository similar to Spring Data JPA where I would only create an interface of an entity repository:
public interface AuthorRepository extends Repository<Author, Long> {
}
I have this also Repository interface:
public interface Repository <T, ID extends Serializable> {
List<T> findAll() throws Exception;
}
And its implementation, which I find it difficult to get the class name passed in as parameterized (T) to Repository :
public class RepositoryImpl implements Repository {
#Inject
private EntityManager em;
#Override
public List<Object> findAll() throws Exception {
try {
String namedQuery = "SELECT a FROM " + <How do I get the entity here as Author?> + " a";
TypedQuery<Object> query = em.createNamedQuery(namedQuery, <How do I get the entity class as Author.class?>);
return query.getResultList();
} catch (Exception e) {
System.out.println(e.getMessage());
throw new ApplicationException();
}
}
}
I can't find way how to dynamically generate the entity class (ex. Author) to be created as part of NamedQuery string and an argument for em.createNamequery().
Thanks for any help.
In the RepositoryImpl you can inject the entityInformation like this:
#Autowired
private JpaEntityInformation<T, ID> entityInformation;
and then use it for example like:
String entityName = entityInformation.getEntityName();
Class<T> entityType = entityInformation.getJavaType();
Custom RepositoryFragments sadly can't autowire the JpaEntityInformation because they are singletons, so for generic fragments one would either need to pass the entity class with each method call and use JpaEntityInformationSupport.getEntityInformation(clazz, entityManager) or modify the BeanDefinition of the fragments and get the clazz using the injection point.
Searching world wide web gave me similar approach and codes but none worked but TypeTools works like a charm.

How to add global where clause for all find methods of Spring data JPA with Hibernate?

We are working on web application using Spring data JPA with hibernate.
In the application there is a field of compid in each entity.
Which means in every DB call (Spring Data methods) will have to be checked with the compid.
I need a way that, this "where compid = ?" check to be injected automatically for every find method.
So that we won't have to specifically bother about compid checks.
Is this possible to achieve from Spring Data JPA framework?
Maybe Hibernate‘s annotation #Where will help you. It adds the passed condition to any JPA queries related to the entity. For example
#Entity
#Where(clause = "isDeleted='false'")
public class Customer {
//...
#Column
private Boolean isDeleted;
}
More info: 1, 2
Agree with Abhijit Sarkar.
You can achieve your goal hibernate listeners and aspects. I can suggest the following :
create an annotation #Compable (or whatever you call it) to mark service methods
create CompAspect which should be a bean and #Aspect. It should have something like this
#Around("#annotation(compable)")`
public Object enableClientFilter(ProceedingJoinPoint pjp, Compable compable) throws Throwable {
Session session = (Session) em.getDelegate();
try {
if (session.isOpen()) {
session.enableFilter("compid_filter_name")
.setParameter("comp_id", your_comp_id);
}
return pjp.proceed();
} finally {
if (session.isOpen()) {
session.disableFilter("filter_name");
}
}
}
em - EntityManager
3)Also you need to provide hibernate filters. If you use annotation this can look like this:
#FilterDef(name="compid_filter_name", parameters=#ParamDef(name="comp_id", type="java.util.Long"))
#Filters(#Filter(name="compid_filter_name", condition="comp_id=:comp_id"))
So your condition where compid = ? will be #Service method below
#Compable
someServicweMethod(){
List<YourEntity> l = someRepository.findAllWithNamesLike("test");
}
That's basically it for Selects,
For updates/deletes this scheme requires an EntityListener.
Like other people have said there is no set method for this
One option is to look at Query by example - from the spring data documentation -
Person person = new Person();
person.setFirstname("Dave");
Example<Person> example = Example.of(person);
So you could default compid in the object, or parent JPA object
Another option is a custom repository
I can contribute a 50% solution. 50% because it seems to be not easy to wrap Query Methods. Also custom JPA queries are an issue for this global approach. If the standard finders are sufficient it is possible to extend an own SimpleJpaRepository:
public class CustomJpaRepositoryIml<T, ID extends Serializable> extends
SimpleJpaRepository<T, ID> {
private JpaEntityInformation<T, ?> entityInformation;
#Autowired
public CustomJpaRepositoryIml(JpaEntityInformation<T, ?> entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
this.entityInformation = entityInformation;
}
private Sort applyDefaultOrder(Sort sort) {
if (sort == null) {
return null;
}
if (sort.isUnsorted()) {
return Sort.by("insert whatever is a default").ascending();
}
return sort;
}
private Pageable applyDefaultOrder(Pageable pageable) {
if (pageable.getSort().isUnsorted()) {
Sort defaultSort = Sort.by("insert whatever is a default").ascending();
pageable = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), defaultSort);
}
return pageable;
}
#Override
public Optional<T> findById(ID id) {
Specification<T> filterSpec = filterOperatorUserAccess();
if (filterSpec == null) {
return super.findById(id);
}
return findOne(filterSpec.and((Specification<T>) (root, query, criteriaBuilder) -> {
Path<?> path = root.get(entityInformation.getIdAttribute());
return criteriaBuilder.equal(path, id);
}));
}
#Override
protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort) {
sort = applyDefaultOrder(sort);
Specification<T> filterSpec = filterOperatorUserAccess();
if (filterSpec != null) {
spec = (Specification<S>) filterSpec.and((Specification<T>) spec);
}
return super.getQuery(spec, domainClass, sort);
}
}
This implementation is picked up e.g. by adding it to the Spring Boot:
#SpringBootApplication
#EnableJpaRepositories(repositoryBaseClass = CustomJpaRepositoryIml.class)
public class ServerStart {
...
If you need this kind of filtering also for Querydsl it is also possible to implement and register a QuerydslPredicateExecutor.

Categories