How to handle choice field with JPA 2, Hibernate 3.5 - java

I have an entity with Integer attributes that looks like this in proto code:
class MyEntity:
String name
#Choices({1, "BSD", 2, "Apache", 3, "GPL"}
Integer frequency
#ChoicesSegment({1000, 2000, 3000}, {"BSD", "Apache", "GPL"})
Integer type
String getFrequency()
return getChoice("frequency", frequency)
String getType()
return getChoice("type", type)
maybe this solution is more feasible:
class MyEntity:
String name
final static private Something? frequencyChoices = {1000, 2000, 3000}, {"BSD", "Apache", "GPL"}
Integer frequency
final static private String[] typeChoices = new String[] {"BSD", "Apache", "GPL"}
Integer type
#Choices(MyEntity.frequencyChoices)
String getFrequency()
return frequency)
#IntervalChoices(MyEntity.typeChoices)
String getType()
return type
*get** accessors return strings according to this table.
value(type) HumanReadableString(type)
1 BSD
2 Apache
3 GPL
min frequency max frequency HumanReadableString(frequency)
0 1000 rare
1000 2000 frequent
2001 3000 sexy
It should be possible to get all possible values that an attribute can take, example:
getChoices(MyEntity, "type") returns ("rare", "frequent", "sexy")
It should be possible to get the bound value from the string:
getValue(MyEntity, "frequency", "sexy") returns (2000,3000)
edit: purpose of all of this This methods should simplify the generation of forms and requests (of course this should not be view implementation bound)
edit: added how I would like to tell Java that some attributes are spécial so that it can generate get* accessors acordingly.
edit: added how to submit in the code the choices
edit: the only thing that I store in the db is integers, when I want to print them they should be converted somehow to their human readable string.

You can have additional info in enums:
public enum License {
GPL("GPL"),
APACHE("Apache License");
public License(String displayName) {
this.displayName=displayName;
}
String displayName;
}
Additional functions as required, but have a close look which functions are already provided by the Enum classes.

You can do it without any hassle (but note, that the value in the DB will be the ordinal() value of the enums. So:
public enum License { GPL, APACHE, BSD }
FrequencyChoices could go into an #ElementCollection.
If you need human readable values, you may want to convert your Enum to an ordinary class, and persist it as a separate table, so you can add new licenses more easily to the list...
#Entity
public class License {
#Id long id;
String name;
}

I have not tried to persist this but you can try following http://marekhalmo.blogspot.sk/2012/09/cool-java-enums.html
I would stick to enums.

Related

Jackson in JSON Output: Rename Fields in Multiple Use Cases (without a single JsonProperty)

Is it possible to rename JSON output fields in an object an arbitrary number of times when outputting with Jackson?
I can use a one-time JsonProperty as shown here,
How to map JSON field names to different object field names?
But suppose I have a single class which is used in multiple outputs. In each output, I want to have the flexibility of defining which name(s) to change.
public class Study implements Serializable {
// Can vary as "id" / "studyId" depending on call
private int id;
// Can vary as "description" / "studyDescription" / "studyDesc" depending on call
private String description;
}
Or do I need to create new objects for each case?
Do refer: https://www.baeldung.com/json-multiple-fields-single-java-field
It's as simple as using #JsonAlias in combination with #JsonProperty annotation as below:
public class Study implements Serializable {
// Can vary as "id" / "studyId" depending on call
#JsonProperty("id")
#JsonAlias("studyId")
private int id;
// Can vary as "description" / "studyDescription" / "studyDesc" depending on call
private String description;
}
PS: Using #JsonProperty twice didn't work :D

Should I use a wrapper or primitive type as field while using Jackson

I'm using Jackson JSON parser. I have simple data transfer object which should be returned via REST service.
public class PersonDto {
private String name;
private Integer age; // int?
public PersonDto(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public Integer getAge() {
return this.age;
}
}
Should I favor wrapper classes over primitive types as fields in such case? Which of these approaches has more advantages, except that wrapper is nullable?
Wrapper class: java.lang.Integer
Pros:
Allows null value, therefore giving the chance to the user to leave a blank / non-specified field
Fast when values are between -128 and 127, as it uses its internal cache instead of creating new objects
Integer::valueOf for parsing String's
Cons:
Immutable: if you have to reset the value (e.g: because it exceeded a certain range), you'll have to re-create a whole new Integer instance.
Slower calculation performance
java.lang.Integer is a reference to an instance. Each calculation will recreate a whole new Object
Primitive type: int
Pros:
Mutable (as long as non-final, and a setter is provided): easily allows: this.age = age > MAX_AGE ? blabla : age;
primitive types calculations are faster
Integer::parseInt for parsing String's
Cons:
Non-selected values will be automatically set to 0; this may create confusion on whether the user selected 0 as value, or did not select anything
It seems the only advantage of java.lang.Integer is the blank / null value.
Also, in cases where a wrapper is needed, e.g:
List<Integer> myList;
you can directly add an unboxed value myList.add(2); which is interpreted by the compiler as myList.add(Integer.valueOf(2));
I found using wrapper in DTO's beneficial. With Jackson, for nullable fields you can set #JsonInclude(JsonInclude.Include.NON_NULL) on top of the DTO object and reduce the number of data sent via network (null fields will not be present in resulting JSON), thus resolving ambiguity if value is 0 or not present, telling the front-end that no node is present and hence no processing/displaying data is needed.
For non-nullable numeric data, primitive works well.
Also, for floating-point data that are not supposed to be used in front-end in arithmetic calculations, one can use String with rounding performed on server-side. I saw this technique multiple times in REST API's.
DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.CEILING);
System.out.println(df.format(d));

javax.validation: Constraint to validate a string length in bytes

I'm using javax.validation to validate some bean fields' values.
This is what I use normally:
public class Market {
#NotNull
#Size(max=4)
private String marketCode;
#Digits(integer=4, fraction=0)
private Integer stalls;
// getters/setters
}
This will make sure that every Market instance has a market code with a maximum length of 4 characters and a number of stall with a maximum of 4 integer digits and 0 decimal digits.
Now, I use this bean to load/store data from/to DB.
In the DB I have table Markets defined like this:
CREATE TABLE MARKETS (
MARKET_CODE VARCHAR2(4 BYTE) NOT NULL,
STALLS NUMBER(4,0)
)
As you can see, I have MARKET_CODE which can be at most 4 bytes long. The #Size annotation will check if the string is at most 4 characters long, which is wrong.
So, the question is: is there an annotation like #Size that will check for the string bytes instead of the characters?
Check the Hibernate Validator documentation on Creating custom constraints.
Your validator will need to encode the String into a byte[], using some default or specified Charset. I imagine you might well use UTF-8.
Maybe something like this, which uses a hard coded UTF-8 encoding and assumes a suitable annotation, as outlined in the Hibernate documentation linked.
public class MaxByteLengthValidator implements ConstraintValidator<MaxByteLength, String> {
private int max;
public void initialize(MaxByteLength constraintAnnotation) {
this.max = constraintAnnotation.value();
}
public boolean isValid(String object, ConstraintValidatorContext constraintContext) {
return object == null || object.getBytes(Charsets.UTF_8).length <= this.max;
}
}

Naive use of java enums' ordinal in the context of JPA spelling disaster in the domain model

I would like to describe a nasty issue that occurs when making naive use of Java enums in the context of JPA entities. Let's take a look at how this problem can occur.
First the domain model:
Say I have an Text JPA entity that represents piece of text (novel, news article, etc.). Here is the JPA entity:
#Entity
public class Text {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Version
#Column(name = "version")
private Integer version;
private String content;
#Enumerated
#ElementCollection
private Set<Style> styles;
//Setters and getters omitted.
To an instance of Text, one or many styles can be applied such as italic, bold and so on. The style is represented as a java enum.
To start with, we assume that the application starts its life with the following enum:
public enum Style {
BOLD, ITALIC
}
The test below will then insert the following lines in a relational database:
Integration test:
#Test
#Rollback(value=false)
public void inEarlyLifePersist() {
Text text =new Text();
text.setContent("This is my beautiful novel...");
text.setStyles(EnumSet.of(Style.BOLD, Style.ITALIC));
text.persist();
}
Data in text table:
# id, content, version
11, This is my beautiful novel..., 0
*Data in text_style table:*
# text, styles
11, 0
11, 1
Then, later on, some ill-advised developer decides to add a new style: STRIKE_THROUGH to our Style enum placing this new enum constant/value as the first one:
public enum Style {
STRIKE_THROUGH, BOLD, ITALIC
}
and then a new record is inserted in DB as follows:
#Test
#Rollback(value=false)
public void afterChangeToEnumPersist() {
Text text =new Text();
text.setContent("This is my beautiful short story...");
text.setStyles(EnumSet.of(Style.STRIKE_THROUGH, Style.BOLD));
text.persist();
}
In text table:
# id, content, version
14, This is my beautiful short story..., 0
And *in text_style table:*
# text, styles
14, 0
14, 1
Obviously, the domain model is seriously compromised now!
My question is what are the possible strategies to avoid spelling disaster in the domain as is the case above (other than the obvious solution to place the STRIKE_THROUGH enum constant after ITALIC)?
edit 1: Obviously I do not want to store strings (see EnumType.STRING) in my database for obvious performance reasons i.e. data retrieval and storage performance would be seriously affected!
You need to redefine your enum like below.
public enum Style {
STRIKE_THROUGH(2), BOLD(0), ITALIC(1)
Style(int code){
this.code=code;
}
}
And implement a Hibernate User type to persist the code.
There's an option (EnumType.STRING) to use the actual name of the enum value (the String returned by { name() } instead of the ordinal. That way you can reorganize your enum values, but then you are tied to the names of the enum values.
The ideal solution would be to be able to declaratively tell the JPA implementation to use an arbitrary property of the enum as the database identifier. But AFAIK, that it's not provided in the current JPA specs, it would be great to have such a feature in future JPA specs.
The answer by Sajan shows how to implement that using a Hibernate-specific feature.
The Enumerated annotation also knows a property that specifies the EnumType. Two types exist: EnumType.ORDINAL and EnumType.STRING. ORDINAL is the default one.
So, if you do it the following way
#Enumerated(EnumType.STRING)
you will see the enumeration names in the DB column (and not the ordinals). Of course, you are now vulnerable to name changes in your enumeration. You have to die one death, but I think, the names are better.
I can't see why people find the enum names more reliable than their ordinals. Actually, there are many good reasons for renaming enums (fixing typos, changed names due to politics or political correctness, etc.), but I can't see any good reason for reordering them.
Both renaming and reordering happens and the only thing which can help is a test. Unfortunately, the best test I can think of will fail on any change. Fortunately, the test can tell what happened and then be fixed easily.
public void testE1IsStable() {
assertEnumUnchanged(E1.class, 4, "bec419c8380dbe9ec3b86a7023a55107");
}
public void testE2IsStable() {
assertEnumUnchanged(E2.class, 3, "1e89e93c6cbdbb7311b814c19d682548");
}
private void assertEnumUnchanged(Class<? extends Enum<?>> enumClass, int expectedCount, String expectedHash) {
final Object[] enumConstants = enumClass.getEnumConstants();
if (expectedCount < enumConstants.length) {
final Object[] shortened = Arrays.copyOf(enumConstants, expectedCount);
assertEquals("Enum constants may be only appended! Ask balteo!",
expectedHash, hashAsString(shortened));
fail("An enum constant has been added! This test needs to be updated. Ask balteo!");
} else if (expectedCount > enumConstants.length) {
fail("Enum constants must not be removed! Ask balteo!");
} else {
assertEquals("Enum constants must not be reordered! If they get renamed, this test must be updated. Ask balteo!",
expectedHash, hashAsString(enumConstants));
}
}
private String hashAsString(Object[] enumConstants) {
final Hasher hasher = Hashing.md5().newHasher();
for (final Object o : enumConstants) hasher.putUnencodedChars(o.toString());
return hasher.hash().toString();
}

How do I map a BigDecimal in Hibernate so I get back the same scale I put in?

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.

Categories

Resources