### Setup Project Dependencies Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/README.md Installs necessary npm packages and git submodules, including the Docsy theme, required for the project setup. ```console $ npm install ``` -------------------------------- ### Multi Join Fetches Example Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/fetch/_index.md Demonstrates fetching multiple levels of related entities using @JoinFetches. This example shows fetching customers, their orders, and the tags associated with those orders. ```java class Customer { @OneToMany(cascade = ALL, fetch = LAZY) @JoinColumn(name = "order_id") private Set orders; } @Entity class Order { @ManyToMany(cascade = ALL, fetch = LAZY) private Set tags; } @Entity class Tag { private String name; } ``` ```java @Data class CustomerOrderTagCriteria { @JoinFetches({ @JoinFetch(path = "orders", alias = "o"), @JoinFetch(path = "o.tags", alias = "t") }) @Spec(path = "t.name", value = In.class) Collection tags; } ``` ```sql select distinct customer0_.* ..., orders1_.* ..., tag3_.* ... from customer customer0_ inner join orders orders1_ on customer0_.id=orders1_.order_id inner join orders_tags tags2_ on orders1_.id=tags2_.order_id inner join tag tag3_ on tags2_.tags_id=tag3_.id where tag3_.name in (?) ``` -------------------------------- ### Example SQL Query Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/spec/_index.md Provides an example of the SQL query generated by SpecMapper based on the provided criteria. ```sql ... where x.firstname like '%Hello%' ``` -------------------------------- ### Install Node.js LTS Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/README.md Installs the latest Long Term Support (LTS) release of Node.js using nvm (Node Version Manager). This is a prerequisite for building the site. ```console $ nvm install --lts ``` -------------------------------- ### Serve Site Locally Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/README.md Starts a local development server to preview the website. The site will be accessible at localhost:1313. ```console $ hugo serve ``` -------------------------------- ### Built-in @Spec: StartingWith Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in StartingWith specification for String fields. ```java @Spec(StartingWith.class) String firstname; ``` -------------------------------- ### Configuring Custom Base Repository Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/starter/qbs/_index.md Provides an example of how to configure a custom base repository by setting the `spec.mapper.repository-base-class` property in the application's properties file. ```yaml spec: mapper: repository-base-class: com.example.MyRepositoryImpl ``` -------------------------------- ### Negated Between SQL Example Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Illustrates the resulting SQL when using a negated Between specification. ```sql ... where x.age not between ? and ? ``` -------------------------------- ### Built-in @Spec: Before Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in Before specification for Comparable types, typically dates. ```java @Spec(Before.class) LocalDate startDate; ``` -------------------------------- ### Configuring Custom Base Repository Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/starter/qbs/_index.md Provides an example of how to configure QBS to use a custom base repository implementation by setting the `spec.mapper.repository-base-class` property in the application's configuration file. ```yaml spec: mapper: repository-base-class: com.example.MyRepositoryImpl ``` -------------------------------- ### Example Log Output with Default Logger Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/logging/_index.md Demonstrates the detailed log output when the SpecMapper logger is set to debug, showing the Abstract Syntax Tree (AST) of the specification. ```text DEBUG 20297 --- [ main] t.c.softleader.data.jpa.spec.SpecMapper : --- Spec AST --- +- [CustomerCriteria]: my.package.CustomerCriteria | +-[CustomerCriteria.firstname]: @Spec(value=Equals, path=, not=false) -> Equals[path=name, value=matt] | +-[CustomerCriteria.address]: my.package.AddressCriteria (NestedSpecificationResolver) | | +-[AddressCriteria.county]: @Spec(value=Equals, path=, not=false) -> null | | +-[AddressCriteria.city]: @Spec(value=Equals, path=, not=false) -> Equals[path=name, value=Taipei] | \-[CustomerCriteria.address]: Conjunction[specs=[Equals[path=city, value=Taipei]]] \-[CustomerCriteria]: Conjunction[specs=[Equals[path=name, value=matt], Conjunction[specs=[Equals[path=city, value=Taipei]]]]] ``` -------------------------------- ### Built-in @Spec: NotEquals Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in NotEquals specification for any field type. ```java @Spec(NotEquals.class) String firstname; ``` -------------------------------- ### Generated SQL for Like Condition Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/spec/_index.md Provides an example of the SQL query generated when using the 'Like' specification for a field. ```sql ... where x.firstname like '%Hello%' ``` -------------------------------- ### Using QueryBySpecExecutor in Service Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/starter/qbs/_index.md Shows an example of how to use the `findBySpec` method from `QueryBySpecExecutor` within a Spring service class. ```java @Service public class PersonService { @Autowired PersonRepository personRepository; public List findPeople(PersonCriteria criteria) { return personRepository.findBySpec(criteria); } } ``` -------------------------------- ### Built-in @Spec: Like Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in Like specification for String fields, supporting wildcard matching. ```java @Spec(Like.class) String firstname; ``` -------------------------------- ### Example Log Output with Impersonation Logger Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/logging/_index.md Illustrates the log output when the ASTWriterFactory is set to impersonation, using the domain object's logger (e.g., 'my.package.CustomerCriteria') for logging. ```text DEBUG 20297 --- [ main] my.package.CustomerCriteria : --- Spec AST --- ``` -------------------------------- ### Built-in @Spec: LessThanEqual Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in LessThanEqual specification for Comparable types. ```java @Spec(LessThanEqual.class) Integer age; ``` -------------------------------- ### Field-Level Join Fetch Example Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/fetch/_index.md Demonstrates applying @JoinFetch directly to a field within a POJO, allowing for inline specification of join paths and aliases. ```java @Data public class CustomerOrderCriteria { @Spec String name; @JoinFetch(path = "orders", alias = "o") @Spec(path = "o.itemName", value = In.class) Collection items; } ``` -------------------------------- ### Using QueryBySpecExecutor in Service Layer Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/starter/qbs/_index.md Shows how to utilize the QueryBySpecExecutor methods from a service class by autowiring the repository that implements the executor. This example demonstrates fetching a list of people based on criteria. ```java @Service public class PersonService { @Autowired PersonRepository personRepository; public List findPeople(PersonCriteria criteria) { return personRepository.findBySpec(criteria); } } ``` -------------------------------- ### Single Join Fetch Example Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/fetch/_index.md Demonstrates how to use @JoinFetch on a class to fetch one-to-many related entities (Orders) when retrieving a Customer. It shows the POJO definition and the resulting SQL query. ```java class Customer { @OneToMany(fetch = LAZY, cascade = ALL) @JoinColumn(name = "order_id") private Collection orders; } @Entity class Order { private String itemName; } ``` ```java @Data @JoinFetch(path = "orders") class CustomerOrderCriteria { @Spec String name; } ``` ```sql select distinct customer0_.* ..., orders1_.* ... from customer customer0_ inner outer join orders orders1_ on customer0_.id=orders1_.order_id where customer0_.name=? ``` -------------------------------- ### Built-in @Spec: LessThan Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in LessThan specification for Comparable types. ```java @Spec(LessThan.class) Integer age; ``` -------------------------------- ### Single Join Fetch Example Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/fetch/_index.md Demonstrates how to use @JoinFetch on a POJO to fetch associated Order data when retrieving Customer entities. It shows the annotation usage and the resulting SQL query. ```java @Entity class Customer { @OneToMany(fetch = LAZY, cascade = ALL) @JoinColumn(name = "order_id") private Collection orders; } @Entity class Order { private String itemName; } @Data @JoinFetch(path = "orders") class CustomerOrderCriteria { @Spec String name; } ``` ```sql select distinct customer0_.* ..., orders1_.* ... from customer customer0_ inner outer join orders orders1_ on customer0_.id=orders1_.order_id where customer0_.name=? ``` -------------------------------- ### Multi Join Fetches Example Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/fetch/_index.md Illustrates using @JoinFetches to define multi-level joins, connecting Customer to Order and then to Tag entities. It shows filtering by tag names. ```java @Entity class Customer { @OneToMany(cascade = ALL, fetch = LAZY) @JoinColumn(name = "order_id") private Set orders; } @Entity class Order { @ManyToMany(cascade = ALL, fetch = LAZY) private Set tags; } @Entity class Tag { private String name; } @Data class CustomerOrderTagCriteria { @JoinFetches({ @JoinFetch(path = "orders", alias = "o"), @JoinFetch(path = "o.tags", alias = "t") }) @Spec(path = "t.name", value = In.class) Collection tags; } ``` ```sql select distinct customer0_.* ..., orders1_.* ..., tag3_.* ... from customer customer0_ inner join orders orders1_ on customer0_.id=orders1_.order_id inner join orders_tags tags2_ on orders1_.id=tags2_.order_id inner join tag tag3_ on tags2_.tags_id=tag3_.id where tag3_.name in (?) ``` -------------------------------- ### Built-in @Spec Types and JPQL Snippets Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/simple/_index.md Provides a comprehensive list of supported @Spec types, their corresponding Java field types, and example JPQL snippets. ```APIDOC Equals: Supported field type: *Any* Sample: `@Spec(Equals.class) String firstname;` JPQL snippet: `... where x.firstname = ?` NotEquals: Supported field type: *Any* Sample: `@Spec(NotEquals.class) String firstname;` JPQL snippet: `... where x.firstname <> ?` Between: Supported field type: *Iterable of Comparable* (Expected exact **2 elements** in *Iterable*) Sample: `@Spec(Between.class) List age;` JPQL snippet: `... where x.age between ? and ?` LessThan: Supported field type: *Comparable* Sample: `@Spec(LessThan.class) Integer age;` JPQL snippet: `... where x.age < ?` LessThanEqual: Supported field type: *Comparable* Sample: `@Spec(LessThanEqual.class) Integer age;` JPQL snippet: `... where x.age <= ?` GreaterThan: Supported field type: *Comparable* Sample: `@Spec(GreaterThan.class) Integer age;` JPQL snippet: `... where x.age > ?` GreaterThanEqual: Supported field type: *Comparable* Sample: `@Spec(GreaterThanEqual.class) Integer age;` JPQL snippet: `... where x.age >= ?` After: Supported field type: *Comparable* Sample: `@Spec(After.class) LocalDate startDate;` JPQL snippet: `... where x.startDate > ?` Before: Supported field type: *Comparable* Sample: `@Spec(Before.class) LocalDate startDate;` JPQL snippet: `... where x.startDate < ?` IsNull: Supported field type: *Boolean* Sample: `@Spec(IsNull.class) Boolean age;` JPQL snippet: `... where x.age is null` *(if true)* JPQL snippet: `... where x.age not null` *(if false)* NotNull: Supported field type: *Boolean* Sample: `@Spec(NotNull .class) Boolean age;` JPQL snippet: `... where x.age not null` *(if true)* JPQL snippet: `... where x.age is null` *(if false)* Like: Supported field type: *String* Sample: `@Spec(Like.class) String firstname;` JPQL snippet: `... where x.firstname like %?%` NotLike: Supported field type: *String* Sample: `@Spec(NotLike.class) String firstname;` JPQL snippet: `... where x.firstname not like %?%` StartingWith: Supported field type: *String* Sample: `@Spec(StartingWith.class) String firstname;` JPQL snippet: `... where x.firstname like ?%` EndingWith: Supported field type: *String* Sample: `@Spec(EndingWith.class) String firstname;` JPQL snippet: `... where x.firstname like %?` In: Supported field type: *Iterable of Any* Sample: `@Spec(In.class) Set firstname;` JPQL snippet: `... where x.firstname in (?, ?, ...)` NotIn: Supported field type: *Iterable of Any* Sample: `@Spec(NotIn.class) Set firstname;` JPQL snippet: `... where x.firstname not in (?, ?, ...)` True: Supported field type: *Boolean* Sample: `@Spec(True.class) Boolean active;` JPQL snippet: `... where x.active = true` *(if true)* JPQL snippet: `... where x.active = false` *(if false)* False: Supported field type: *Boolean* Sample: `@Spec(False.class) Boolean active;` JPQL snippet: `... where x.active = false` *(if true)* JPQL snippet: `... where x.active = true` *(if false)* HasLength: Supported field type: *Boolean* Sample: `@Spec(HasLength.class) Boolean firstname;` JPQL snippet: `... where x.firstname is not null and character_length(x.firstname)>0` *(if true)* JPQL snippet: `... where not(x.firstname is not null and character_length(x.firstname)>0)` *(if false)* HasText: Supported field type: *Boolean* Sample: `@Spec(HasText.class) Boolean firstname;` JPQL snippet: `... where x.firstname is not null and character_length(trim(BOTH from x.firstname))>0` *(if true)* JPQL snippet: `... where not(where x.firstname is not null and character_length(trim(BOTH from x.firstname))>0)` *(if false)* ``` -------------------------------- ### Custom SpecMapper Configuration Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/starter/config/_index.md Provides an example of how to completely customize the SpecMapper by registering a custom implementation as a Spring @Bean. This approach takes precedence over other configurations. ```java import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration class MyConfig { @Bean SpecMapper mySpecMapper() { return SpecMapper.builder() // Add your custom configurations here . ... .build(); } } ``` -------------------------------- ### Join Fetch with Clause Example Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/fetch/_index.md Shows how to use @JoinFetch with a 'with clause' to filter both customer and order names. It demonstrates filtering by order item name using the 'In' specification. ```java @Data @JoinFetch(path = "orders", alias = "o") public class CustomerOrderCriteria { @Spec String name; @Spec(path = "o.itemName", value = In.class) Collection items; } ``` ```sql select distinct customer0_.* ..., orders1_.* ... from customer customer0_ inner outer join orders orders1_ on customer0_.id=orders1_.order_id where customer0_.name=? and orders1_.item_name in (?) ``` -------------------------------- ### Using Custom Specification with @Spec Annotation Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/simple/_index.md Shows how to use a custom `SimpleSpecification` implementation (`MaxCustomerCreatedTime`) with the `@Spec` annotation in a POJO. This example defines a `CustomerCriteria` class and demonstrates converting it to a JPA `Specification` for repository querying. ```java @Data public class CustomerCriteria { @Spec(MaxCustomerCreatedTime.class) String maxBy; } var criteria = new CustomerCriteria(); criteria.setMaxBy("firstname"); var spec = mapper.toSpec(criteria, Customer.class); repository.findAll(spec); ``` -------------------------------- ### Repeatable @Join Annotations Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/join/_index.md Explains that @Join can be used repeatedly instead of @Joins for defining multi-level joins, providing an equivalent example. ```java @Join(path = "orders", alias = "o") @Join(path = "o.tags", alias = "t") public class CustomerOrderCriteria {} ``` -------------------------------- ### Alias Usage Rules and Examples Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/join/_index.md Details the rules for using the @Join#alias attribute, including sharing within a POJO, uniqueness, default alias generation, and handling of dots in aliases. ```java @Join(path = "orders") // default alias is "orders" @Join(path = "orders.tags") // default alias is "orders_tags" @Spec(path = "orders_tags.name", value = In.class) ``` -------------------------------- ### Implement SimpleSpecification for Custom Predicates Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/simple/_index.md Demonstrates how to extend `SimpleSpecification` to create custom query logic. This example shows how to find the maximum creation time for a given entity based on a specific field, using Java's JPA Criteria API. ```java public class MaxCustomerCreatedTime extends SimpleSpecification { // This is the required constructor, the modifier can be public, protected, default, or private protected MaxCustomerCreatedTime(Context context, String path, Object value) { super(context, path, value); } @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder builder) { // The following provides an example implementation for the subquery var subquery = query.subquery(LocalDateTime.class); var subroot = subquery.from(Customer.class); subquery.select(builder.greatest(subroot.get("createdTime").as(LocalDateTime.class))) .where(builder.equal(root.get((String) value), subroot.get((String) value))); return builder.equal(root.get("createdTime"), subquery); } } ``` -------------------------------- ### Generated SQL for MaxCreatedTime Specification Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/customize/_index.md Shows an example of the SQL query generated by the `SpecMapper` when using the custom `@MaxCreatedTime` annotation, illustrating the subquery used to find the maximum creation time based on a specified field. ```sql ... where customer0_.created_time=( select max(customer1_.created_time) from customer customer1_ where customer0_.firstname=customer1_.firstname ) ``` -------------------------------- ### Register Custom ASTWriterFactory Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/starter/config/_index.md Provides an example of registering a custom ASTWriterFactory as a Spring @Bean. This allows for full customization of how the Abstract Syntax Tree is written, enhancing logging capabilities. ```java @Configuration class MyConfig { @Bean ASTWriterFactory myASTWriterFactory() { return ...; } } ``` -------------------------------- ### Incorrect Join Fetch Order Example Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/fetch/_index.md Highlights the importance of correct ordering for @JoinFetch annotations, demonstrating an incorrect sequence that would lead to alias resolution errors. ```java @Data class CustomerOrderTagCriteria { @JoinFetch(path = "o.tags", alias = "t") // "o" alias will be not exist during processing this @Join @JoinFetch(path = "orders", alias = "o") @Spec(path = "t.name", value = In.class) Collection tagNames; } ``` -------------------------------- ### CustomerCriteria POJO with Spec Annotation Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/spec/_index.md Shows an example of a Plain Old Java Object (POJO) used for defining query criteria, utilizing the @Spec annotation for mapping fields to query conditions. ```java @Data public class CustomerCriteria { @Spec(Like.class) String firstname; } ``` -------------------------------- ### Incorrect Join Fetch Order Example Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/fetch/_index.md Highlights an incorrect ordering of @JoinFetch annotations, demonstrating that annotations must be processed in the correct join sequence to avoid alias resolution errors. ```java @Data class CustomerOrderTagCriteria { @JoinFetch(path = "o.tags", alias = "t") // "o" alias will be not exist during processing this @Join @JoinFetch(path = "orders", alias = "o") @Spec(path = "t.name", value = In.class) Collection tagNames; } ``` -------------------------------- ### Logging Configuration Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/starter/_index.md Configure the logging level for the specification-mapper-starter to 'info' for detailed output. ```yaml logging: level: tw.com.softleader.data.jpa.spec.starter: info ``` -------------------------------- ### Customizing Base Repository with QueryBySpecExecutorAdapter Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/starter/qbs/_index.md Illustrates how to implement `QueryBySpecExecutorAdapter` to customize the base repository, allowing for single inheritance while retaining original functionality. ```java class MyRepositoryImpl extends SimpleJpaRepository implements QueryBySpecExecutorAdapter { @Setter @Getter private SpecMapper specMapper; private final EntityManager entityManager; MyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); // Keep the EntityManager around to be used from the newly introduced methods. this.entityManager = entityManager; } @Override public Class getDomainClass() { return super.getDomainClass(); } @Transactional public S mySave(S entity) { // implementation goes here } } ``` -------------------------------- ### Build Static Site Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/README.md Executes the Hugo build command to generate the static website files. The output is placed in the 'public' folder. ```console $ hugo ``` -------------------------------- ### Built-in @Spec: EndingWith Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in EndingWith specification for String fields. ```java @Spec(EndingWith.class) String firstname; ``` -------------------------------- ### Maven Dependency Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/starter/_index.md Add this dependency to your pom.xml to include the specification-mapper-starter. ```xml tw.com.softleader.data.jakarta specification-mapper-starter ${specification-mapper.version} ``` -------------------------------- ### Built-in @Spec: GreaterThan Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in GreaterThan specification for Comparable types. ```java @Spec(GreaterThan.class) Integer age; ``` -------------------------------- ### SpecMapper Initialization Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/spec/_index.md Demonstrates how to build and initialize a SpecMapper instance using its builder pattern. ```java var mapepr = SpecMapper.builder().build(); ``` -------------------------------- ### Built-in @Spec: Equals Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in Equals specification for any field type. ```java @Spec(Equals.class) String firstname; ``` -------------------------------- ### Build SpecMapper Instance Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/spec/_index.md Demonstrates how to create an instance of SpecMapper using its builder pattern. This is the initial step before performing any Spec operations. ```java var mapepr = SpecMapper.builder().build(); ``` -------------------------------- ### Built-in @Spec: GreaterThanEqual Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in GreaterThanEqual specification for Comparable types. ```java @Spec(GreaterThanEqual.class) Integer age; ``` -------------------------------- ### Maven Dependency Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/starter/_index.md Add this dependency to your pom.xml to include the specification-mapper-starter in your Maven project. ```xml tw.com.softleader.data.jakarta specification-mapper-starter ${specification-mapper.version} ``` -------------------------------- ### Built-in @Spec: After Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in After specification for Comparable types, typically dates. ```java @Spec(After.class) LocalDate startDate; ``` -------------------------------- ### Java Module Requirements Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/starter/_index.md Declare these module requirements in your Java module-info.java file when using the starter. ```java requires specification.mapper; requires specification.mapper.starter; requires jakarta.persistence; ``` -------------------------------- ### Customizing Base Repository with QueryBySpecExecutorAdapter Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/starter/qbs/_index.md Explains how to customize the base repository implementation in QBS by implementing the QueryBySpecExecutorAdapter. This is useful when the application needs to retain its existing parent base repository due to Java's single inheritance. ```java class MyRepositoryImpl extends SimpleJpaRepository implements QueryBySpecExecutorAdapter { @Setter @Getter private SpecMapper specMapper; private final EntityManager entityManager; MyRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); // Keep the EntityManager around to used from the newly introduced methods. this.entityManager = entityManager; } @Override public Class getDomainClass() { return super.getDomainClass(); } @Transactional public S mySave(S entity) { // implementation goes here } } ``` -------------------------------- ### Built-in @Spec: NotLike Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in NotLike specification for String fields, supporting wildcard matching. ```java @Spec(NotLike.class) String firstname; ``` -------------------------------- ### Built-in @Spec: Between Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in Between specification for Iterable of Comparable types. Requires exactly 2 elements in the Iterable. ```java @Spec(Between.class) List age; ``` -------------------------------- ### Negating @Spec Conditions Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/simple/_index.md Illustrates how to negate a specification using the 'not' attribute in the @Spec annotation, providing an example for the Between spec. ```java @Spec(value = Between.class, not = true) Collection age; ``` -------------------------------- ### Logging Configuration Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/starter/_index.md Configure the logging level for the specification-mapper-starter in your application's logging configuration. ```yaml logging: level: tw.com.softleader.data.jpa.spec.starter: info ``` -------------------------------- ### Built-in @Spec: In Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in In specification for Iterable types, checking if a field's value is present in the provided collection. ```java @Spec(In.class) Set firstname; ``` -------------------------------- ### Generated SQL for Max Creation Time Query Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/simple/_index.md Illustrates the SQL query generated by the custom `SimpleSpecification` implementation. This SQL fetches customers by their first name, selecting the one with the maximum creation time for each unique first name. ```sql ... where customer0_.created_time=( select max(customer1_.created_time) from customer customer1_ where customer0_.firstname=customer1_.firstname ) ``` -------------------------------- ### Generated SQL for Max Customer Created Time Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Illustrates the SQL query generated by applying the custom specification, showing the subquery used to find the maximum creation time for customers with the same first name. ```sql ... where customer0_.created_time=( select max(customer1_.created_time) from customer customer1_ where customer0_.firstname=customer1_.firstname ) ``` -------------------------------- ### Built-in @Spec: NotNull Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in NotNull specification for Boolean fields. If true, checks for not null; if false, checks for null. ```java @Spec(NotNull .class) Boolean age; ``` -------------------------------- ### Java Module Requirements Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/starter/_index.md Declare the necessary module requirements for using the specification-mapper-starter in a Java Module System environment. ```java requires specification.mapper; requires specification.mapper.starter; requires jakarta.persistence; ``` -------------------------------- ### Built-in @Spec: IsNull Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in IsNull specification for Boolean fields. If true, checks for null; if false, checks for not null. ```java @Spec(IsNull.class) Boolean age; ``` -------------------------------- ### Built-in @Spec: NotIn Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in NotIn specification for Iterable types, checking if a field's value is not present in the provided collection. ```java @Spec(NotIn.class) Set firstname; ``` -------------------------------- ### Built-in @Spec: False Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in False specification for Boolean fields. If true, checks if the field is false; if false, checks if the field is true. ```java @Spec(False.class) Boolean active; ``` -------------------------------- ### QueryBySpecExecutor Interface Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/starter/qbs/_index.md The `QueryBySpecExecutor` interface provides methods for querying data using specifications. It includes methods for finding entities by spec, with sorting, and with pagination. ```java public interface QueryBySpecExecutor { List findBySpec(Object spec); List findBySpec(Object spec, Sort sort); Page findBySpec(Object spec, Pageable pageable); // … more functionality omitted. } ``` -------------------------------- ### Define POJO and Convert to Specification Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/customize/_index.md Shows how to define a POJO (`CustomerCriteria`) with the custom annotation and use the `SpecMapper` to convert it into a `Specification` for repository queries. ```java @Data public class CustomerCriteria { @MaxCreatedTime(from = Customer.class) String maxBy; } var criteria = new CustomerCriteria(); criteria.setMaxBy("firstname"); var spec = mapper.toSpec(criteria, Customer.class); repository.findAll(spec); ``` -------------------------------- ### Built-in @Spec: HasText Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in HasText specification for Boolean fields. If true, checks if the string has non-whitespace characters; if false, checks the opposite. ```java @Spec(HasText.class) Boolean firstname; ``` -------------------------------- ### Built-in @Spec: HasLength Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in HasLength specification for Boolean fields. If true, checks if the string has length > 0; if false, checks the opposite. ```java @Spec(HasLength.class) Boolean firstname; ``` -------------------------------- ### Built-in @Spec: True Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/simple/_index.md Example of using the built-in True specification for Boolean fields. If true, checks if the field is true; if false, checks if the field is false. ```java @Spec(True.class) Boolean active; ``` -------------------------------- ### Combining Nested Specs with @Or Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/mapper/nested/_index.md Illustrates how to use the `@Or` annotation on a nested spec field to control the combination logic. In this example, the `firstname` condition is OR-ed with the combined conditions from the nested `AddressCriteria`. ```java @Data public class CustomerCriteria { @Spec(Like.class) String firstname; @Or @NestedSpec AddressCriteria address; } @Data public class AddressCriteria { @Spec String county; @Spec String city; } ``` -------------------------------- ### Integrating QueryBySpecExecutor with Repository Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/docs/starter/qbs/_index.md Demonstrates how to integrate the QueryBySpecExecutor into a custom Spring Data JPA repository by extending it alongside JpaRepository. This allows direct use of QBS methods within repository implementations. ```java public interface PersonRepository extends JpaRepository, QueryBySpecExecutor { ... } ``` -------------------------------- ### Generated SQL for Max Created Time Query Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/en/docs/mapper/customize/_index.md Illustrates the SQL query generated by the `SpecMapper` when using the custom `@MaxCreatedTime` resolver, demonstrating the subquery logic. ```sql select ... from customer customer0_ where customer0_.created_time=( select max(customer1_.created_time) from customer customer1_ where customer0_.firstname=customer1_.firstname ) ``` -------------------------------- ### Specification Mapper Features Source: https://github.com/softleader/specification-mapper/blob/jakarta/site/content/zh/_index.md Specification Mapper offers several key features for dynamic query generation and integration with Spring Boot. It automatically reads object fields and generates query conditions based on annotations, saving time and reducing the need for custom query code. The starter integrates seamlessly with Spring Boot without requiring configuration. It also supports flexible skipping strategies, ignoring fields that are null, empty, or lack annotations, which helps manage query conditions and reduce redundant conditions. Furthermore, it allows for customizable annotations like Equals, Like, and Between via the @Spec annotation, and supports custom Specification implementations for complex queries such as subqueries. Specifications can also be combined at the class and field levels using @And and @Or annotations for flexible query logic. ```Java /** * Specification Mapper is a tool that helps build Specifications by reading POJO fields and converting them into Specifications through simple and memorable annotations. * It also provides multiple extension points for easy customization and implementation of custom logic. * * Features: * - Dynamic Query Generation: Automatically reads object fields and generates query conditions based on annotations. * - Integration with Spring Boot: Seamless integration via `specification-mapper-starter`. * - Flexible Skipping Strategy: Ignores null, empty, or unannotated fields. * - Customizable Annotations: Supports @Spec for Equals, Like, Between, etc. * - Support for Custom Specifications: Allows custom @Spec implementations for complex queries (e.g., subqueries). * - Combining Specifications: Uses @And and @Or annotations to combine specifications at class and field levels. */ ```