Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add interfaces for CRUD services #20743

Merged
merged 36 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
9f5c5ec
feat: Add interfaces for CRUD services
Artur- Dec 18, 2024
8ebca25
Move jpa related classes into separate package
Artur- Dec 18, 2024
ffbdff0
format
Artur- Dec 18, 2024
e840b56
Changes VS Code left unsaved
Artur- Dec 18, 2024
a32c1ff
Make JpaFilterConverter work without the EntityManager.
peholmst Dec 18, 2024
7fd7590
Leave out FilterTransformer as it is not used anywhere
Artur- Dec 18, 2024
2f7a5db
Simplify as JpaFilterConverter now only has one helper method
Artur- Dec 18, 2024
fcaf974
Tests from Hilla
Artur- Dec 18, 2024
5a2d557
Add missing dependencies to make Spring Data tests work.
peholmst Dec 18, 2024
14d7d87
Change expected exception
Artur- Dec 18, 2024
94436bf
fix test
Artur- Dec 18, 2024
c1a9062
final
Artur- Dec 18, 2024
b00f394
Use jspecify instead of custom annotations
Artur- Dec 18, 2024
5163a3c
Util class not serializable
Artur- Dec 18, 2024
7645905
format
Artur- Dec 18, 2024
9d55027
Merge branch 'main' into crud-interfaces
Artur- Dec 18, 2024
2b8b150
For Hilla compat
Artur- Dec 18, 2024
8a9f6b6
fix test
Artur- Dec 18, 2024
230bbc5
Tweak
Artur- Dec 19, 2024
463bffb
Merge branch 'main' into crud-interfaces
Artur- Dec 19, 2024
10e9ead
Remove nullable
Artur- Dec 19, 2024
a032f73
Merge remote-tracking branch 'origin/main' into crud-interfaces
Artur- Dec 19, 2024
57ec343
Refactor to be more understandable and overrideable/extendable
Artur- Dec 20, 2024
9663c01
Merge branch 'main' into crud-interfaces
Artur- Dec 20, 2024
2cb9676
Fix test
Artur- Dec 20, 2024
3f9f7fe
format
Artur- Dec 20, 2024
0807c0f
Merge branch 'main' into crud-interfaces
Artur- Dec 20, 2024
5c83fcd
Merge branch 'main' into crud-interfaces
Artur- Jan 3, 2025
250212a
Merge branch 'main' into crud-interfaces
Artur- Jan 13, 2025
c272a43
Add convenience constructors
Artur- Jan 13, 2025
72f6548
Improve javadoc
Artur- Jan 13, 2025
9fa935e
Improve javadoc
Artur- Jan 13, 2025
98bee66
javadoc
Artur- Jan 13, 2025
c16124f
Add count()
Artur- Jan 13, 2025
ce450df
Revert "Add count()"
Artur- Jan 13, 2025
961675c
format
Artur- Jan 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 49 additions & 7 deletions vaadin-spring/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jspecify</groupId>
<artifactId>jspecify</artifactId>
<version>1.0.0</version>
</dependency>


<dependency>
<groupId>com.vaadin</groupId>
Expand Down Expand Up @@ -97,6 +103,26 @@
<artifactId>spring-data-commons</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<scope>provided</scope>
<exclusions>
<exclusion>
<artifactId>aspectjrt</artifactId>
<groupId>org.aspectj</groupId>
</exclusion>
<exclusion>
<artifactId>jcl-over-slf4j</artifactId>
<groupId>org.slf4j</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>jakarta.persistence</groupId>
<artifactId>jakarta.persistence-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>flow-data</artifactId>
Expand All @@ -120,6 +146,22 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
Expand Down Expand Up @@ -189,13 +231,13 @@

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.vaadin.flow.spring.data;

import org.jspecify.annotations.Nullable;

import com.vaadin.flow.spring.data.filter.Filter;

/**
* A service that can count the number of items with a given filter.
*/
public interface CountService {

/**
* Counts the number of items that match the given filter.
*
* @param filter
* the filter, or {@code null} to use no filter
* @return the number of items in the service that match the filter
*/
public long count(@Nullable Filter filter);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.vaadin.flow.spring.data;

/**
* A service that can create, read, update, and delete a given type of object.
*
* @param <T>
* the type of object to manage
* @param <ID>
* the type of the object's identifier
*/
public interface CrudService<T, ID> extends ListService<T>, FormService<T, ID> {
Artur- marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.vaadin.flow.spring.data;

/**
* A service that can update and delete a given type of object.
*
* @param <T>
* the type of object to manage
* @param <ID>
* the type of the object's identifier
*
*/
public interface FormService<T, ID> {

/**
* Saves the given object and returns the (potentially) updated object.
* <p>
* If you store the object in a SQL database, the returned object might have
* a new id or updated consistency version.
*
* @param value
* the object to save
* @return the fresh object; will never be {@literal null}.
*/
T save(T value);
peholmst marked this conversation as resolved.
Show resolved Hide resolved

/**
* Deletes the object with the given id.
*
* @param id
* the id of the object to delete
*/
void delete(ID id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.vaadin.flow.spring.data;

import java.util.Optional;

/**
* A service that can fetch the given type of object.
*/
public interface GetService<T, ID> {

/**
* Gets the object with the given id.
*
* @param id
* the id of the object
* @return the object, or an empty optional if no object with the given id
*/
Optional<T> get(ID id);

/**
* Checks if an object with the given id exists.
*
* @param id
* the id of the object
* @return {@code true} if the object exists, {@code false} otherwise
*/
default boolean exists(ID id) {
return get(id).isPresent();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.vaadin.flow.spring.data;

import java.util.List;

import org.jspecify.annotations.Nullable;
import org.springframework.data.domain.Pageable;

import com.vaadin.flow.spring.data.filter.Filter;

/**
* A service that can list the given type of object.
*
* @param <T>
* the type of object to list
*/
public interface ListService<T> {
/**
* Lists objects of the given type using the paging, sorting and filtering
* options provided in the parameters.
*
* @param pageable
* contains information about paging and sorting
* @param filter
* the filter to apply or {@code null} to not filter
* @return a list of objects or an empty list if no objects were found
*/
List<T> list(Pageable pageable, @Nullable Filter filter);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.vaadin.flow.spring.data.filter;

import java.util.List;

/**
* A filter that requires all children to pass.
* <p>
* Custom filter implementations need to handle this filter by running all child
* filters and verifying that all of them pass.
*/
public class AndFilter extends Filter {

private List<Filter> children;

/**
* Create an empty filter.
*/
public AndFilter() {
// Empty constructor is needed for serialization
}

/**
* Create a filter with the given children.
*
* @param children
* the children of the filter
*/
public AndFilter(Filter... children) {
setChildren(List.of(children));
}

public List<Filter> getChildren() {
return children;
}

public void setChildren(List<Filter> children) {
this.children = children;
}

@Override
public String toString() {
return getClass().getSimpleName() + " [children=" + children + "]";
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.vaadin.flow.spring.data.filter;

import java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;

/**
* Superclass for all filters to be used with CRUD services. This specific class
* is never used, instead a filter instance will be one of the following types:
* <ul>
* <li>{@link AndFilter} - Contains a list of nested filters, all of which need
* to pass.</li>
* <li>{@link OrFilter} - Contains a list of nested filters, of which at least
* one needs to pass.</li>
* <li>{@link PropertyStringFilter} - Matches a specific property, or nested
* property path, against a filter value, using a specific operator.</li>
* </ul>
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
@JsonSubTypes({ @Type(value = OrFilter.class, name = "or"),
@Type(value = AndFilter.class, name = "and"),
@Type(value = PropertyStringFilter.class, name = "propertyString") })
public class Filter implements Serializable {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the Filter classes meant to mainly be created by Jackson, or is there a situation where a developer may want to create them in Java as well? Also why are the Filter classes mutable and not immutable?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to create some examples to show usage before this is merged. I think they should be public and ok to create by hand but they don't need to be mutable

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added convenience constructors. Some usage examples in https://github.com/Artur-/flow-crud-demo/tree/main/src/main/java/com/example/demo


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.vaadin.flow.spring.data.filter;

import java.util.List;

/**
* A filter that requires at least one of its children to pass.
* <p>
* Custom filter implementations need to handle this filter by running all child
* filters and verifying that at least one of them passes.
*/
public class OrFilter extends Filter {

private List<Filter> children;

/**
* Create an empty filter.
*/
public OrFilter() {
// Empty constructor is needed for serialization
}

/**
* Create a filter with the given children.
*
* @param children
* the children of the filter
*/
public OrFilter(Filter... children) {
setChildren(List.of(children));
}

public List<Filter> getChildren() {
return children;
}

public void setChildren(List<Filter> children) {
this.children = children;
}

@Override
public String toString() {
return getClass().getSimpleName() + " [children=" + children + "]";
}

}
Loading
Loading