Giter Site home page Giter Site logo

idrinks / infobip-spring-data-jpa-querydsl Goto Github PK

View Code? Open in Web Editor NEW

This project forked from infobip/infobip-spring-data-querydsl

0.0 1.0 0.0 42 KB

Infobip Spring Data JPA Querydsl provides new functionality that enables the user to leverage the full power of Querydsl API on top of Spring Data repository infrastructure.

License: Apache License 2.0

Java 100.00%

infobip-spring-data-jpa-querydsl's Introduction

Infobip Spring Data JPA Querydsl

Build Status Maven Central Coverage Status

Infobip Spring Data JPA Querydsl provides new functionality that enables the user to leverage the full power of Querydsl API on top of Spring Data repository infrastructure.

Contents

  1. News
  2. Features and examples
  3. Setup
  4. Domain Driven Design concerns
  5. Requirements
  6. Further reading
  7. Running tests
  8. Contributing
  9. License

News

2.0.1

Changed scope of SimpleExtendedQueryDslJpaRepository to public.

2.0.0

Upgrade to Spring Data 2 (Spring Boot 2).

Breaking change: new repository methods introduced in Spring Data 2 CrudRepository#findById(Object) and QuerydslPredicateExecutor#findOne(Predicate) replace old ExtendedQueryDslJpaRepository.findOneById(Object) and ExtendedQueryDslJpaRepository.findOneByPredicate(Predicate) (they were removed).

Features and examples:

All examples have corresponding tests in the project and can be found here.

Native queries with Querydsl:

Example which uses union clause (unions aren't available in JPA):

List<Person> actual = repository.jpaSqlQuery(query -> query
        .union(
                repository.jpaSqlSubQuery(subQuery ->
                                                  subQuery.select(person)
                                                          .from(person)
                                                          .where(person.firstName.like("John"))),
                repository.jpaSqlSubQuery(subQuery ->
                                                  subQuery.select(person)
                                                          .from(person)
                                                          .where(person.firstName.like("Jan%")))
        )
        .orderBy(person.firstName.asc(), person.lastName.asc())
        .fetch()
);

Projections

For examples how to construct projections refer to the official documentation - section result handling.

Here is an example that uses constructor:

@Value
public class PersonProjection {
    private final String firstName;
    private final String lastName;
}
...
 
List<PersonProjection> actual = repository.query(query -> query
                                          .select(Projections.constructor(PersonProjection.class, person.firstName, person.lastName))
                                          .from(person)
                                          .fetch());

Query

Query exposes full API of JPAQuery (QueryDslPredicateExecutor only exposes where clause (Predicate) and order clause (OrderSpecifier)).

This along with Querydsl 4 API improvement can lead to code that looks more like regular SQL:

List<Person> actual = repository.query(query -> query
        .select(person)
        .from(person)
        .where(person.firstName.in("John", "Jane"))
        .orderBy(person.firstName.asc(), person.lastName.asc())
        .limit(1)
        .offset(1)
        .fetch());

Update

repository.update(query -> query
        .set(person.firstName, "John")
        .where(person.firstName.eq("Johny"))
        .execute());

Delete

long numberOfAffectedRows = repository.deleteWhere(person.firstName.like("John%"));

List instead of Iterable return type

QueryDslPredicateExecutor#findAll methods return Iterable which can be cumbersome to use. Those methods were overridden and now return a List which is easier to use and is easier to convert to Stream.

Transactional support

Query execution is always done inside the repository implementation (loan pattern) in a transaction so transactions don't have to be handled manually (like they do if you are manually managing JPAQuery and other Querydsl constructs).

Stored procedure builder

JPA support for stored procedures is quite cumbersome and it also requires a reference to EntityManager which leads to code like this:

@PersistenceContext
private EntityManager entityManager
...
 
@SuppressWarnings("unchecked")
public List<Person> delete(Person personToDelete) {
    return (List<Person>) entityManager
            .createStoredProcedureQuery("Person_Delete")
            .registerStoredProcedureParameter("FirstName", String.class, ParameterMode.IN)
            .registerStoredProcedureParameter("LastName", String.class, ParameterMode.IN)
            .setParameter("FirstName", personToDelete.getFirstName())
            .setParameter("LastName", personToDelete.getLastName())
            .getResultList(); // returns untyped List => unchecked
}

For this case, executeStoredProcedure method was added which supports Q class attributes:

public List<Person> delete(Person personToDelete) {
    return repository.executeStoredProcedure(
            "Person_Delete",
            builder -> builder.addInParameter(person.firstName, personToDelete.getFirstName())
                              .addInParameter(person.lastName, personToDelete.getLastName())
                              .getResultList());
}

Setup:

  1. Dependency:
<dependency>
   <groupId>com.infobip</groupId>
   <artifactId>infobip-spring-data-jpa-querydsl</artifactId>
   <version>${infobip-spring-data-jpa-querydsl.version}</version>
</dependency>

As this project depends on querydsl-apt with jpa classifier you don't need to set up explicit Maven build phase for Q classes generation. For building Q classes without Maven, make sure your IDE has Annotation processing enabled.

  1. Add @EnableExtendedRepositories to your Main class:
@EnableExtendedRepositories // replaces @EnableJpaRepositories
@SpringBootApplication
public class Main {
 
    public static void main(String[] args) {
        new SpringApplicationBuilder(Main.class).run(args);
    }
}
  1. Refactor repository interfaces to use ExtendedQueryDslJpaRepository instead of JpaRepository and QueryDslPredicateExecutor (note that ExtendedQueryDslJpaRepository extends and provides the API of both):
// ExtendedQueryDslJpaRepository replaces both JpaRepository and QueryDslPredicateExecutor
interface FooRepository extends ExtendedQueryDslJpaRepository<Foo, ID> {
}
  1. Done

If you need other features from @EnableJpaRepositories you can use:

@EnableJpaRepositories(repositoryBaseClass = SimpleExtendedQueryDslJpaRepository.class)

Domain Driven Design concerns

In following example one could argue that database related logic has leaked from repository to service layer:

class FooService {

    private final FooRepository repository;
    
    ...
    
    List<Foo> findAll(String barName, Long limit, Long offset) {
        
        ...
        
        return repository.query(query -> query.select(foo)
                                              .from(foo)
                                              .where(foo.bar.name.eq(barName))
                                              .limit(limit)
                                              .offset(offset)
                                              .fetch());
    }
}

In order to prevent this, you can customize the repository.

First, create a custom repository:

interface FooCustomRepository {

    List<Foo> findAll(String barName, Long limit, Long offset);
}

Make FooRepository extend FooCustomRepository:

interface FooRepository extends ExtendedQueryDslJpaRepository<Foo, ID>, FooCustomRepository {
}

Provide an implementation for FooCustomRepository:

class FooCustomRepositoryImpl implements FooCustomRepository {

    private final ExtendedQueryDslJpaRepository<Foo, ID> repository;

    FooCustomRepositoryImpl(@Lazy ExtendedQueryDslJpaRepository<Foo, ID> repository) {
        this.repository = repository;
    }
    
    @Override
    public List<Foo> findAll(String barName, Long limit, Long offset) {
        return repository.query(query -> query.select(foo)
                                              .from(foo)
                                              .where(foo.bar.name.eq(barName))
                                              .limit(limit)
                                              .offset(offset)
                                              .fetch());
    }
}

Refactor service layer to use the new method:

class FooService {

    private final FooRepository repository;
    
    ...
    
    List<Foo> findAll(String barName, Long limit, Long offset) {
        
        ...
        
        return repository.findAll(barName, limit, offset);
    }
}

Requirements:

  • Java 8
  • Hibernate (if you need support for other JPA implementors please open an issue)
  • Spring Data
  • Querydsl

Further reading

Running tests

Tests require SQL Server DB.

Easies way to set it up on your machine is to use docker:

docker run -e 'ACCEPT_EULA=Y' -e 'SA_PASSWORD=<YourStrong!Passw0rd>' -p 1433:1433 -d microsoft/mssql-server-linux:2017-latest

Contributing

If you have an idea for a new feature or want to report a bug please use the issue tracker.

Pull requests are welcome!

License

This library is licensed under the Apache License, Version 2.0.

infobip-spring-data-jpa-querydsl's People

Contributors

lpandzic avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.