Hibernate -validator group sequence provider getDefaultSequenceProvider gets null as input - spring

I am using the hibernate validator group sequence and want to execute the groups in a sequence based on business rules. But the input to the groupSequenceProvider for its getValidationGroups is always null, and hence custom sequence never gets added.
My request object:
#GroupSequenceProvider(BeanSequenceProvider.class)
public class MyBean {
#NotEmpty
private String name;
#NotNull
private MyType type;
#NotEmpty(groups = Special.class)
private String lastName;
// Getters and setters
}
Enum type:
public enum MyType {
FIRST, SECOND
}
My custom sequence provider:
public class BeanSequenceProvider implements DefaultGroupSequenceProvider<MyBean> {
#Override
public List<Class<?>> getValidationGroups(MyBean object) {
final List<Class<?>> classes = new ArrayList<>();
classes.add(MyBean.class);
if (object != null && object.getType() == MyType.SECOND) {
classes.add(Special.class);
}
return classes;
}
}
Group annotation:
public interface Special {
}
When I execute the above code, I get the input MyBean object as null and cannot add the custom sequence. What am I missing? I am using hibernate-validator version as 5.4.1.Final

Related

How to generate cache CustomKey for Redis in Spring Boot

I have spring boot application which is integrated with Redis cache. Have to implement caching for one of the method call. That method argument is an object with multiple params which is external Request object. This object params will vary for each request also based on that param and its values output of the method is varies. I need to create a cache key using that Request object field/param values. How to achieve it.
We can use SimpleKeyGenerator only when method params are static?
UserService.java
#Cacheable(value = "usercache", keyGenerator="customKeyGenerator")
public UserResponse getUserResp(User user){
//Some backend calls
return user
}
User.java
public class User {
private String firstname;
private String lastname;
private Integer age;
private Date dob;
private Address address;
// Another 10 params
}
In this method implementation User object is dynamic. I have to create a cache key based on User object fields which is having valid non null values. How to achieve it.
I have implemented as like below.
User.java
public class User implements Serializable {
private String firstname;
private String lastname;
private Integer age;
private Date dob;
private Address address;
// Another 10 params
#Override
public int hashCode() {
final int prime = 31;
//Add necessary fields
}
#Override
public boolean equals(Object obj) {
if (this == obj)
return true;
//Add necessary fields
}
}
public class UserKeyGenerator implements KeyGenerator{
private static final String UNDERSCORE_DELIMITER = "_";
#Override
public Object generate(Object target, Method method, Object... params) {
String cacheKey = null;
if(params.length > 0) {
StringJoiner paramStrJoiner = new StringJoiner(UNDERSCORE_DELIMITER);
User userReq = (User) params[0];
paramStrJoiner.add(target.getClass().getSimpleName());
paramStrJoiner.add(method.getName());
paramStrJoiner.add(String.valueOf(userReq.hashCode()));
cacheKey = paramStrJoiner.toString();
}
return cacheKey;
}

#Embeddable Composite Key is not throwing integrity exception in with spring data JPA

I have used Spring Data JPA and #Embedabble to create the composite key.
And one Base class BaseDate will be extended by all the Entity.
sysCreationDate will be generated during insertion (not null and non-updatable)
save user is working fine for the first time but there are 3 issues here-
During the second call instead of throwing an exception it is updating the sysUpdateDate and userType
During the first call sysUpdateDate is not null (#UpdateTimestamp)
During the second call in response it returns the sysCreationDate as null
Below is the code-
Embeddable class
#Embeddable
public class CompKey implements Serializable {
#Column(name ="USER_ID")
private String userId;
#Column(name ="USER_NAME")
private String userName;
public CompKey(String userId, String userName) {
super();
this.userId = userId;
this.userName = userName;
}
public CompKey() {
super();
}
//Getters /Setters /Equual and Hashcode
}
Base Class for Date
#MappedSuperclass
public abstract class BaseDate {
#CreationTimestamp
#Column(name = "SYS_CREATION_DATE", updatable=false, nullable=false)
private Calendar sysCreationDate;
#Column(name = "SYS_UPDATE_DATE")
#UpdateTimestamp
private Calendar sysUpdateDate;
public BaseDate(Calendar sysCreationDate, Calendar sysUpdateDate) {
this.sysCreationDate = sysCreationDate;
this.sysUpdateDate = sysUpdateDate;
}
public BaseDate() {
}
//Getters and Setters
}
Entity Class
#Entity
public class User extends BaseDate{
#Column(name = "USER_TYPE")
private String userType;
#EmbeddedId
private CompKey compkey;
public User() {
super();
}
public User(Calendar sysCreationDate, Calendar sysUpdateDate, String userType, CompKey compkey) {
super(sysCreationDate, sysUpdateDate);
this.userType = userType;
this.compkey = compkey;
}
//Getters and setters
}
Repo -
#Repository
public interface UserRepo extends CrudRepository<User, CompKey> {
}
Service and Controller -
#Service
public class UserService {
#Autowired
UserRepo userRepo;
public User saveUser(User user) {
return userRepo.save(user);
}
public Optional<User> getUser(CompKey key) {
return userRepo.findById(key);
}
}
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
UserService userService;
#PostMapping("/save")
public User saveUser(#RequestBody User user) {
return userService.saveUser(user);
}
#GetMapping("/get")
public Optional<User> getUser(#RequestBody CompKey key) {
return userService.getUser(key);
}
Input -
{
"userType": "K",
"compkey": {
"userId": "1002",
"userName": "ASDF"
}
}
Output 1)-
{
"sysCreationDate": "2021-01-08T18:09:28.802+00:00",
"sysUpdateDate": "2021-01-08T18:09:28.802+00:00",
"userType": "K",
"compkey": {
"userId": "1002",
"userName": "ASDF"
}
{
"sysCreationDate": null,
"sysUpdateDate": "2021-01-08T18:10:43.206+00:00",
"userType": "K",
"compkey": {
"userId": "1002",
"userName": "ASDF"
}
}
Thanks in advance
The integrity constraint violation exception is not thrown because your Spring repository just updates the object.
Spring repositories do not differentiate between insert and update. There is only one general-purpose method -- save. By default, this method persists (inserts) a new object only when a primary key is null or 0; otherwise, it merges (updates) into an existing object. You always have a primary key set, so it always calls merge, which updates the second time.
Its basic implementation in SimpleJpaRepository looks like:
#Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null.");
if (this.entityInformation.isNew(entity)) {
this.em.persist(entity);
return entity;
} else {
return this.em.merge(entity);
}
}
The key part is isNew method with its default implementation like:
public boolean isNew(T entity) {
ID id = getId(entity);
Class<ID> idType = getIdType();
if (!idType.isPrimitive()) {
return id == null;
}
if (id instanceof Number) {
return ((Number) id).longValue() == 0L;
}
throw new IllegalArgumentException(String.format("Unsupported primitive id type %s!", idType));
}
The available solutions are:
call EntityManager directly.
implement Persistable interface from Spring and implement your own isNew to inform a Spring repository whether your object is new or was already persisted.
use a surrogate primary key (long, #GeneratedValue) and a unique constraint on your logical key
I would recommend the third solution (with a surrogate primary key) as it's simple and has better extensibility. For example, it will be easier to add a foreign key referencing your entity.
There also is a solution with calling find first, just to check if the object exists in a database. However, this solution is prone to a race issue (two concurrent REST requests to create a new object, both call find, both receive null, thus both save, and one data is lost/overwritten).
For #UpdateTimestamp, you've already got a comment, and for #CreationTimestamp null, please, post your controller.

Make #ConfigurationProperties properties partially mandatory

Given the following simple (not nested) configuration properties class:
#ConfigurationProperties("env")
public class MyServiceProperties {
private String anyProperty;
private Boolean anyOther;
...
}
How can I make sure that anyProperty is mandatory, i.e. env.any-property must be set to startup the application? Is there any difference for nested configuration property classes?
You can perform all kind of validations.
#Validated
#ConfigurationProperties("env")
public class MyServiceProperties {
#NotNull
#Min(5)
private String anyProperty;
// this is for nested objects
#Valid
#NotNull
private FooNested fooNested;
public static class FooNested{
#NotNull
private String someVal;
}
}
You could also perform manual validation in setter
#Validated
#ConfigurationProperties("env")
public class MyServiceProperties {
private String anyProperty;
public void setAnyProperty(String anyProp){
// just an example
if(anyProp.lenght < 6){
throw new RuntimeException();
}
this.anyProperty = anyProp;
}
}

How to use Java 8 Optional with Moxy and Jersey

Is it possible to use Jersey with Moxy to/from Json and Java 8 Optionals?
How to configure it?
You can declare following class:
public class OptionalAdapter<T> extends XmlAdapter<T, Optional<T>> {
#Override
public Optional<T> unmarshal(T value) throws Exception {
return Optional.ofNullable(value);
}
#Override
public T marshal(Optional<T> value) throws Exception {
return value.orElse(null);
}
}
And use like this:
#XmlRootElement
public class SampleRequest {
#XmlElement(type = Integer.class)
#XmlJavaTypeAdapter(value = OptionalAdapter.class)
private Optional<Integer> id;
#XmlElement(type = String.class)
#XmlJavaTypeAdapter(value = OptionalAdapter.class)
private Optional<String> text;
/* ... */
}
Or declare in package-info.java and remove #XmlJavaTypeAdapter from POJOs:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlJavaTypeAdapters({
#XmlJavaTypeAdapter(type = Optional.class, value = OptionalAdapter.class)
})
But here are some drawbacks:
Adapter above can only work with simple types like Integer, String, etc. that can be parsed by MOXY by default.
You have to specify #XmlElement(type = Integer.class) explicitly to tell the parser type are working with, otherwise null values would be passed to adapter's unmarshal method.
You miss the opportunity of using adapters for custom types, e.g. custom adapter for java.util.Date class based on some date format string. To overcome this you'll need to create adapter something like class OptionalDateAdapter<String> extends XmlAdapter<String, Optional<Date>>.
Also using Optional on field is not recommended, see this discussion for details.
Taking into account all the above, I would suggest just using Optional as return type for your POJOs:
#XmlRootElement
public class SampleRequest {
#XmlElement
private Integer id;
public Optional<Integer> getId() {
return Optional.ofNullable(id);
}
public void setId(Integer id) {
this.id = id;
}
}

No composite key property found for type error in Spring JPA2

I have an error in spring JPA
org.springframework.data.mapping.PropertyReferenceException: No property CompanyId found for type CompanyUserDetail!
#Embeddable
public class CompanyUserKey implements Serializable {
public CompanyUserKey() {
}
#Column(name = "company_id")
private UUID companyId;
#Column(name = "user_name")
private String userName;
public UUID getCompanyId() {
return companyId;
}
public void setCompanyId(UUID companyId) {
this.companyId = companyId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
}
#Entity
#Table(name = "company_user_detail")
public class CompanyUserDetail {
#EmbeddedId
CompanyUserKey companyUserkey;
public CompanyUserKey getCompanyUserkey() {
return companyUserkey;
}
public void setCompanyUserkey(CompanyUserKey companyUserkey) {
this.companyUserkey = companyUserkey;
}
}
I am trying to access below method Service layer
#Component
public interface CompanyUserRepository extends JpaRepository<CompanyUserDetail, CompanyUserKey> {
public List<CompanyUserDetail> findByCompanyId(UUID companyId);
}
How can I achieve this ?
Thanks
Since in java model your CompanyUserKey is a property in the CompanyUserDetail class, I believe you should use full path (companyUserkey.companyId) to reach companyId:
public List<CompanyUserDetail> findByCompanyUserkeyCompanyId(UUID companyId);
Also note that you have a naming inconsistency: field in CompanyUserDetail is named companyUserkey instead of companyUserKey.
Assuming you are not using spring-data-jpa's auto generated implementations, your method contents might look something like the following:
FROM CompanyUserDetail c WHERE c.companyUserKey.companyId = :companyId
Now simply provide that query to the EntityManager
entityManager.createQuery( queryString, CompanyUserDetail.class )
.setParameter( "companyId", companyId )
.getResultList();
The key points are:
Query uses a named bind parameter called :companyId (not the leading :).
Parameter values are bound in a secondary step using setParameter method variants.
createQuery uses a second argument to influence type safety so that the return value from getResultList is a List<CompanyUserDetail> just like you requested.
Looking at spring-data-jpa's implementation however, I suspect it could look like this:
public interface CustomerUserRepository
extends JpaRepository<CompanyUserDetail, CompanyUserKey> {
#Query("select c FROM CompanyUserDetail c WHERE c.companyUserKey.companyId = :companyId")
List<CompanyUserDetail> findByCompanyId(#Param("companyId") UUID companyId);
}

Resources