I want to dynamic search with Criteria API in Java.
In the code I wrote, we need to write each entity in the url bar in JSON. I don't want to write "plaka".
The URL : <localhost:8080/api/city/query?city=Ankara&plaka=> I want to only "city" or "plaka"
Here we need to write each entity, even if we are going to search with only 1 entity. Type Entity and it should be empty.
My code is as below. Suppose there is more than one entity, what I want to do is to search using a single entity it wants to search. As you can see in the photo, I don't want to write an entity that I don't need. can you help me what should I do?
My code in Repository
public interface CityRepository extends JpaRepository<City, Integer> , JpaSpecificationExecutor<City> {
}
My code in Service
#Service
public class CityServiceImp implements CityService{
private static final String CITY = "city";
private static final String PLAKA = "plaka";
#Override
public List<City> findCityByNameAndPlaka(String cityName, int plaka) {
GenericSpecification genericSpecification = new GenericSpecification<City>();
if (!cityName.equals("_"))
genericSpecification.add(new SearchCriteria(CITY,cityName, SearchOperation.EQUAL));
if (plaka != -1)
genericSpecification.add(new SearchCriteria(PLAKA,plaka, SearchOperation.EQUAL));
return cityDao.findAll(genericSpecification);
}
#Autowired
CityRepository cityDao;
My code in Controller
#RestController
#RequestMapping("api/city")
public class CityController {
#Autowired
private final CityService cityService;
public CityController(CityService cityService) {
this.cityService = cityService;
#GetMapping("/query")
public List<City> query(#RequestParam String city, #RequestParam String plaka){
String c = city;
int p;
if (city.length() == 0)
c = "_";
if (plaka.length() == 0) {
p = -1;
}
else
p = Integer.parseInt(plaka);
return cityService.findCityByNameAndPlaka(c,p);
}
My code in SearchCriteria
public class SearchCriteria {
private String key;
private Object value;
private SearchOperation operation;
public SearchCriteria(String key, Object value, SearchOperation operation) {
this.key = key;
this.value = value;
this.operation = operation;
}
public String getKey() {
return key;
}
public Object getValue() {
return value;
}
public SearchOperation getOperation() {
return operation;
}
My code in GenericSpecification
public class GenericSpecification<T> implements Specification<T> {
private List<SearchCriteria> list;
public GenericSpecification() {
this.list = new ArrayList<>();
}
public void add(SearchCriteria criteria){
list.add(criteria);
}
#Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Predicate> predicates = new ArrayList<>();
for (SearchCriteria criteria : list) {
if (criteria.getOperation().equals(SearchOperation.GREATER_THAN)) {
predicates.add(builder.greaterThan(
root.get(criteria.getKey()), criteria.getValue().toString()));
} else if (criteria.getOperation().equals(SearchOperation.LESS_THAN)) {
predicates.add(builder.lessThan(
root.get(criteria.getKey()), criteria.getValue().toString()));
} else if (criteria.getOperation().equals(SearchOperation.GREATER_THAN_EQUAL)) {
predicates.add(builder.greaterThanOrEqualTo(
root.get(criteria.getKey()), criteria.getValue().toString()));
} else if (criteria.getOperation().equals(SearchOperation.LESS_THAN_EQUAL)) {
predicates.add(builder.lessThanOrEqualTo(
root.get(criteria.getKey()), criteria.getValue().toString()));
} else if (criteria.getOperation().equals(SearchOperation.NOT_EQUAL)) {
predicates.add(builder.notEqual(
root.get(criteria.getKey()), criteria.getValue()));
} else if (criteria.getOperation().equals(SearchOperation.EQUAL)) {
predicates.add(builder.equal(
root.get(criteria.getKey()), criteria.getValue()));
} else if (criteria.getOperation().equals(SearchOperation.MATCH)) {
predicates.add(builder.like(
builder.lower(root.get(criteria.getKey())),
"%" + criteria.getValue().toString().toLowerCase() + "%"));
} else if (criteria.getOperation().equals(SearchOperation.MATCH_END)) {
predicates.add(builder.like(
builder.lower(root.get(criteria.getKey())),
criteria.getValue().toString().toLowerCase() + "%"));
}
}
return builder.and(predicates.toArray(new Predicate[0]));
}
My code in SearchOperation
public enum SearchOperation {
GREATER_THAN,
LESS_THAN,
GREATER_THAN_EQUAL,
LESS_THAN_EQUAL,
NOT_EQUAL,
EQUAL,
MATCH,
MATCH_END,
}
The good thing about the Criteria API is that you can use the CriteriaBuilder to build complex SQL statements based on the fields that you have. You can combine multiple criteria fields using and and or statements with ease.
How I approached something similar int he past is using a GenericDao class that takes a Filter that has builders for the most common operations (equals, qualsIgnoreCase, lessThan, greaterThan and so on). I actually have something similar in an open-source project I started: https://gitlab.com/pazvanti/logaritmical/-/blob/master/app/data/dao/GenericDao.java
https://gitlab.com/pazvanti/logaritmical/-/blob/master/app/data/filter/JPAFilter.java
Next, the implicit DAO class extends this GenericDao and when I want to do an operation (ex: find a user with the provided username) and there I create a Filter.
Now, the magic is in the filter. This is the one that creates the Predicate.
In your request, you will receive something like this: field1=something&field2=somethingElse and so on. The value can be preceded by the '<' or '>' if you want smaller or greater and you initialize your filter with the values. If you can retrieve the parameters as a Map<String, String>, even better.
Now, for each field in the request, you create a predicate using the helper methods from the JPAFilter class and return he resulted Predicate. In the example below I assume that you don't have it as a Map, but as individual fields (it is easy to adapt the code for a Map):
public class SearchFilter extends JPAFilter {
private Optional<String> field1 = Optional.empty();
private Optional<String> field2 = Optional.empty();
#Override
public Predicate getPredicate(CriteriaBuilder criteriaBuilder, Root root) {
Predicate predicateField1 = field1.map(f -> equals(criteriaBuilder, root, "field1", f)).orElse(null);
Predicate predicateField2 = field2.map(f -> equals(criteriaBuilder, root, "field2", f)).orElse(null);
return andPredicateBuilder(criteriaBuilder, predicateField1, predicateField2);
}
}
Now, I have the fields as Optional since in this case I assumed that you have them as Optional in your request mapping (Spring has this) and I know it is a bit controversial to have Optional as input params, but in this case I think it is acceptable (more on this here: https://petrepopescu.tech/2021/10/an-argument-for-using-optional-as-input-parameters/)
The way the andPredicateBuilder() is made is that it works properly even if one of the supplied predicates is null. Also, I made s simple mapping function, adjust to include for < and >.
Now, in your DAO class, just supply the appropriate filter:
public class SearchDao extends GenericDAO {
public List<MyEntity> search(Filter filter) {
return get(filter);
}
}
Some adjustments need to be made (this is just starter code), like an easier way to initialize the filter (and doing this inside the DAO) and making sure that that the filter can only by applied for the specified entity (probably using generics, JPAFIlter<T> and having SearchFilter extends JPAFilter<MyEntity>). Also, some error handling can be added.
One disadvantage is that the fields have to match the variable names in your entity class.
Related
Having a hard time figuring out how to utilize #ForAll in jqwik on a #Provide function accepting a collection.
Consider:
// domain model
public class Name {
public final String first;
public final String last;
public Name(String f, String l) {
this.first = f;
this.last = l;
}
}
// jqwik domain context
public class NameDomain extends DomainContextBase {
#Provide
public Arbitrary<Name> arbName() {
return Combinators.combine(
Arbitraries.strings().alpha(),
Arbitraries.strings().alpha()
).as(Name::new);
}
}
// properties test
public class NameProperties {
// obviously a made-up use case, just demonstrating the issue
#Provide
#Domain(NameDomain.class)
public Arbitrary<Set<String>> namesToParse(
#ForAll #Size(min = 1, max = 4) Set<Name> names) {
// ... code here
}
#Property
public void namesAreParsed(#ForAll("namesToParse") Set<String> names) {
// ... code here
}
}
When running this, I end up with:
net.jqwik.api.CannotFindArbitraryException:
Cannot find an Arbitrary for Parameter of type [#net.jqwik.api.ForAll(value="", supplier=net.jqwik.api.ArbitrarySupplier$NONE.class) #net.jqwik.api.constraints.Size(value=0, max=4, min=1) Set] in method [public net.jqwik.api.Arbitrary mypackage.NameProperties.namesToParse(java.util.Set)]
Very similar issues attempting to use #UniqueElements List<Name> instead. What am I missing here?
What you are missing is that the #Domain annotation can only be applied to property methods or their container class. What should therefore work is:
#Property
#Domain(NameDomain.class)
public void namesAreParsed(#ForAll("namesToParse") Set<String> names) {
// ... code here
}
or
#Domain(NameDomain.class)
class NameProperties { ... }
That said, you should be aware that using #ForAll params in a providing method will always use flat mapping over the injected parameters.
Don't use that if you actually want to just map over or combine the injected parameters. In that case your providing method would look something like:
#Provide
public Arbitrary<Set<String>> namesToParse() {
SetArbitrary<Name> names = Arbitraries.defaultFor(Name.class)
.set().ofMinSize(1).ofMaxSize(4);
// Code here just an example of what could be done:
return names.mapEach((Set<Name> ignore, Name n) -> n.first + " " + n.last);
}
I have an OOP approach to calculating a special code. There is a list of strategies that uses the chain of responsibility approach to calculate my value;
interface ChainStrategy {
Strategy getNext();
String getCode(SomeDto dto);
default String getDefaultVlue() {
return "";
};
}
class StrategyA implements ChainStrategy {
Strategy next;
StrategyA() {}
StrategyA(Strategy next) {
this.next = next;
}
Strategy getNext() {
return next;
}
public String getCode(SomeDto dto) {
if(dto.isA()) {
String result = dto.getA();
//this code could be placed in the abstract class to fulfill DRY
if(result == null) {
if(next!=null) {
result = next.getCode(dto);
}
else {
result = getDefaultVlue();
}
}
return result;
}
}
class StrategyB implements ChainStrategy {
// mostly the same code with different result calculation logic
}
class Client {
ChainStrategy strategy = new StrategyA(new StrategyB());
System.out.println(strategy.getCode())
}
}
This is "Java < 8" code that meets SOLID principles and can be easily tested. Usually, the real logic is more complicated than just dto.getA()
But it is just a chain of functions so I rewrite it:
interface ChainStrategy {
String getCode(SomeDto dto);
}
class CombineStrategy implements ChainStrategy {
private static final Function<SomeDto, Optional<String>> STRATEGY_A = dto -> Optional.of(dto).filter(SomeDto::isA).map(SomeDto::getA());
private static final Function<SomeDto, Optional<String>> STRATEGY_B = dto -> Optional.of(dto).filter(SomeDto::isB).map(SomeDto::getB());
private static final Function<SomeDto, String> STRATEGY_DEFAULT = dto -> "";
String getCode(SomeDto dto) {
Stream.of(STRATEGY_A, STRATEGY_B).map(st->st.apply(dto))
.filter(Optional::isPresent)
.map(Optional::get)
.findFirst()
.orElseGet(() -> STRATEGY_DEFAULT.apply(dto));
}
}
And my questions:
This code has problems with a single responsibility and "open-close" principles. And I can't test my functions individually. But creating separate classes for my functions looks like an overhead. Do we need these principles in functional programming?
I can rewrite "String getCode" to another static function. And store all these functions as a static Util class. But I don't want to lose ability to dynamically substitute my ChainFunction in the runtime. How do people combine static functions and dynamic binding in functional languages?
I'm trying to map DTOs to entities. I created a service that only takes care of mapping objects - ObjectMapper. DTO objects have relationships with each other. When I map a single object, for example when I create User, Group, Note, everything works. But when I want to use a method that returns a Note with a specific ID - /notes/{id}, I get the following error.
Handler dispatch failed; nested exception is java.langStackOverflowError] with root cause
To get specific Note, I need to use this mapping method that also cause this error. As u can see, I have to also convert Group and Tags.
//Note
public NoteDTO NoteEntityToDtoGet(Note note) {
NoteDTO noteDTO = new NoteDTO();
noteDTO.setId(note.getId());
noteDTO.setTitle(note.getTitle());
noteDTO.setDescription(note.getDescription());
noteDTO.setGroup(GroupEntityToDtoGet(note.getGroup()));
noteDTO.setTags(TagConvertSet(note.getTags()));
return noteDTO;
}
When I don't have relationships defined as another DTO in the DTO class, but as an entity, everything works, since I don't have to convert the DTO to an entity.
Do you know where I'm making a mistake when mapping? Am I making a mistake in mapping multiple objects at once?
ObjectMapper
#Service
public class ObjectMapper {
//User
public UserDTO UserEntityToDtoGet(User user) {
UserDTO userDTO = new UserDTO();
userDTO.setId(user.getId());
userDTO.setName(user.getName());
userDTO.setEmail(user.getEmail());
userDTO.setGroup(user.getGroups());
return userDTO;
}
private UserCreationDTO UserEntityToDtoCreate(User user) {
UserCreationDTO userCreationDTO = new UserCreationDTO();
userCreationDTO.setName(user.getName());
userCreationDTO.setEmail(user.getEmail());
return userCreationDTO;
}
private User UserDtoToEntityCreate(UserCreationDTO userCreationDTO) {
User user = new User();
user.setName(userCreationDTO.getName());
user.setEmail(userCreationDTO.getEmail());
return user;
}
//Group
public GroupDTO GroupEntityToDtoGet(Group group) {
GroupDTO groupDTO = new GroupDTO();
groupDTO.setId(group.getId());
groupDTO.setName(group.getName());
groupDTO.setUser(UserEntityToDtoGet(group.getUser()));
groupDTO.setNotes(NoteConvertList(group.getNotes()));
groupDTO.setTags(TagConvertSet(group.getTags()));
return groupDTO;
}
public GroupCreationDTO GroupEntityToDtoCreate(Group group) {
GroupCreationDTO groupCreationDTO = new GroupCreationDTO();
groupCreationDTO.setName(group.getName());
groupCreationDTO.setUser(UserEntityToDtoGet(group.getUser()));
groupCreationDTO.setTags(TagConvertSet(group.getTags()));
return groupCreationDTO;
}
public Group GroupDtoToEntityCreate(GroupCreationDTO groupCreationDTO) {
Group group = new Group();
group.setName(groupCreationDTO.getName());
return group;
}
//Note
public NoteDTO NoteEntityToDtoGet(Note note) {
NoteDTO noteDTO = new NoteDTO();
noteDTO.setId(note.getId());
noteDTO.setTitle(note.getTitle());
noteDTO.setDescription(note.getDescription());
noteDTO.setGroup(GroupEntityToDtoGet(note.getGroup()));
noteDTO.setTags(TagConvertSet(note.getTags()));
return noteDTO;
}
public Note NoteDtoToEntityCreate(NoteCreationDTO noteCreationDTO) {
Note note = new Note();
note.setTitle(noteCreationDTO.getTitle());
note.setDescription(noteCreationDTO.getDescription());
return note;
}
public NoteCreationDTO NoteEntityToDtoCreate(Note note) {
NoteCreationDTO noteCreationDTO = new NoteCreationDTO();
noteCreationDTO.setTitle(note.getTitle());
noteCreationDTO.setDescription(note.getDescription());
return noteCreationDTO;
}
public List<NoteDTO> NoteConvertList(List<Note> note) {
return note.stream()
.map(this::NoteEntityToDtoGet)
.collect(Collectors.toList());
}
//Tag
public TagDTO TagEntityToDtoGet(Tag tag) {
TagDTO tagDTO = new TagDTO();
tagDTO.setId(tag.getId());
tagDTO.setName(tag.getName());
tagDTO.setNotes(tag.getNotes());
tagDTO.setGroups(tag.getGroups());
return tagDTO;
}
public TagCreationDTO TagEntityToDtoCreate(Tag tag) {
TagCreationDTO tagCreationDTO = new TagCreationDTO();
tagCreationDTO.setId(tag.getId());
tagCreationDTO.setName(tag.getName());
tagCreationDTO.setNotes(tag.getNotes());
return tagCreationDTO;
}
public Set<TagDTO> TagConvertSet(Set<Tag> groups) {
return groups.stream()
.map(this::TagEntityToDtoGet)
.collect(Collectors.toSet());
}
}
You get StackOverFlowError because you end up with infinite recursive methods call and your application creates infinite amount of objects, so you just run out of memory:
1) your NoteEntityToDtoGet method gets Note's group and calls GroupEntityToDtoGet method on the Group object;
2) in GroupEntityToDtoGet method you get all Group's notes and call NoteConvertList method on them, which calls NoteEntityToDtoGet on each of the 'Note'
3) step 1 again...
... the same cycle goes over and over without a stop until your stack memory, you know, overflows :)
So you should decide do your DTO classes really need to hold references to other entity collections.
I have the following repository with 2 custom query methods:
#Repository
public interface CropVarietyNameDao extends JpaRepository<CropVarietyName, Long> {
//https://stackoverflow.com/questions/25362540/like-query-in-spring-jparepository
Set<CropVarietyName> findAllByNameIgnoreCaseContainingOrScientificNameIgnoreCaseContaining(String varietyName, String scientificName);
Set<CropVarietyName> findAllByNameIgnoreCaseStartsWithOrScientificNameIgnoreCaseStartsWith(String varietyName, String scientificName);
}
I need to add an additional condtion, namely parent entity called crop deleted = false.
If i change a method to the following:
findAllByNameIgnoreCaseContainingOrScientificNameIgnoreCaseContainingAndCrop_deletedIsFalse(String varietyName, String scientificName);
Then most likely it will interpret the query as (Name containing OR (scientificNameContaining AND crop_deleted = false), but thats not how i want it.
It needs to be (NameContaining OR scientificNameContaining) AND crop_deleted = false)
My guess is that I have to add the AND crop_deleted part to both parts, but that seems inefficient. How can i write a method thats essentially (NameContaining OR scientificNameContaining) AND crop_deleted = false) without having to use #Query?
You could try JPA Specification -
#Repository
public interface CropVarietyNameDao extends JpaRepository<CropVarietyName, Long>,
JpaSpecificationExecutor<CropVarietyName> {
}
public class CropVarietySpecs {
public static Specification<CropVarietyName> cropPredicate(String varietyName, String sciName, boolean cropStatus) {
return new Specification<CropVarietyName>() {
#Override
public Predicate toPredicate(Root<CropVarietyName> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<>();
Predicate varietyContainingIgnoreCasePredicate = criteriaBuilder.like(criteriaBuilder.lower(root.get("<column_name>")), varietyName.toLowerCase());
Predicate scientificContainingIgnoreCasePredicate = criteriaBuilder.like(criteriaBuilder.lower(root.get("<column_name>")), sciName.toLowerCase());
Predicate cropStatusPredicate = criteriaBuilder.equal(root.get("<column_name>"), cropStatus);
predicates.add(varietyContainingIgnoreCasePredicate);
predicates.add(scientificContainingIgnoreCasePredicate);
criteriaBuilder.or(predicates.toArray(new Predicate[predicates.size()]));
return criteriaBuilder.and(cropStatusPredicate);
}
};
}
}
Then you can call the findAll() method of your repository like -
List<CropVarietyName> entities = cropVarietyNameDao.findAll(CropVarietySpecs.cropPredicate("varietyName", "sciName", false));
I need to build a process which will validate a record against ~200 validation rules. A record can be one of ~10 types. There is some segmentation from validation rules to record types but there exists a lot of overlap which prevents me from cleanly binning the validation rules.
During my design I'm considering a chain of responsibility pattern for all of the validation rules. Is this a good idea or is there a better design pattern?
Validation is frequently a Composite pattern. When you break it down, you want to seperate the what you want to from the how you want to do it, you get:
If foo is valid
then do something.
Here we have the abstraction is valid -- Caveat: This code was lifted from currrent, similar examples so you may find missing symbology and such. But this is so you get the picture. In addition, the
Result
Object contains messaging about the failure as well as a simple status (true/false).
This allow you the option of just asking "did it pass?" vs. "If it failed, tell me why"
QuickCollection
and
QuickMap
Are convenience classes for taking any class and quickly turning them into those respected types by merely assigning to a delegate. For this example it means your composite validator is already a collection and can be iterated, for example.
You had a secondary problem in your question: "cleanly binding" as in, "Type A" -> rules{a,b,c}" and "Type B" -> rules{c,e,z}"
This is easily managed with a Map. Not entirely a Command pattern but close
Map<Type,Validator> typeValidators = new HashMap<>();
Setup the validator for each type then create a mapping between types. This is really best done as bean config if you're using Java but Definitely use dependency injection
public interface Validator<T>{
public Result validate(T value);
public static interface Result {
public static final Result OK = new Result() {
#Override
public String getMessage() {
return "OK";
}
#Override
public String toString() {
return "OK";
}
#Override
public boolean isOk() {
return true;
}
};
public boolean isOk();
public String getMessage();
}
}
Now some simple implementations to show the point:
public class MinLengthValidator implements Validator<String> {
private final SimpleResult FAILED;
private Integer minLength;
public MinLengthValidator() {
this(8);
}
public MinLengthValidator(Integer minLength) {
this.minLength = minLength;
FAILED = new SimpleResult("Password must be at least "+minLength+" characters",false);
}
#Override
public Result validate(String newPassword) {
return newPassword.length() >= minLength ? Result.OK : FAILED;
}
#Override
public String toString() {
return this.getClass().getSimpleName();
}
}
Here is another we will combine with
public class NotCurrentValidator implements Validator<String> {
#Autowired
#Qualifier("userPasswordEncoder")
private PasswordEncoder encoder;
private static final SimpleResult FAILED = new SimpleResult("Password cannot be your current password",false);
#Override
public Result validate(String newPassword) {
boolean passed = !encoder.matches(newPassword,user.getPassword());
return (passed ? Result.OK : FAILED);
}
#Override
public String toString() {
return this.getClass().getSimpleName();
}
}
Now here is a composite:
public class CompositePasswordRule extends QuickCollection<Validator> implements Validator<String> {
public CompositeValidator(Collection<Validator> rules) {
super.delegate = rules;
}
public CompositeValidator(Validator<?>... rules) {
super.delegate = Arrays.asList(rules);
}
#Override
public CompositeResult validate(String newPassword) {
CompositeResult result = new CompositeResult(super.delegate.size());
for(Validator rule : super.delegate){
Result temp = rule.validate(newPassword);
if(!temp.isOk())
result.put(rule,temp);
}
return result;
}
public static class CompositeResult extends QuickMap<Validator,Result> implements Result {
private Integer appliedCount;
private CompositeResult(Integer appliedCount) {
super.delegate = VdcCollections.delimitedMap(new HashMap<PasswordRule, Result>(), "-->",", ");
this.appliedCount = appliedCount;
}
#Override
public String getMessage() {
return super.delegate.toString();
}
#Override
public String toString() {
return super.delegate.toString();
}
#Override
public boolean isOk() {
boolean isOk = true;
for (Result r : delegate.values()) {
isOk = r.isOk();
if(!isOk)
break;
}
return isOk;
}
public Integer failCount() {
return this.size();
}
public Integer passCount() {
return appliedCount - this.size();
}
}
}
and now a snippet of use:
private Validator<String> pwRule = new CompositeValidator<String>(new MinLengthValidator(),new NotCurrentValidator());
Validator.Result result = pwRule.validate(newPassword);
if(!result.isOk())
throw new PasswordConstraintException("%s", result.getMessage());
user.obsoleteCurrentPassword();
user.setPassword(passwordEncoder.encode(newPassword));
user.setPwExpDate(DateTime.now().plusDays(passwordDaysToLive).toDate());
userDao.updateUser(user);
Chain of responsibility implies that there is an order in which the validations must take place. I would probably use something similar to the Strategy pattern where you have a Set of validation strategies that are applied to a specific type of record. You could then use a factory to examine the record and apply the correct set of validations.