So I am trying to use Bean Validation to assertain that the DiscriminatorValue of a given entity can only be one of a selected few.
If the discriminator would have been an ordinary field, that would have been an easy task using #Pattern with a matching regexp.
Since it's not, how do I go about this?
The short answer is that you cannot do that with Bean Validation. As you say, the discriminator column/value is not even part of your entity. It is just a JPA internal value.
I guess my second questions is why you would want to do that. What do you want to achieve? At best these discriminator values are determined at development time by the developer. Provided you let the JPA provider handle the data there should never be a problem.
Related
I'm wondering whether it is possible to add additional functionality to the #Column annotation in JPA. Specifically, what I would like to do is tag columns of sensitive data with an #ProtectedColumn annotation: this would then tell the persistence framework to apply some type of data protection (encryption, tokenization, whatever...) to the values when storing them into the actual data store, and then reverse that process when reading the values from the data store.
So I might have a Customer class that included this code:
#Column(value="Name")
private String name;
#ProtectedColumn(value="CreditCardNumber", protectionType="ultra")
private String creditCardNumber;
Instead of storing the actual credit card number, this would then store the result of protecting the credit card number with the protection type "ultra" (whatever that may be).
Obviously, I don't want to re-implement all the database access functionality already present in the #Column annotation: I just want to extend its functionality. I know that annotations are not directly extensible (see Why is not possible to extend annotations in Java?), but it seems to me that it might be possible to intercept the value before it gets to the #Column annotation, so perhaps the field definition looks like this:
#Protected(protectionType="ultra")
#Column(value="CreditCardNumber")
private String creditCardNumber;
So my first question is whether this is even theoretically possible: if so, I'd appreciate any pointers on how to combine/extend annotations in this way.
You can use a converter. For example you can implement a Converter like he did:
use converter
He uses xml configuration.
If you want to use annotations, just have a look at these two java classes in this git repository:jpa converter with annotation
Therefore you can use the annotation
#Convert(converter = JPACryptoConverter.class)
(Given that JPACryptoConverter is a child of AttributeConverter).
Well short answer is No you can't simply extend the #Column annotation in Hibernate by adding a protection option to it but to provide a complete answer you can surely combine it with other annotations, to protect/encrypt a column in Hibernate you have two possible options:
Use Hibernate's #ColumnTransformer annotation to provide a customised column transformer for your column.
Use JPA Attribute Converter to provide a custom representation of your column.
Useful links:
For further reading about these two options you can check the following Thoughts On Java's tutorials:
How to map encrypted database columns with Hibernate’s #ColumnTransformer annotation.
How to implement a JPA Attribute Converter.
You can also check this answer to see how can you implement a custom Column Transformer.
A third option is to use Jasypth Integration library with Hibernate, you can read more about it in Integrating Jasypt with Hibernate 3.x or 4.x.
I have the following question, which is not covered in Hibernate documentation. Or I just couldn't find the answer there. Googling doesn't give me details also.
If we use an Entity object as a parameter and bind it to an HQL using Query.setParameter, what happens next?
Does hibernate uses only an entity ID of a mapped parameter object to build the SQL 'where' query?
Or Hibernate uses some extra logic (maybe in some cases) which is not limited to ID only? For example, hibernate add additional fields in SQL which are not primary keys.
Is it dangerous to use detached Entity object as a parameter?
Thank you in advance!
In terms of the SQL it will simply compare using the ids. The entity you bind does not have to be managed within that session as the comment on your question suggests.
Essentially what happens is that Hibernate will attempt to resolve the entity type of the entity instance it is given. It will then use that type to bind the JDBC parameter value, which will write just the identifier. So the assumption here is that the entity instance can resolved to its "entity type". That is usually easy in most situations. Where it gets difficult is in the case of Hibernate-specific "entity name" features.
Why is #ForceDiscriminator or its equivalent #DiscriminatorOptions(force=true) necessary in some cases of inheritance and polymorphic associations? It seems to be the only way to get the job done. Are there any reasons not to use it?
As I'm running over this again and again, I think it might help to clarify:
First, it is true that Hibernate does not require discrimination when using JOINED_TABLE mapping. However, it does require it when using SINGLE_TABLE. Even more importantly, other JPA providers mostly do require it.
What Hibernate actually does when performing a polymorphic JOINED_TABLE query is to create a discriminator named clazz on the fly, using a case-switch that checks for the presence of fields unique for concrete subclasses after outer-joining all tables involved in the inheritance-tree. You can clearly see this when including the "hibernate.show_sql" property in your persistence.xml. In my view this is probably the perfect solution for JOINED_TABLE queries, so the Hibernate folks are right to brag about it.
The matter is somewhat different when performing updates and deletes; here hibernate first queries your root-table for any keys that match the statement's where clause, and creates a virtual pkTable from the result. Then it performs a "DELETE FROM / UPDATE table WHERE pk IN pkTable" for any concrete class withing your inheritance tree; the IN operator causes an O(log(N)) subquery per table entry scanned, but it is likely in-memory, so it's not too bad from a performance perspective.
To answer your specific question, Hibernate simply doesn't see a problem here, and from a certain perspective they are correct. It would be incredibly easy for them to simply honour the #DiscriminatorValue annotations by injecting the discriminator values during entityManager.persist(), even if they do not actually use them. However, not honoring the discriminator column in JOINED_TABLE has the advantage (for Hibernate) to create a mild case of vendor lockin, and it is even defensible by pointing to superior technology.
#ForceDiscriminator or #DiscriminatorOptions(force=true) sure help to mitigate the pain a little, but you have to use them before the first entities are created, or be forced to manually add the missing discriminator values using SQL statements. If you dare to move away from Hibernate it at least costs you some code change to remove these Hibernate specific annotations, creating resistance against the migration. And that is obviously all that Hibernate cares about in this case.
In my experience, vendor lockin is the paradise every market leader's wildest dreams are about, because it is the machiavellian magic wand that protects market share without effort; it is therefore done whenever customers do not fight back and force a price upon the vendor that is higher than the benefits reaped. Who said that an Open Source world would be any different?
p.s, just to avoid any confusion: I am in no way affiliated to any JPA implementor.
p.p.s: What I usually do is ignore the problem until migration time; you can then formulate an SQL UPDATE ... FROM statement using the same case-switch-with-outer-joins trick Hibernate uses to fill in the missing discriminator values. It's actually quite easy once you have understood the basic principle.
Guys let me try to explain about #DiscriminatorOptions(Force=true).
Well , it is used in single table inheritence, i have recently used this in one of the scenario.
i have two entities which was mapped to single table. when i was trying to fetch the record for one entity i was getting list of result containg records from both the entities and this was my problem. To solve this problem i have used #DiscriminatorOptions(Force=true) which will create the predicate using Discriminator column with the specified value mapped to the corresponding entity.
so the query will be look like this after i used #DiscriminatorOptions(Force=true)
select *
from TABLE
where YOUR PREDICATE AND DiscriminatorColumn = DiscriminatorValue
I think this is more of my opinion but I think some will agree with me. I prefer the fact that Hibernate enables you to not use a discriminator. In several cases the discriminator isn't necessary.
For example, I have a Person entity which contains stuff like a name, a date of birth, etc. This entity can be used by several other entities like Employee or Customer. When I don't reference Person from other entities, but reference Employee or Customer instead, the discriminator isn't used as Hibernate is instructed to fetch either one.
#yannisf ForceDiscriminator is not the only solution to solve this issue.
You can do instanceof tests for each child class. Though this will be like hardcoding your classes in your code but is a cleaner way to solve the problem if the discriminator column is not populated.
This also helps your code avoid mixing jpa and hibernate annotations.
As pointed out by yannisf, instanceOf is kind of an antipattern in the OO world.
Another solution could be changing your entity mapping. Suppose an entity A has a refernce to a superclass B and B has child classes of type C1 and C2, the instead of A pointing to B, you can have C1 and C2 have a foreign key pointing to A. It all comes down to changing the entity design so as not to mix annotations.
Thanks
Vaibhav
I'd like to know the answer to this simple question.
When I create an entity object and I want to restrict a setting of an attribute (for example I don't want to allow anyone to set an integer value less then 1 to an attribute), should I implement it in the setter of this attribute or should I check this restriction latter in a class that handles these objects ? Generally, can I implement getters and setters however I want as long as my getters return and setters set attributes ?
I know there are some rules (code conventions) in java, so I don't want to break any of them.
Thanks in advance, hope that my question is clear enough and sorry for any grammar mistakes I might have made :/ .
Yes getters/setters are useful for that.
for example:
public void setAge(int age){
if(age < 0){
throw new IllegalArgumentException("Invalid age : " + age);
//or if you don't want to throw an exception you can handle it otherways too
}
}
You can also use Java-EE's Bean Validators for this
public class Person{
#Min(value = 0)
#Max(value = 99)
private Integer age;
//some other code
}
My preferred approach is to use JSR 303 (Bean Validation API) to ensure that the properties of the class are valid.
It is quite alright to perform validation in setters, but this is not always a desirable approach. There is the potential of mixing the needs of several contexts that are not related to each other. For example, some of your properties must never be set from the user-interface, and would instead be computed by a service, before being persisted. In such an event, it is not desirable to have this logic inside a setter, for you would need to know the context in which the setter is being invoked; you'll need to apply different rules in your UI layer and in your persistence layer. JSR 303 allows you to separate these concerns using validation groups, so that your UI validation group is different from your persistence validation group.
In JPA 2.0, when you annotate your class using constraints that are evaluated by a JSR 303 validator, your persistence provider can automatically evaluate these constraints on the PrePersist, PreUpdate and PreRemove (typically not done; see below) lifecycle events of entities. To perform validation of entities in your JPA provider, you must specify either the validation-mode element or the javax.persistence.validation.mode property in your persistence.xml file; the values must be either AUTO (the default) or CALLBACK (and not NONE).
The presence of a Bean Validation provider is sufficient to ensure that validation occurs on JPA entity lifecycle events, as the default value is AUTO. You get this by default, in a Java EE 6 application server; Glassfish uses the RI implementation of JSR 303 which is Hibernate Validator, and it works quite well with EclipseLink as well.
The CALLBACK mode will allow you to override the validation groups that are to be applied when the lifecycle events are triggered. By default, the default Bean validation group (Default) will be validated for update and persist events; the remove event does not involve any validation. The CALLBACK mode allows you to specify a different validation group for these events, using the properties javax.persistence.validation.group.pre-persist, javax.persistence.validation.group.pre-update and javax.persistence.validation.group.pre-remove.
Do keep in mind that JSR 303 validation can be used outside a Java EE container, although the Bean Validation API documentation link that I've posted above is from the Java EE 6 API documentation.
This is the goal of getters and setters.
If we cannot add some behavior in these methods, well... why don't we use public attributes ?
From my understanding of your question, it pretty much related to encapsulation OO principle.
You can have a look at this article: http://www.tutorialspoint.com/java/java_encapsulation.htm
Getters and setters are great for adding the restrictions, just like Jigar Joshi has in his answer. That way you get feedback immediately and can handle the problem when it is introduced.
Another solution would be to use object validation (something like a JSR-303 implementation) which would allow you to annotate the field with a min and max values. Something like
#Min(value=1)
private int myvalue;
Then you can validate the entire object in one go and get all messages if you have other constrained fields. This is obviously not useful everywhere, but if it fits your need it is an option.
Finally, when you say "entity" I think of something stored in a database or related to ORM tools. If that is the case, you will want to be careful with what you do in your getter. For instance, if you do lazy initialization in the getter some ORM suppliers will mark the entity as dirty and attempt to flush it to the database possibly causing an unintended write.
I'm stuck with validation in my current use case.
My app has standard structure (WEB <-> EJB3 Services <-> EJB3 DAO <-> DB).
I have an entity which has validation annotations applied to it.
#Entity
class PhoneNumber {
...
private NumberType numberType;
}
where
enum NumberType {
FIXED,
MOBILE,
ANY
}
Now I have new validation rule to be applied. On PhoneNumber update there should be not possible to change NumberType to ANY if it was set previously to either FIXED or MOBILE.
My Bean Validation rules are checked just before db operations, and the rule above should be applied in service layer (at least I think so) to have a DB access to get previous entity version to compare.
But without having bean not yet validated I'm forced to check manually if e.g. numberType is not null.
Can you please provide me some advices or general rules how to deal with more complex busines validations (not only checking single field's values in isolation) when using Bean Validation?
I don't think Bean Validation is the right solution for implementing this kind of business logic.
Instead you could implement this check in the setNumberType() method of the PhoneNumber entity. There you have the old value at hand and compared to an implementation in the service layer there is no chance to perform an illegal state transition by circumventing (accidentally or intentionally) the service implementing the check.
Here you can find a good description of how to write a custom validator which can do "cross-field" validation.