Coder Social home page Coder Social logo

mikesafonov / spring-boot-starter-specification-builder Goto Github PK

View Code? Open in Web Editor NEW
12.0 3.0 3.0 235 KB

Spring Boot starter for building specifications in declarative way

License: MIT License

Java 100.00%
spring-boot spring-data-jpa spring-data-jpa-specification java

spring-boot-starter-specification-builder's Introduction

spring-boot-starter-specification-builder

Maven Central codecov Travis-CI Conventional Commits

Quality Gate Status Reliability Rating Maintainability Rating Security Rating

Bugs Code Smells Vulnerabilities

Duplicated Lines (%) Lines of Code Technical Debt

This is a spring Boot starter for building specifications in declarative way.

The starter is available at maven central repository.

Using gradle:

dependencies {
    implementation 'com.github.mikesafonov:spring-boot-starter-specification-builder:version'
}

Using maven:

<dependency>
  <groupId>com.github.mikesafonov</groupId>
  <artifactId>spring-boot-starter-specification-builder</artifactId>
  <version>version</version>
</dependency>

Usage

Only one bean (SpecificationBuilder) is created automatically by this starter.

In all of the examples below, it is assumed that we have the following classes:

Entity:

@Entity
@Table(name = "my_entity")
public class MyEntity {
    ...
}

Filter:

public class MyEntityFilter {
    ...
}

Repository:

public interface MyEntityRepository extends JpaRepository<MyEntity, Integer>, JpaSpecificationExecutor<MyEntity> {
}

Service which uses MyEntityRepository and SpecificationBuilder

public class MyEntityService {
    ...
    public List<MyEntity> findByFilter(MyEntityFilter filter){
        return myEntityRepository.findAll(specificationBuilder.buildSpecification(filter));
    }   
}

Get all entities with specific field is equals to filters value

The following code example demonstrates how to find all entities by filter`s value:

Entity:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class CarFilter {
    private String name;
}

If there is no annotation on the filters field then SpecificationBuilder create equals predicate.

Different name of column in filter

The following code example demonstrates how to link filters field with entity`s field:

Entity:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class CarFilter {
    @Name(value = "name")
    private String filterByName;
}

Filter by several columns by one expression

The following code example demonstrates how to filter by several columns by one expression:

Entity:

@Entity
@Table(name = "cars")
public class CarEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "number")
    private String number;
    @Column(name = "cost_from")
    private int costFrom;
    @Column(name = "cost_to")
    private int costTo;
    @ManyToOne
    @JoinColumn(name = "id_model")
    private CarModel model;
}

Filter:

@Data
public class CostGreaterThenCarFilter {
    @GreaterThan
    @Names(value = {"costFrom", "costTo"}, type = Names.SearchType.AND)
    private Integer value;
}

Ignore specific field in filter

The following code example demonstrates how to ignore specific field in filter:

Entity:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class CarFilter {
    @Ignore
    private String name;
    @Name(value = "name")
    private String filterByName;
}

In this example SpecificationBuilder create equals predicate only by filterByName field.

Get all entities with specific field is not null

The following code example demonstrates how to find all entities with specific field is not null:

Entity:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class CarFilter {
    @NonNull
    @Name(value = "name")
    private String filterByName;
}

Get all entities with specific field is null

The following code example demonstrates how to find all entities with specific field is null:

Entity:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class CarFilter {
    @IsNull
    @Name(value = "name")
    private String filterByName;
}

Get all entities with specific field greater than/greater than or equals/less than/less than or equals filters value

The following code example demonstrates how to use @GreaterThan, @GreaterThanEqual, @LessThan and @LessThanEqual annotations:

Filter:

public class CarFilter {
    @GreaterThan
    @Name(value = "size")
    private Double filterSize;
    @GreaterThanEqual
    @Name(value = "size2")
    private Double filterSize2;
    @LessThan
    @Name(value = "size3")
    private Double filterSize3;
    @LessThanEqual
    @Name(value = "size4")
    private Double filterSize4;
}

Like predicate

Like predicate works only with String values.

The following code example demonstrates how to find all entities with specific field is like to filters value

Entity:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class CarFilter {
    @Like
    @Name(value = "size")
    private String likeName;
}

By default @Like ignores case. If you want to search by case sensitive values use caseSensitive property:

public class CarFilter {
    @Like(caseSensitive = true)
    @Name(value = "size")
    private String likeName;
}

By default @Like search by full like (%value%). If you want to search by left like or right use direction property:

public class CarFilter {
    @Like(direction = Like.DIRECTION.LEFT)
    @Name(value = "size")
    private String likeName;
}
public class CarFilter {
    @Like(direction = Like.DIRECTION.RIGHT)
    @Name(value = "size")
    private String likeName;
}

Also, you can use @Like without % bounds

@Like(direction = Like.DIRECTION.NONE)

Using Join

The following code example demonstrates how to find all entities with specific field is equal to filters value, joined by another field

Entities:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}
@Entity
@Table(name = "cars")
public class Car {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
    @ManyToOne
    @JoinColumn(name = "id_model")
    private CarModel model;
}

Filter:

public class CarFilter {
    @Join(value = "model")
    @Name(value = "name")
    private String model;

    @Join(value = "model")
    @Name(value = "id")
    @GreaterThan
    private Integer modelId;
}

Join is Repeatable annotation so you can join multiple entities.

Collections in filter

The following code example demonstrates how to find all entities with specific field contains in filters value:

Entity:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class CarFilter {
    @Name(value = "name")
    private Collection<String> names;
}

Filter by many-to-many linked tables

The following code example demonstrates how to find all entities with specific many-to-many joined field contains in filters value:

Entity:

@Entity
@Table(name = "students")
public class StudentEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name = "students_classes",
            joinColumns = @JoinColumn(name = "id_student"),
            inverseJoinColumns = @JoinColumn(name = "id_class"))
    private Set<ClassEntity> classEntities;
}
@Entity
@Table(name = "classes")
public class ClassEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class StudentFilter {
    @ManyToManyCollection
    @Join(value = "classEntities")
    @Name(value = "name")
    private List<String> classes;
}

Filter by segment intersection

The following code example demonstrate how to find entities by segment bounded by fields like start and end and intersect with segment from filter.

Entity:

@Entity
@Table(name = "students")
public class StudentEntity {
    @Column(name = "date_start_studying")
    private LocalDate studyingDateStart;

    @Column(name = "date_end_studying")
    private LocalDate studyingDateEnd;
}

Filter:

public class StudentStudyingFilter {
    @SegmentIntersection(fromField = "studyingDateStart", toField = "studyingDateEnd")
    private SegmentFilter<LocalDate> periodFilter;
    
    public StudentStudyingFilter(LocalDate from, LocalDate to) {
        this.periodFilter = new SegmentFilter<>(from, to);
    }
}

SegmentFilter - special type from com.github.mikesafonov.specification.builder.starter.type containing temporal from and to.

You can use @Join annotation with @SegmentIntersection to refer to referenced entity.

Segment intersection predicate allow null values in from and to inside SegmentFilter, and null value in entity toField what mean not ended segment.

Not predicate

Not predicate negates another predicate on field.

The following code example demonstrates how to find all entities exclude specific value in field.

Entity:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class CarFilter {
    @Not
    private String name;
}

Use Distinct

Specify whether duplicate query results will be eliminated.

Entities:

@Entity
@Table(name = "clients")
public class ClientEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String name;

    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "id_client")
    private List<ContractEntity> contracts = new ArrayList<>();
}
@Entity
@Table(name = "contracts")
public class ContractEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String number;

    @Column(name = "id_client")
    private Long idClient;
}

Filter:

@Distinct
public class ClientContractNumberFilter {

    @Like
    @Join("contracts")
    @Name("number")
    private String contract;
}

Function wrapping

Function annotation allow wrap entity field or/and filter value with function. It can be used with another predicate annotation.

Entity:

@Entity
@Table(name = "car_models")
public class CarModel {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
}

Filter:

public class CarFilter {
    @Function(name = "LOWER", wrapping = Function.FunctionWrapping.FILTER)
    @Like
    private String name;
}

Debug

You may turn on additional logging to debug created predicates:

logging:
  level:
    com:
      github:
        mikesafonov:
          specification:
            builder:
              starter: trace

Build

Build from source

You can build application using following command:

./gradlew clean build -x signArchives

Requirements:

JDK >= 1.8

Unit tests

You can run unit tests using following command:

./gradlew test

Contributing

Feel free to contribute. New feature proposals and bug fixes should be submitted as GitHub pull requests. Fork the repository on GitHub, prepare your change on your forked copy, and submit a pull request.

IMPORTANT!

Before contributing please read about Conventional Commits / Conventional Commits RU

spring-boot-starter-specification-builder's People

Contributors

boggard avatar dependabot-preview[bot] avatar eugenelesnov avatar mikesafonov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

spring-boot-starter-specification-builder's Issues

Use of mutation testing in spring-boot-starter-specification-builder - Help needed

Hello there!

My name is Ana. I noted that you use the mutation testing tool Pit in the project.
I am a postdoctoral researcher at the University of Seville (Spain), and my colleagues and I are studying how mutation testing tools are used in practice. With this aim in mind, we have analysed over 3,500 public GitHub repositories using mutation testing tools, including yours! This work has recently been published in a journal paper available at https://link.springer.com/content/pdf/10.1007/s10664-022-10177-8.pdf.

To complete this study, we are asking for your help to understand better how mutation testing is used in practice, please! We would be extremely grateful if you could contribute to this study by answering a brief survey of 21 simple questions (no more than 6 minutes). This is the link to the questionnaire https://forms.gle/FvXNrimWAsJYC1zB9.

We apologize if you have already received message multiple times or if you have already had the opportunity to complete the survey. If you have already shared your feedback, we want to convey our appreciation, kindly disregard this message, and please accept our apologies for any inconvenience.

Drop me an e-mail if you have any questions or comments ([email protected]). Thank you very much in advance!!

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.