How to validate a property that should be grater than zero?and not zero.I used built in annotation #Min(0) but how can i ignore the zero?Is there any other built in annotation for this case?
#Min(0)
default public Double getAmd1() {
return (Double) get("amd1");
}
Check out the #DecimalMin annotation
It provides a boolean parameter inclusive which fits your needs.
Sadly Double is not supported because of rounding issues, but BigDecimal is. You can then get the value as double by using getDouble().
So you can try something like this:
#DecimalMin(value = "0.0" ,inclusive = false)
private BigDecimal amd1;
public BigDecimal getAmd1() {
Double d = (Double) get("amd1");
return BigDecimal.valueOf(d);
}
Related
I am trying to learn Spring Boot and working on a simple REST API which I develop a simple calculator.
I have a service class annotated with #Service and it has a simple method
public double calculate(String operation, double num1, double num2) {
...
}
And I am calling this method from the controller like
#GetMapping("/calculate")
public double calculate(#RequestParam(value = "op") String op,
#RequestParam(value = "num1") double num1,
#RequestParam(value = "num2") double num2) {
return calculatorService.calculate(op, num1, num2);
}
I want to validate these parameters(Whether num1 and num2 are numeric and all parameters should be not null). What is the best practice to validate the request parameters for Spring Boot?
It depends on what validation you want to perform.
One way would be to use validation annotations on the parameters themselves.
For example, #NotNull, #Min, #Max, etc. These particular ones can be found in javax.validation:validation-api, but there are many others (and you can build your own).
#GetMapping("/calculate")
public double calculate(#RequestParam(value = "op") #NotNull String op,
#RequestParam(value = "num1") double num1,
#RequestParam(value = "num2") double num2) {
The #NotNull validation will ensure that op is not null - you might want to use a #NotEmpty or #NotBlank annotation to ensure that the actual value of op has a length > 0 or a trimmed length > 0 respectively.
As Cristian Colorado points out in their comment below, Spring will automatically fail validation (or endpoint matching) if either num1 or num2 are not valid double values.
As you have told that you want to validate only Number #NumberFormat Annotation is there. The #NumberFormat annotation applies to subclasses of java.lang.Number (Integer, Float, Double, BigDecimal, etc.)
#NumberFormat(style=Style.NUMBER)
I have a problem in retriving data with two decimal places from the database.
I was using resultset.getDouble() method. But this gives me only 1 decimal places on the result. I tried using the DecimalFormat to convert to 2 decimal. When I run my code, I'm getting the error "String cannot convert to double" from DisplayFood.java
DisplayFood.java
Connection connection = null;
Statement statement = null;
ResultSet resultset = null;
DecimalFormat df = new DecimalFormat("#.00");
String query = "SELECT * FROM food";
statement = connection.createStatement();
resultset = statement.executeQuery(query);
while(resultset.next())
{
FoodRecord foodmenu = new FoodRecord();
foodmenu.setItemName(resultset.getString("itemName"));
foodmenu.setPrice(df.format(resultset.getDouble("price")));
foodmenu.setRestaurant(resultset.getString("restaurant"));
food.add(foodmenu);
}
FoodRecord.java
public class FoodRecord {
private String itemName;
private double price;
private String restaurant;
public FoodRecord() {
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getRestaurant() {
return restaurant;
}
public void setRestaurant(String restaurant) {
this.restaurant = restaurant;
}
}
You get this Error bacause You are trying to set a String value in a double Field.
Use resultset.getDouble("price") to read the items Price from resultset.
To print the Price with two decimal Places you can use your formatter with System.out.println like df.format(foodMenu.getPrice()).
I was using resultset.getDouble() method. But this gives me only 1
decimal places on the result
The getDouble() method returns the value number as a double is represented in java.
The getDouble() javadoc :
Retrieves the value of the designated column in the current row of
this ResultSet object as a double in the Java programming language.
It means if your number value is 10.5, the double will have as value 10.5 and not 10.50.
I tried using the DecimalFormat to convert to 2 decimal
DecimalFormat.format(double number) formats a double as a String with a specific format.
The format specified is here in your code :
DecimalFormat df = new DecimalFormat("#.00");
So, yes you cannot assign a Double to a String as these are not compatible types.
To solve your problem, you have to store the result of DecimalFormat.format(double number) in a String.
But it is indeed not not consistent with the FoodRecord class where price is a double field : private double price;
Changing price from double to String in the class is not necessarily the best way for two reasons :
the visual representation of a data should not change the data value.
If the decimal is on 4 floating value (for example : 10.5445), formatting it to 10.54 and storing the information in the class only as a String field truncates the data value. If you store again the value in your database, it changes the initial value while the object was not modified by the client.
you could not any longer have a simple setPrice() method with a double (so in a natural way) as you should perform some computations in the setPrice() method to change the double to a String.
To solve your problem, you could introduce a new method that returns the formatted price :
public String getFormatedPrice(){
return df.format(resultset.getDouble("price"));
}
If getFormatedPrice() is frequently invoked, the result could be cached instead of invoking df.format().
I am using BigDecimal to get some price values. Requirement is something like this, what ever the value we fetch from database, the displayed valued should have 2 decimal points.
Eg:
fetched value is 1 - should be displayed as 1.00
fetched value is 1.7823 - should be displayed as 1.78
I am using setScale(2, BigDecimal.ROUND_HALF_UP) but still some places, if the data from DB is a whole number then the same is being displayed !!
I mean if the value is 0 from DB its displayed as 0 only. I want that to be displayed as 0.00
Thanks
BigDecimal is immutable, any operation on it including setScale(2, BigDecimal.ROUND_HALF_UP) produces a new BigDecimal. Correct code should be
BigDecimal bd = new BigDecimal(1);
bd.setScale(2, BigDecimal.ROUND_HALF_UP); // this does change bd
bd = bd.setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println(bd);
output
1.00
Note - Since Java 9 BigDecimal.ROUND_HALF_UP has been deprecated and you should now use RoundingMode.ROUND_HALF_UP.
you can use the round up format
BigDecimal bd = new BigDecimal(2.22222);
System.out.println(bd.setScale(2,BigDecimal.ROUND_UP));
Hope this help you.
To format numbers in JAVA you can use:
System.out.printf("%1$.2f", d);
where d is your variable or number
or
DecimalFormat f = new DecimalFormat("##.00"); // this will helps you to always keeps in two decimal places
System.out.println(f.format(d));
You need to use something like NumberFormat with appropriate locale to format
NumberFormat.getCurrencyInstance().format(bigDecimal);
BigDecimal.setScale would work.
The below code may help.
protected String getLocalizedBigDecimalValue(BigDecimal input, Locale locale) {
final NumberFormat numberFormat = NumberFormat.getNumberInstance(locale);
numberFormat.setGroupingUsed(true);
numberFormat.setMaximumFractionDigits(2);
numberFormat.setMinimumFractionDigits(2);
return numberFormat.format(input);
}
You can use a custom annotation in this manner:
The custom annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface FormatBigDecimal {
String format() default "0.00";
}
And then you can apply the annotation on a field and in the constructor call the implementation method as shown below:
public class TestClass {
#FormatBigDecimal
private BigDecimal myDecimal;
public TestClass(BigDecimal myDecimal) throws ParseException, IllegalAccessException {
this.myDecimal = myDecimal;
formatBigDecimalFields();
}
public void setMyDecimal(BigDecimal myDecimal) {
this.myDecimal = myDecimal;
}
public BigDecimal getMyDecimal() {
return myDecimal;
}
/**
In the above method, we are using reflection to get all the fields declared in the class. Then, we are iterating over all the fields and checking whether the #FormatBigDecimal annotation is present on the field. If it is present, we are making the field accessible and getting its value.
We are also getting the format string from the #FormatBigDecimal annotation and using it to create a DecimalFormat object with the desired format. Then, we are formatting the value of the field using the DecimalFormat object and storing the formatted value in a string.
Finally, we are parsing the formatted value back into a BigDecimal object and setting it as the value of the field.
**/
public void formatBigDecimalFields() throws IllegalAccessException, ParseException {
Field[] fields = this.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(FormatBigDecimal.class)) {
field.setAccessible(true);
BigDecimal value = (BigDecimal) field.get(this);
FormatBigDecimal formatAnnotation = field.getAnnotation(FormatBigDecimal.class);
String formatString = formatAnnotation.format();
NumberFormat format = NumberFormat.getNumberInstance(Locale.US);
DecimalFormat decimalFormat = (DecimalFormat) format;
decimalFormat.applyPattern(formatString);
String formattedValue = decimalFormat.format(value);
BigDecimal formattedDecimal = new BigDecimal(formattedValue);
field.set(this, formattedDecimal);
}
}
}
}
To Use it:
TestClass testClass = new TestClass(new BigDecimal("10000.899"));
System.out.println(testClass.getMyDecimal());
will give: 10000.90
In Java, new BigDecimal("1.0") != new BigDecimal("1.00") i.e., scale matters.
This is apparently not true for Hibernate/SQL Server, however. If I set the scale on a BigDecimal to a particular value, save the BigDecimal to the database via Hibernate and then re-inflate my object, I get back a BigDecimal with a different scale.
For instance, a value of 1.00 is coming back as 1.000000, I assume because we're mapping BigDecimals to a column defined as NUMERIC(19,6). I can't just define the column as the required scale as I need to store both Dollar and Yen values (for example) in the same column. We need to represent the BigDecimals as numeric types in the database for the benefit of external reporting tools.
Does there exist a Hibernate UserType which maps BigDecimal "properly", or do I have to write my own?
Just for informational sake, I can tell you that the creation of the BigDecimal coming back from the database is done by the proprietary JDBC driver's implementation of the 'getBigDecimal' method of the database-specific 'ResultSet' sub-class.
I found this out by stepping thru the Hibernate source code with a debugger, while trying to find the answer to my own question.
I think this will work, I didn't test it though.
public class BigDecimalPOJO implements Serializable {
private static final long serialVersionUID = 8172432157992700183L;
private final int SCALE = 20;
private final RoundingMode ROUNDING_MODE = RoundingMode.CEILING;
private BigDecimal number;
public BigDecimalPOJO() {
}
public BigDecimal getNumber() {
return number.setScale(SCALE, ROUNDING_MODE);
}
public void setNumber(BigDecimal number) {
this.number = number.setScale(SCALE, ROUNDING_MODE);
}
}
Not sure, but you can check equality using a.compareTo(b) == 0.
does anyone know how I can create a Java POJO which has stores a double in memory but an integer on disk?
I would like to store these objects to disk as integers, because I have 100 million of them, and that would save space. However, sometimes my Java code manipulates the values in memory (without saving them) and turns them into real numbers
I have tried something like:
#Entity
#Table(name = "POJO")
public class POJO {
#Id
#GeneratedValue
#Column(name = "id")
int id;
#Column(name = "value")
int valueInteger; // this is weird: in the database, we want to use integers, to save space. but in memory, sometimes we use real values
double value;
public int getValueInteger() {
return valueInteger;
}
public void setValueInteger(int valueInteger) {
this.valueInteger = valueInteger;
this.value = valueInteger;
}
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
// might want to call setValueInteger() here if consistency is required. Right now, I think it's not, so I'll leave it out.
}
}
This is not a terrible solution, except the memory footprint is larger than needed. Is there some way to have the POJO have only a double (and the ID of course), and instruct hibernate to force a double to integer conversion when it stores to the database (for instance, by rounding that double)?
Thanks for the help!
Hope this works:
#Column(name = "value")
private int intValue;
#Transient
private Double value = null;
public double getValue() {
if (value == null)
value = intValue;
return value;
}
public void setValue(double value) {
this.value = value;
this.intValue = value;
}
RDBMS tend to define columns in terms of precision and scale, rather than IEEE types and bit counts. If you just define a database column with fixed scale of 0 you will get the result you want when the DB automatically chops the number down.
You could define a user type object that holds it and converts, but if you're genuinely concerned about the memory footprint of a 32bit number, replacing that with a reference to a custom user type object isn't really a step forward. (Although with the behemoth that is hibernate lurking under your application code, worrying about primitive int values doesn't seem like a good place to focus your optimization efforts.)
OK I figured it out: Hibernate does all of the rounding and casting automatically. So no need for anything clever. Remove valueInteger. value will get saved to the database as an integer in the way you would except (its rounded value).