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.
Related
I am writing one model class in spring mvc.
I want to do domain validation.
In domain class I have 3 variables say isABCApplicable,abcValue1,abcValue2:
private Boolean isABCApplicable;
private BigDecimal abcValue1;
private BigDecimal abcValue2;
.......getters and setters.........
Now my aim is :
If isABCApplicable is true then i want to make abcValue1 as
#NotNull
abcValue1
and
#NotNull
abcValue2
Is there any way to achieve this?
Why do you want use annotations?
Create your own DataBinder for your model-object and do all validation in object constructor.
Or if you realy want annotations, you can write your own constraint like #NotNullIfApplicable, see Cross field validation with Hibernate Validator (JSR 303) for details.
I believe this is possible by bytecode instrumentation. ASM is one such tool you can manipulate Java bytecode with. Have a look at http://asm.ow2.org/index.html
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 trying to build a Play! app running against an existing database where all the columns have underscores to separate words. This means I have to put an #Column annotation on each field to specify a different name. Is there anyway to get Play! to use underscores by default?
If Play uses Hibernate, as the other answers suggest, you will have to implement a custom NamingStrategy.
Here's a sample NamingStrategy that converts all column names from lower camel to lower case with underscores, using Guava:
public class CustomNamingStrategy extends ImprovedNamingStrategy {
private static final long serialVersionUID = -306957679456120781L;
#Override
public String columnName(final String columnName) {
return CaseFormat.LOWER_CAMEL
.to(CaseFormat.LOWER_UNDERSCORE, columnName);
}
}
Configure it like this:
add the following line to
application.conf to configure the
NamingStrategy
hibernate.ejb.naming_strategy=<your naming strategy classname>
Reference:
Implementing a custom
NamingStrategy (Hibernate Reference)
NamingStrategy (Hibernate JavaDocs)
How to set up a different Naming
Strategy (Play-Framework Google Group)
CaseFormat (Guava JavaDoc)
The solution given works great, but as stated in the comments, ImprovedNamingStrategy does that, so there's no need to use Guava.
An improved naming strategy that prefers embedded underscores to mixed
case names
You can simply add hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy in your application.conf (Tested in Play! Framework 1.2.5)
Well, if I read the doc correctly, the play framework is using Hibernate as the JPA implementation. AFAIK, there's no way to change the standard naming of Hibernate, so you'd have to add the annotations or provide the column names in the mapping XML file.
I want to have a column for an entity which only accepts one of an enumerated set of values. For example let's say I have a POJO/entity class "Pet" with a String column "petType". I want petType to only allow one of three values: "cat", "dog", or "gorilla". How would I go about annotating the getPetType() method in order to have a database level constraint created which enforces this?
I am allowing Hibernate to create or update my database table at application start up via the Hibernate property "hbm2ddlauto" being set to "update".
I have tried using a parameterized user type in association with the #Type annotation but this doesn't appear to provide any sort of constraint on the database column itself. There doesn't appear to be a way of specifying this sort of constraint in the #Column annotation short of using some SQL with the columnDefinition element, and I'm hesitant to go this route since it seems that whatever I use there will not be cross platform/database independent (important to me since I run my code in production on Oracle but I do testing locally using HSQLDB and Derby). Maybe what I want to do just can't be done simply using annotations.
Thanks in advance for any insight you can give me on this topic.
Create a enum of type PetType and defined you mapping as
#Enumerated(EnumType.STRING)
That way, strings are stored in the database and your java enum type only accept the 3 values you specify.
For a project I am working on, I need to persist a number of POJOs to a database. The POJOs class definitions are sometimes highly nested, but they should flatten okay, as the nesting is tree-like and contains no cycles (and the base elements are eventually primitives/Strings). It is preferred that the solution used create one table per data type and that the tables will have one field per primitive member in the POJO. Subclassing and similar problems are not issues for this particular project.
Does anybody know of any existing solutions that can:
Automatically generate a CREATE TABLE definition from the class definition
Automatically generate a query to persist an object to the database, given an instance of the object
Automatically generate a query to retrieve an object from the database and return it as a POJO, given a key.
Solutions that can do this with minimum modifications/annotions to the class files and minimum external configuration are preferred.
Example:
Java classes
//Class to be persisted
class TypeA {
String guid;
long timestamp;
TypeB data1;
TypeC data2;
}
class TypeB {
int id;
int someData;
}
class TypeC {
int id;
int otherData;
}
Could map to
CREATE TABLE TypeA (
guid CHAR(255),
timestamp BIGINT,
data1_id INT,
data1_someData INT,
data2_id INt,
data2_otherData INT
);
Or something similar.
I would use the standardized Java Persistence API (JPA), preferably with annotations. Regarding your requirements:
This is not required by the specification but most JPA providers (all major implementations do) support DDL generation from the mapping metadata.
EntityManager#persist(Object entity) does that.
<T> T EntityManager#find(Class<T> entityClass, Object primaryKey) does that.
As hinted, JPA is an API, you need an implementation to use it. My preference goes to Hibernate Entity Manager or EclipseLink (see this previous question).
Hibernate can help you solve all the three problems you listed.
(1) You need to annotate your entity classes so Hibernate is able to map between classes/objects to tables/rows. Hibernate uses a convention over configuration approach so it is possible to use just a few annotations and have a complete o/r mapping ready for use. You could use the hibernate.hbm2ddl.auto configuration option to instruct Hibernate to automatically validate/export and schema DDL when the session factory is first created.
(2) / (3) Hibernate has enough information about classes, database schema and mappings to allow it generate SQL statements for simple CRUD operations with minimal effort. You can fine tune how Hibernate loads and persists a tree of objects. Association mapping annotations have the fetch and cascade options that let you specify how associated objects are fetched (lazy / eager) and how operations are propagated through the object tree. Please refer to the Hibernate documentations for the details about these options.
If you are new to Hibernate, I recommend the good Hibernate documentation as reference and the book Java Persistence with Hibernate for the deeper understanding about the framework (it has very good sections about fetching and cascading).
In a typical scenario, Hibernate requires just a bit of configuration (one hibernate.cfg.xml file). You can define the mappings using XML files (no good) or annotations (the "default" option for new projects).
You tagged your question as Hibernate. Have you tried using Hibernate for this?
As long as you define well how collections should be mapped (e.g., one-to-many), I've found it generally very effective for this kind of thing.
The Hibernate tutorials provide a lot of examples for situations that are similar to the code you provided.
A highly recommended framework is JPersist, an extremely simple Database-to-POJO framework. No XML or annotations needed. I use it it my project because if I want a new table object, I simply create a bean.
The issue though in your situation is your wanting something to setup the database for you. Doing that would be very hard and your asking alot from a framework. With JPersist, you should be able to create a db table from class name and columns from fields, and then use phpMyAdmin's designer to resolve references.
5 min of reading the documentation for JPersist now will save hours in development time later.
JPA provides sufficient options to do this. For example you can use #Embeddable and #Embedded:
#Embeddable
class TypeB {
int id;
int someData;
}
class TypeA {
....
#Embedded
TypeB data1;
}
You can either manually create the underlying schema, or let something like hbm2ddl.auto=update to create it for you.