How can I save UTF-8 strings in an ObjectDB JPA database? - java

I'm trying to save some UTF-8 strings in the ObjectDB database with the following code:
ResourceBundle entries = Utf8ClassLoader.getBundle("localization/language", "fa-IR"); // fa-IR is a UTF-8 and RTL language
Enumeration<String> keys = entries.getKeys();
for (String key = keys.nextElement(); keys.hasMoreElements(); key = keys.nextElement()) {
ResourceEntity entity = new ResourceEntity();
entity.setId(new ResourceEntity.PKC(key, locale));
entity.setValue(entries.getObject(key));
resourceDAO.persistOrUpdate(entity);
}
The model:
#Entity
public class ResourceEntity {
#EmbeddedId
private PKC id;
private Object value;
// Getters and setters omitted
#Embeddable
public static class PKC {
String key;
String locale;
public PKC() {}
public PKC(String key, String locale) {
this.key = key;
this.locale = locale;
}
// Getters and setters omitted
}
}
localization/language_fa_IR.properties exists and opens properly.
The DAO's persistOrUpdate method is nothing more than an EntityManager.persist function within a transaction. (and does EntityManager.merge If the key exists)
But when I open the ObjectDBViewer, I see this:
How can I save the strings without changing characters?

ObjectDB as an object database just retrieves the objects with their content, including string fields, as you store them. So usually the encoding is not relevant for using ObjectDB, as it just preserves the state of your strings in memory.
It may, however, affect your ability to view special characters in the Explorer. For this you may also try setting an encoding in the Explorer using Tools > Options > General > Encoding.

There was no problem with the ObjectDB actually, the ResourceBundle was returning a not-UTF string. I solved it with this line:
entity.setValue(new String(entries.getString(key).getBytes("ISO-8859-1"), "UTF-8"));

Related

How to encrypt/decypt data with custom anotation(hibernate) in spring project

I'm developing some RESTFull web services for a project. I use the Spring framework and use gradle for build.
The problem is , I want to encrypt and decrypt data table when write and read data. I already have a algorithm(class) to encrypt and decrypt data with AES etc. What I need is, how annotate this method to hibernate entity class, am I need to create bean for this class ?
Ex:-
#Column(columnDefinition= "LONGBLOB", name = "card_no")
#ColumnTransformer(
read="decrypt(card_no)",
write="encrypt(?)")
private String cardNo;
Like this I want to add my own encryption/decryption java method to here.
If you have access to JPA 2.1, I would advocate for the use of an #Convert annotation with an AttributeConverter implementation.
An AttributeConverter defines a contract between the state of an entity property when it is serialized to the datastore and when it is deserialized from the datastore.
public class CreditCard {
#Convert(converter = CreditCardNumberConverter.class)
private String creditCardNumber;
}
Your converter implementation might look like this
public class CreditCardNumberConverter implements AttributeConverter<String, String> {
#Override
public String convertToDatabaseColumn(String attribute) {
/* perform encryption here */
}
#Override
public String convertToEntityAttribute(String dbData) {
/* perform decryption here */
}
}
If you are not able to leverage JPA 2.1, an EntityListener or the use of #PrePersist, #PreUpdate, and #PostLoad may be used in order to perform similar logic for encrypting and decrypting the database value.
Just be sure that if you decide to use an EntityListener or any of the Pre/Post callback method annotations, store the decrypted result in a transient field and use that field as your business layer's usage, such as follows:
public class CreditCard {
// this field could have package private get/set methods
#Column(name = "card_number", length = 25, nullable = false)
private String encrpytedCardNumber;
// this is the public operated upon field
#Transient
private String cardNumber;
#PostLoad
public void decryptCardNumber() {
// decrypts card number during DATABASE READ
this.cardNumber = EncryptionUtils.decrypt(encryptedCardNumber);
}
#PrePersist
#PreUpdate
public void encryptCardNumber() {
// encrypts card number during INSERT/UPDATE
this.encryptedCardNumber = EncryptionUtils.encrypt(cardNumber);
}
}
Doing the above keeps entity state consistent in the object as to what exists in your database, without having Hibernate believing that the entity has changed immediately upon loading the database data.
You could do this in several ways.
Using JPA Listeners
Following is a simple example. Please change accordingly.
public class CustomListener{
#Inject
private EncryptorBean encryptor;
#PostLoad
#PostUpdate
public void decrypt(Object pc) {
if (!(pc instanceof)) {
return;
}
MyObj obj = (MyObj) pc;
if (obj.getCardNo() != null) {
obj.setCardNo(
encryptor.decryptString(user.getEncryptedCardNo);
}
}
#PrePersist
#PreUpdate
public void encrypt(Object pc) {
if (!(pc instanceof MyObj)) {
return;
}
MyObj obj = (MyObj ) pc;
if (obj.getCardNo() != null) {
user.setEncryptedCardNo(
encryptor.encryptString(user.getCardNo());
}
}
}
With this approach, you might have to take some precaution to avoid encrypting a already encrypted cardNo value. An additional Transient property could be used to hold the state whether the cardNo is already encrypted or not.
Or Simply Implementing this feature in the getters and setters of the entity property.
public String getCardNo(){
return EncrypUtil.decrypt(this.cardNo);
}
public void setCardNo(String cardNo){
this.cardNo = EncrypUtil.encrypt(cardNo);
}
You could also use JPA vendor specific interceptors. i.e HibernateInterceptors
public class CustomInterceptor extends EmptyInterceptor{
public boolean onSave(Object entity,Serializable id,
Object[] state,String[] propertyNames,Type[] types)
throws CallbackException {
if (entity instanceof MyObj){
// check if already encrypted or not.
//(A transient property could be useful)
entity.setCardNo(EncrypUtils.encrypt(entity.getCardNo()));
}
You could also use #Convert annotation and specify a converter
#Convert(converter = CCConverter.class)
private String creditCardNumber;
CCConverter class should be an implementation of AttributeConverter
Hope this helps.

Generate Getters and setters without underscore (not prefix/suffix) + eclipse

My variable is
private String category_code = null;
My getter and setter is generated as
public String getCategory_code() {
return category_code;
}
public void setCategory_code(String category_code) {
this.category_code = category_code;
}
Is it possible to generate
public String getCategorycode() {
return category_code;
}
public void setCategorycode(String categorycode) {
this.category_code = category_code;
}
I checked Properties-->Code Style-->Fields but that is only for prefix and suffix.
Or should I just rename my variables as m_categoryCode? and get my output as follows?
public String getCategoryCode() {
return m_categoryCode;
}
public void setCategoryCode(String categoryCode) {
m_categoryCode = categoryCode;
}
Which is better?
The names of the getter and setter methods are derived from the field name. If you use a prefix or suffix for fields (e.g. fValue, _value, val_m), you can specify the suffixes and prefixes in the Code Style preference page (Windows > Preferences > Java > Code Style).
reference at here
Java code tends to follow the camelCaseStyle, not the c_underscore_style. Following the existing standards will generally help you in a variety of ways (you will be able to better read others' code and others will be able to better read your code, where "others" are other developers in the same language). also, the tooling for the language tends to work better (case in point).

Tapestry5 Value Encoder with Hibernate Composite Key

I'm trying to get my tapestry value encoder to work with a hibernate composite key. I have the following code and I'm trying to get the composite id and pass it to the interface where it could later be sent back to the server for decoding back to an object.
#Embeddable
public class IfasvVendorPK implements Serializable{
#Column(length = 4, nullable = false)
protected String peId;
#Column(length = 8, nullable = false)
protected String peAddrCd;
public IfasvVendorPK() {
}
public IfasvVendorPK(String peId, String peAddrCd) {
this.peId = peId;
this.peAddrCd = peAddrCd;
}
// equals, hashCode
}
#Entity
public class IfasvVendor implements Serializable {
#EmbeddedId
private IfasvVendorPK ifasvVendorPK;
//...
}
The following is my value encoder. The toClient is where would I need to send the composite key to the interface. I'm not sure how to get the composite key.
#SuppressWarnings("unchecked")
public LabelAwareValueEncoder getEncoderVendor() {
return new LabelAwareValueEncoder<IfasvVendor>() {
public String toClient(IfasvVendor value) {
return value.getIfasvVendorPK().toString();
}
public IfasvVendor toValue(String clientValue) {
if (clientValue.isEmpty()) {
return null;
}
return (IfasvVendor) session.get(IfasvVendor.class, clientValue);
}
public String getLabel(IfasvVendor value) {
return value.getPeNameU();
}
};
}
If someone could help me to better understand how to work with the composite key so I could get my value encoder working, it would be greatly appreciated. Thanks in advance.
Hibernate has no way to know how what this string means and cannot convert it back.
I suggest adding a non-composite ID or concaternate the values which you then split again in your toValue method.
If you keep the ValueEncoder longer than your request (ex. with #Persist) you could put a HashMap in it to easily get the object back for a concaternated client key;
Since your Composite key is serializeable you could serialize it in toClient and deserialize it in toValue. However, I really wouldn't do that, serializing stuff and sending it to a browser and back is a big, evil security hole.
I think the problem is with your line:
return (IfasvVendor) session.get(IfasvVendor.class, clientValue);
At this point, your "clientValue" is a String, as generated by IfasvVendorPK.toString()
I'm not sure this should work in hibernate, shouldn't you be passing an instance of IfasvVendorPK to session.get?
You can test whether this should work with the following:
public String toClient(IfasvVendor value) {
// test toValue strategy (probably breaks):
System.out.println(
session.get(IfasvVendor.class, value.getIfasvVendorPK().toString()));
// test toValue strategy (probably works):
System.out.println(
session.get(IfasvVendor.class, value.getIfasvVendorPK()));
return value.getIfasvVendorPK().toString();
}
So if I'm right, what you need to do in toValue is convert the String back to an IfasvVendorPK before sending it to session.get.

JPA: Map invalid database values to enums

In my datamodel a have many entities where attributes are mapped to enumerations like this:
#Enumerated(EnumType.STRING)
private MySpecialEnum enumValue;
MySpecialEnum defines some fixed values. The mapping works fine and if the database holds a NULL-value for a column I get NULL in the enumValue-attribute too.
The problem is, that my backend module (where I have no influence on) uses spaces in CHAR-columns to identify that no value is set. So I get an IllegalArgumentException instead of a NULL-value.
So my question is: Is there a JPA-Event where I can change the value read from the database before mapping to the enum-attribute?
For the write-access there is the #PrePersist where I can change Null-values to spaces. I know there is the #PostLoad-event, but this is handled after mapping.
Btw: I am using OpenJpa shipped within WebSphere Application Server.
You could map the enum-type field as #Transient (it will not be persisted) and map another field directly as String, synchronizing them in #PostLoad:
#Transient
private MyEnum fieldProxy;
private String fieldDB;
#PostLoad
public void postLoad() {
if (" ".equals(fieldDB))
fieldProxy = null;
else
fieldProxy = MyEnum.valueOf(fieldDB);
}
Use get/setFieldProxy() in your Java code.
As for synchronizing the other way, I'd do it in a setter, not in a #PreUpdate, as changes to #Transient fields probably do not mark the entity as modified and the update operation might not be triggered (I'm not sure of this):
public void setFieldProxy(MyEnum value) {
fieldProxy = value;
if (fieldProxy == null)
fieldDB = " ";
else
fieldDB = value.name();
}
OpenJPA offers #Externalizer and #Factory to handle "special" database values.
See this: http://ci.apache.org/projects/openjpa/2.0.x/manual/manual.html#ref_guide_pc_extern_values
You might end up with something like this: not tested...
#Factory("MyClass.mySpecialEnumFactory")
private MySpecialEnum special;
...
public static MySpecialEnum mySpecialEnumFactory(String external) {
if(StringUtils.isBlank(external) return null; // or why not MySpecialEnum.NONE;
return MySpecialEnum.valueOf(external);
}

Hibernate Enum mapping using annotaions

I have an existing database that I am now connecting to using hibernate. I cannot change the data in it at the moment and have everything working apart from a single column.
I have a status column that has the values:
new
mailed
in
out
And the column is mapped as follows:
#Column(name = "STATUS", nullable = false, length = 50)
#Enumerated(EnumType.STRING)
private TeamMemberStatus status;
I would REALLY like (for application reasons) to have this column mapped as a Java Enum (TeamMemberStatus), but due to the fact that 'new' is a keyword in Java I cannot have that as an enum member.
If I have the enum contstants NEW, MAILED, IN and OUT hibernate fails as inside EnumType it does a Enum.valueOf().
Is there any way for me to map this to my Enum without having to write a complex UserType?
-- added content
My Enum like this:
public enum TeamMemberStatus {
NEW, MAILED, IN, OUT
}
is a valid Java enum, but not matching the case of the database. If I change it to match the database like:
public enum TeamMemberStatus {
new, mailed, in, out
}
It won't compile as 'new' is a Java reserved word.
If you can use a SQL UPPER statement at database, It will work without using any UserType
UPDATE
Well, It can not be The nicest solution but it solves what you want
#Entity
public class WrapperEntity {
private TeamMemberStatus memberStatus;
#Transient
private TeamMemberStatus getMemberStatus() {
return this.memberStatus;
}
public void setMemberStatus(TeamMemberStatus memberStatus) {
this.memberStatus = memberStatus;
}
#Column(name="STATUS", nullable=false, length=50)
public String getMemberStatusAsString() {
return memberStatus.name().toLowerCase();
}
public void setMemberStatusAsString(String memberStatus) {
this.setsetMemberStatus(TeamMemberStatus.valueOf(memberStatus.toUpperCase()));
}
}
If your Database values are "new", "mailed", "in" and "out" then your Enum need exactly the same names. - I believe that the problem is, that your Enums are in capital letters but your data base values not.

Categories