Hibernate-validator 6: Weird behavior of the #Max annotation with a BigDecimal set on a Number field - hibernate-validator

I think I might have found an oddity in hibernate-validator 6.0.15.Final. It used to work with the version 5.4.2.Final.
Here is a test example:
import lombok.Data;
import org.junit.Test;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.constraints.Max;
import java.math.BigDecimal;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
public class ValidTest {
#Data
static class ClassToValidate{
public ClassToValidate() {
failingNumber = new BigDecimal("1.001");
failingBigDecimal = new BigDecimal("1.001");
passingNumber = new BigDecimal("0.001");
passingBigDecimal = new BigDecimal("0.001");
}
#Max(1)
private Number failingNumber;
#Max(1)
private BigDecimal failingBigDecimal;
#Max(1)
private Number passingNumber;
#Max(1)
private BigDecimal passingBigDecimal;
}
#Test
public void test(){
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
Set<ConstraintViolation<ClassToValidate>> violations = validator
.validate(new ClassToValidate());
for (ConstraintViolation<ClassToValidate> violation : violations) {
System.out.println(violation);
}
assertThat(violations).hasSize(2);
}
}
The BigDecimal stored in a Number field will not trigger a constraint exception even though it is bigger than 1. And a bigdecimal such as 2.xxx would.
It feels like the validator does not (anymore) take into account the numbers after the comma in BigDecimals objects stored in a Number.

Hum, you're right, we have a bug here: when you use a Number as the declared type as you did, we end up comparing longs. It should be an easy fix though.
I saw you filled https://hibernate.atlassian.net/browse/HV-1699 , we will give you updates there.
I will make a release as soon as we have a fix as it's definitely a bad one.

With Java Bean Validation , you can use three alternative for BigDecimal type:
#DecimalMax --->
Sample:
#DecimalMax("30.00")
BigDecimal discount;
#DecimalMin --->
Sample:
#DecimalMin("5.00")
BigDecimal discount;
#Digits --->
Sample:
#Digits(integer=6, fraction=2)
BigDecimal price;
Note:
You can use #Max only for an integer value.

Related

Jpa Criteria query

I have a table
Customer(Name ,Gender,State,age,city)
I want to filter my customer table by a combination of
(gender,state,city,age)
User can either enter any one,two,three or four filter criteria. I am stuck at writing the criteria query for this How can I do this??
package com.thoughtclan.segmentationofcustomers.specification;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.jpa.domain.Specification;
import com.thoughtclan.segmentationofcustomers.model.Customer;
public class FilterCriteria implements Specification<Customer> {
private SearchCriteria criteria;
#Override
public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// TODO Auto-generated method stub
return null;
}
}
Can you Explain in this context??
**
This Is my service class method
**
public Set<Customer> filterDetails(TargetGroupDto targetGroupDto) {
// TODO Auto-generated method stub
}
You should store your filter options in some class. Than just check if they exist and add criteria.
Criteria criteria = session.createCriteria(Customer.class);
// here you receive some DTO with filter fields
if (filter.getState != null) {
criteria.add(Restrictions.eq("state", filter.getState));
}
if (filter.getCity != null) {
criteria.add(Restrictions.eq("city", filter.getCity));
}
criteria.list() // return result
session.close() // or you can use try-with-resources
Edit: As your question is updated later, I already asked you in comment for a JPQL query. So this answer is related with JPQL, not criteria query.
You can do it with JPQL query. I assume that you generate (you can generate if from your IDE ) or create a database entity class named Customer which has the member
private String name;
private String gender;
private String state;
private Integer age;
private String city;
.... setters and getters
Now add the class reference in persistent.xml if not exist.
<class>Customer</class> //if additional package exist then give the package name e.g com.test.Customer
Now come in the query part.
Query q = entityManager.createQuery("SELECT c from Customer c where c.gender=:gender and c.state=:state and c.city=:city and c.age=:age");
q.setParameter("gender", "MALE");//Assume gender value is Male
q.setParameter("state", "Honululu");//Assume state value is Honululu
q.setParameter("city", "abc");//Assume city value is abc
q.setParameter("age", 10);//Assume age value is 10
Object resp = q.getResultList();
Now resp object contains your data. Cast the object to java.util.List to get data.
You can add some logic to combine your query. Suppose you have a business object CustomerBO instance cbo.
if(cbo.getGender()!=null && !cbo.getGender().isEmpty()){
q.setParameter("gender", cbo.getGender());
}
and so many condition as your wish .....

Using validators with a class that is not persisted in the database

as part of a website i'm building using Spring Boot, I receive input via Thymeleaf form. To validate the input i have created a class and annotated its fields:
package com.bank.domain;
import java.util.Date;
import javax.persistence.Column;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
public class NewClass {
//if record is active/inactive (hidden)
#Column
private boolean active=true;
//bank of account holder
#NotNull
#Size(min = 2, max =3)
private String banknum;
//branch
#NotNull
#Size(min = 1, max =3)
private String branchnum;
// account number
#NotNull
#Size(min = 4, max =10)
private String accountnum;
// number / range of numbers of Check
#NotNull
#Digits(integer=9, fraction=0)
#Column(columnDefinition = "UNSIGNED INT(9) not null",nullable=false)
private String fromchecknum;
// number / range of numbers of Check
#NotNull
#Digits(integer=9, fraction=0)
#Column(columnDefinition = "UNSIGNED INT(9) not null",nullable=false)
private String tochecknum;
}
because I have debug turned on, i see that sql output in dumped to the log, even though there is no table for this class in the database.
I want to not waste resources on the SQL queries or whatever they are, since there is no underlying table, just a class with fields that have validators.
So my question is -
is it possible to just use a regular class and also employing the various annotation such as #Size, #Min, #NotNull, #Digits, etc?
Looking at your class it seems that the use of #Column is the only thing dealing with persistence. Do you want to have that annotation? If you look at the package structure, #column is is persistence package and the validation related annotations reside in javax.validation and not in the persistence package.
Bean validations (JSR303) like #NotNull, etc can be used even when your bean is not #Entity. I think that was your question, right?

Prtining method arguments using byte buddy API

I am working on a project where I need access method arguments during execution.
Is it possible to print method arguments using byte buddy framework? any sample code on this using javaagent is highly appreciated.
Yes, this is possible. You can use MethodDelegation or Advice to inject your code and then use the #AllArguments annotation to get hold of the actual arguments.
The question is, how do you create your code in your project? You can either use a Java agent with the AgentBuilder or create proxy subclasses using ByteBuddy instances. Refer to the documentation and the mentioned classes javadoc to find out how this is done.
Here is an example of how this can be implemented using MethodDelegation. I use it to measure the execution time of methods. I specifically did not begin to remove the extra code, because I want to more fully reveal the capabilities of Byte Buddy.
package md.leonis.shingler;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.stream.Collectors;
public class MeasureMethodTest {
public static void main(String[] args) throws InterruptedException {
premain(ByteBuddyAgent.install());
for (int i = 0; i < 4; i++) {
SampleClass.foo("arg" + i);
}
}
public static void premain(Instrumentation instrumentation) {
new AgentBuilder.Default()
.type(ElementMatchers.nameStartsWith("md.leonis.shingler"))
.transform((builder, type, classLoader, module) ->
builder.method(ElementMatchers.any()).intercept(MethodDelegation.to(AccessInterceptor.class))
).installOn(instrumentation);
}
public static class AccessInterceptor {
#RuntimeType
public static Object intercept(#Origin Method method, #SuperCall Callable<?> callable, #AllArguments Object[] args) throws Exception {
long start = System.nanoTime();
try {
return callable.call();
} finally {
if (method.getAnnotationsByType(Measured.class).length > 0) {
String params = Arrays.stream(args).map(Object::toString).collect(Collectors.joining(", "));
System.out.println(method.getReturnType().getSimpleName() + " " + method.getName() + "("+ params +") took " + ((System.nanoTime() - start) / 1000000) + " ms");
}
}
}
}
public static class SampleClass {
#Measured
static void foo(String s) throws InterruptedException {
Thread.sleep(50);
}
}
}
This example measures the execution time of all methods found in the md.leonis.shingler package and marked with the #Measured annotation.
To run it, you need two libraries: byte-buddy and byte-buddy-agent.
The result of work:
void foo(arg0) took 95 ms
void foo(arg1) took 50 ms
void foo(arg2) took 50 ms
void foo(arg3) took 50 ms
Note that the console displays the values of all arguments passed to the method. This is the answer to the question asked.
Here is the annotation example:
package md.leonis.shingler;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Measured {
}
To be honest, I was not able to directly configure filtering by annotations in the Agent. Here is an example (not working):
new AgentBuilder.Default()
.type(ElementMatchers.isAnnotatedWith(Measured.class))
.transform((builder, type, classLoader, module) ->
builder.method(ElementMatchers.any()).intercept(MethodDelegation.to(AccessInterceptor.class))
).installOn(instrumentation);
If someone knows how to do this, please comment below.

Minecraft modding block constructer error

I'm making a mod, and I am getting an error(no duh) and I have tried searching it up but I want an answer specific to my problem because I am not very good at this. I am getting this error in my block class.
Implicit super constructor Block() is undefined for default constructor. Must define an explicit constructor
and I don't know how to fix it. Please Help its for a project.
block class:
package GDMCrocknrollkid.fandomcraft;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
public class BlockCbBlock extends Block {
protected BlockCbBlock(Material material) {
super(material);
}
}
mod class:
package GDMCrocknrollkid.fandomcraft;
import net.minecraft.block.Block;
import net.minecraft.item.Item;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
#Mod(modid = "fc", name = "Fandomcraft", version = "1.0")
public class fandomcraft {
public static Item itemCbIngot;
public static Block blockCbBlock;
#EventHandler
public void preInit(FMLPreInitializationEvent event){
//Item/Block initialization and registering
//Config Handling
itemCbIngot = new ItemCbIngot().setUnlocalizedName("ItemCbIngot").setTextureName("fc:itemCbIngot"); //item.itemCbIngot.name
blockCbBlock = new BlockCbBlock(Material.iron);
GameRegistry.registerItem(itemCbIngot, itemCbIngot.getUnlocalizedName().substring(5));
}
#EventHandler
public void init(FMLInitializationEvent event){
//Proxy, TileEntity, entity, GUI and Packet Registering
}
#EventHandler
public void postInit(FMLPostInitializationEvent event) {
}
}
This error pertains to all of java, not just minecraft forge. Check this for some more reference. There are a couple possible reasons for this error. It is most likely 1, but 2 and 3 can be a contributing factor to the error.
Your BlockCbBlock Class declares a constructor that is not the default, no-argument constructor that the compiler would otherwise provide (that is, if the Block class doesn't have a constructor) and, if in fact the Block class is using the default constructor, then you can't call super() on the arguements because the Block class uses a constructor with no arguments. Because of this, if you wanted to modify the Block constructor, it would be safier and easier to create a custom construcotr inside of the BlockCbBlock class itself.
You are trying to inherit the constructor of Block, but you have declared it as protected, when the constructor in your class should be public to match the inherited .
If you're using Eclipse, it can give this error when you have your project setup incorrectly (system configuration mismatch)
Probably not directly realted to this specific error, but a possible cause of other errors in the near future; you are using the annotation #EventHandler, but you have not actually declared the forge event handler.
You don't actually register the block for some reason. Even if you're using the block as a recipe item, you still need to register it
To fix potential problems 1, 2, and 4, try this (obtained from here):
package GDMCrocknrollkid.fandomcraft;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
private final String name = "BlockCbBlock";
public class BlockCbBlock extends Block {
public BlockCbBlock() {
super(Material.iron);
GameRegistry.registerBlock(this, name);
setUnlocalizedName(Reference.MODID + "_" + name);
setCreativeTab(CreativeTabs.tabBlock);
}
public String getName() {
return name;
}
}
This way, you'll declare its UnlocalizedName, Material, and CreativeTab ahead of time. This method might be unnecessary, but its a good precaution to help prevent the error. Now, all you have to do is declare it like this:
//You need to make your own EventHandler class. Search online for that.
FCEventHandler handler = new FCEventHandler();
#EventHandler
public void preInit(FMLPreInitializationEvent event){
//Config Handling
//event handler registry
FMLCommonHandler.instance().bus().register(handler);
MinecraftForge.EVENT_BUS.register(handler);
//the same thing can be similarly done with this if you wish
itemCbIngot = new ItemCbIngot().setUnlocalizedName("ItemCbIngot").setTextureName("fc:itemCbIngot");
blockCbBlock = new BlockCbBlock();
GameRegistry.registerItem(itemCbIngot, itemCbIngot.getUnlocalizedName().substring(5));
}

querying for a string

i have a model which has basically just one string variable...i want to write a function to query all the elements in the datastore...how do i do it? also, once i get the result, i want to display the fetched strings in a listview..how do i go abt this???
My class definition is as follows :
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
#PersistenceCapable(identityType = IdentityType.APPLICATION)
public class TrialDB {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long key;
#Persistent
private String message;
//Accessor Methods etc follow
}
Perhaps you just read Googles docs
http://code.google.com/appengine/docs/java/datastore/jdo/queries.html

Resources