Automatically apply field conversion function in Hibernate - java

I have a database table with a field that I need to read from and write to via Hibernate. It is string field, but the contents are encrypted. And for various reasons (e.g. a need to sort the plain text values), the encrypt/decrypt functions are implemented inside the database, not in Java.
The problem I'm struggling with now is finding a way to invoke the encrypt/decrypt functions in Hibernate-generated SQL everywhere that the field is referenced and in a way that's transparent to my application code. Is this possible? I've looked into Hibernate's support for "derived" properties, but unfortunately, that approach doesn't support read-write fields. Any ideas appreciated.

I don't think there's a way to make encryption like you've described it completely transparent to your application. The closest thing you can get is to make it transparent outside of entity. In your entity class:
#Entity
#SQLInsert(sql="INSERT INTO my_table(my_column, id) VALUES(encrypt(?),?)")
#SQLUpdate( sql="UPDATE my_table SET my_column = encrypt(?) WHERE id = ?")
public class MyEntity {
private String myValue;
....
#Formula("decrypt(my_column)")
public String getValue() {
return myValue;
}
public void setValue(String value) {
myValue = value;
}
#Column (name="my_column")
private String getValueCopy() {
return myValue;
}
private void setValueCopy(String value) {
}
}
value is mapped as derived property, you should be able to use it in queries.
valueCopy is private and is used to get around derived property being read-only.
SQLInsert and SQLUpdate is black voodoo magic to force encryption on insert / update. Note that parameter order IS important, you need to find out what order Hibernate would generate parameters in without using custom insert / update and then replicate it.

You could have a trigger internal to the database that, on retrieval, decrypts the value and replaces the returned result and on insert encrypts the value and replaces the stored result with the encrypted value. You could also do this with a view wrapper - i.e. have an insert trigger on the view, and have the view automatically decrypt the value.
To better explain: have a view that decrypts the value, and an on insert trigger that encrypts the value that is linked to the view.

Actually, in the end, I went a different route and submitted a patch to Hibernate. It was committed to trunk last week and so I think it will be in the next release following 3.5. Now, in property mappings, you can specify SQL "read" and "write" expressions to call SQL functions or perform some other kind of database-side conversion.

Assuming you have access to the encrypt/decrypt algorithm from within Java, I would set up my mapped class something like
public class encryptedTable {
#Column(name="encrypted_field")
private String encryptedValue;
#Transient
private String value;
public String getEncryptedValue() {
return encryptedValue;
}
public String getValue() {
return value;
}
public void setEncryptedValue(String encryptedValue) {
this.encryptedValue = encryptedValue;
this.value = decrypt(encryptedValue);
}
public void setValue(String value) {
this.value = value;
this.encryptedValue = encrypt(value);
}
}
And then use get/set Value as the accessor within your program and leave the get/set EncryptedValue for Hibernates use when accessing the database.

Why not just use the SQl server encryption that seems to already be in place by calling a stored proc in Hibernate instead of letting Hibernate generate a query?

Related

Generate sequence NexVal without execute select query

My class is not Entity there is code fragment
#SequenceGenerator(name="seqUniqueKeyGenerator",sequenceName="SEQ_UNIQUE_KEY",allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="seqUniqueKeyGenerator")
#Id
private Integer sequenceId;
public Integer getSequenceId() {
return sequenceId;
}
public void setSequenceId(Integer sequenceId) {
this.sequenceId = sequenceId;
}
public static void main(String[] args) {
UniqueKeyGenerator uniqueKeyGenerator = new UniqueKeyGenerator();
System.out.println(uniqueKeyGenerator.getSequenceId());
}
I want retrieve nextVal like this, is it possible?
You can consume nextVal as mentioned in this thread but you have to consider it is consumed by means of a SQL sentence, which means this is a solution coupled to database.
I don't know a way to consume nextVal in such way you are asking above.
We all know the default behaviour of Hibernate when using #SequenceGenerator - it increases real database sequence by one, multiple this value by 50 (default allocationSize value) - and then uses this value as entity ID.
From G. Demecki
This means that the ID id not generated in your Java-Program. It is created in your database. So you can't read it before it is acutally generated in the database. You can guess it using the formula described by G.Demecki but that is certainly not the way to go.
If you want the id of an entity just save it and read the id from the return value of save what should be the saved entity itself.

A way to generate code from an instance of a Java Object

I know that HashCode is a way, but I've noticed that after a while the HashCode change. So, I have an application that permit to buy things, every article is identified by a code generated by now from the hashcode and stored in the db PostgreSQL, but I have discovered this issue so I can't use it. Infact the next day that I try to identify this article on the db the hashcode changed so it doesn't works. What is a solution? Thanks a lot!
My object that generate code for article is something like this
public class AcquistoDVDRichiesto implements IsSerializable, CustomEnum {
private int codice_carrello;
private String utente;
private int numero;
private String film;
private int fornitura;
public AcquistoDVDRichiesto(){}
public AcquistoDVDRichiesto(int c, String user){
utente=user;
codice_carrello=c;
}
public void generateCodeBasket(){
if(film!=null && numero!=0 && fornitura!=0){
codice_carrello=Math.abs(film.hashCode()+((Integer)numero).hashCode()+
((Integer)fornitura).hashCode()+tipo_supporto.DVD.hashCode());
}
}
}
-
You shouldn't generate db primary keys by hand. The best approach is to let the database generate the unique primary keys for each record. This way you can be sure that there will be no primary key collisions and the codes will not change.
In PostreSQL, you can use a SERIAL column type to achieve that. Example:
CREATE TABLE tablename (
colname SERIAL
);
The other way is to use a sequence, but it is a bit more complicated.

Need to know if each field has changed, how should I model this in Hibernate

So I have a class with three fields that maps to a table using hibernate
Class Widget
{
String field1;
String field2;
String field3;
}
On application startup a number of instances these widgets will be added to the database from an external files, but when I exit the application I need to know which (if any) of these fields have been changed by the user since the application was started, so the changes can be saved back to the files. I also need to store the original value for logging purposes.
I can't work whether I need a status field in the table or whether there is already a way of doing this using Hibernate/Database.
EDIT:A good solution to the program was given below . however the main reason I am using Hibernate is to reduce memory consumption so storing the original values when changed is not a good solution for me , I want everthing stored in the database. So I have create this new question How do I store a copy of each entity I add to database in Hibernate
Given an entity like the following you can track changes on one of it's field (while preserving its original value too).
#Entity
#Table(schema = "test", name = "test")
public final class Test {
private static final int ORIGINAL = 0;
private static final int CURRENT = 1;
private Integer id;
// holds the original and current state of the field
private final AtomicReferenceArray<String> field = new AtomicReferenceArray<>(2);
#Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
#Transient
public String getOriginalField() {
return field.get(ORIGINAL);
}
#Basic
public String getField() {
return field.get(CURRENT);
}
public void setField(String field) {
this.field.compareAndSet(ORIGINAL, null, field);
this.field.set(CURRENT, field);
}
#PreUpdate
public void preUpdate() {
System.out.format("Original: %s, New: %s\n", getOriginalField(), getField());
}
...
}
If there is a single row in a database like this:
id: 1
field: a
version: 2011-12-02 11:24:00
before the field gets updated (say, from a to b) you'll get the following output.
Original: d, New: b
The original value gets preserved even if the the entity is updated multiple times and both state can be accessed through the corresponding getters (getField and getOriginalField—you can get more creative than me in the naming :).
This way, you can spare yourself from creating version columns in your database and also can hide the implementation details from clients.
Instead of an AtomicReferenceArray you could use arrays, lists, etc, to track all changes like this way.
The #PreUpdate isn't necessary of course, but this way you can be notified of changes in the entity's state and atomically save the updated fields into file. There more annotations like these: see the documentation for javax.persistence for other annotation types.
If you are using MySql then you can get table's last update time from information_schema database like
SELECT UPDATE_TIME FROM `information_schema`.`tables`
WHERE TABLE_SCHEMA = 'dbName' AND TABLE_NAME = 'tableName'
Or else simple solution will be to add a column for update time stamp. By this you can even monitor which particular row has been updated.
If you need to synchronize with files as soon as you save into database, You can use the Hibernate event mechanism to intercept any save to database and save it to file, here's a sample doing that.

Getting Callback after all setters called to add extra fields?

I'd like to override a setter so that I can perform some function on the data so I can return a calculated column for my entity. The function depends a several columns (e.g. COL1, COL2, ...) so I can't really intercept any particular setter because the other values might not yet be populated. Does hibernate provide some sort of "finish()" method that can be called once at the values are set for the Entity?
#Override
#Column(name="COL1")
public String getCol1() {
return this.col1;
}
#Override
public void setCol1(String value) {
super.setCol1(value);
genMagicValue();
}
public String getMagicValue() {
return this.magicValue();
}
I don't understand your question,
setCol1 may be never called (and left with its default value).
Furthermore, no one is preventing you to call it twice with different values.
perhaps the pattern you are looking for is:
boolean magicDone=false;
public String getMagicValue() {
if (!magicDone){
magicDone=true;
genMagicValue();
}
return this.magicValue();
}
Aside from what hibernate provides, is it not possible to lazy init the magicValue so that the calculation happens the first time getMagicValue is called and subsequent calls to getMagicValue just return the computed value?

Best practice for storing global data in J2EE App using Hibernate

I'm looking for the best solution to store Java EE application's global data using Hibernate. It will consist of key value pairs. Example:
projectStarted = "10-11-11"
developerNumber = 3
teamLeader = "John"
As you see, all of this entries have different types.
For now I see two options:
1) Create GlobalData entity. Each field of it will be represented as unique column in the table and will contain unique setting. This way I have no problems with type casting, but I would like to avoid it in case where there will be big amount of settings.
2) Create Setting entity. Each of it will contain two fields: key(Primary key) and value and will be represented as unique record in the table. This is preferable solution, but It's seems to me that I will get a lot of type casting, because settings can be any type.
So basically, I'm looking for the way to implement second solution without getting a lot of troubles from different types. Can anybody help me?
Thanks.
Edit 1.
Yeah, thanks Christian. Just got similar idea.
What if I will have Settings entity, which will be like:
#Entity
#Table(name = "settings")
public class Setting {
#Column
private String key;
#Column
private String value;
#Column
private String converterClassFullName; //example by.lugovsky.MyConverter
//Getters, setters
}
And GlobalData class.
public class GlobalData {
private Date projectStarted;
private int developerNumber;
private String teamLeader;
Set<Setting> settings;
//Getters and setters for all, except settings.
}
So basically my idea is to convert Setting entity before persisting/updating/ etc. I can do this in my DAO, but I was wondering, if I could annotate GlobalData class with #Entity annotation as well without creating new table. This way I can set OneToMany annotation to Setting's set and Perform conversions in the internal #PrePersist etc. methods.
Will Hibernate allow me to do this?
Thanks again
You could store a Converter-Class into the db and the let it run through the given converter for a property before using the value. JSF offers Converter API:
public interface Converter{
public Object getAsObject(FacesContext fc, UIComponent component, String value) throws ConverterException;
public String getAsString(FacesContext fc, UIComponent component, Object obj) throws ConverterException;
}
If you have a schema with
name: String
value: String
converter: Class
then you could do something like this:
PropertyEntry pe = // Get from OR-Mapper
Converter c = (Converter) pe.getConverter().newInstance();
Object o = c.getAsObject(null, null, pe.getValue());
// use the object o instead of value
For even more coolness you could also define a field in the class which will not be persisted which you could use to hold the converted value within the object.

Categories

Resources