Representing differences between database representation and object representation in JPA - java

I am working with a legacy database that makes frequent use of blobs of JSON stored as text. I am trying to write some JPA classes that will hide this implementation detail, make it easier to work with the database, and allow us to refactor the database schema in the future. My current approach is:
class MyTableObject{
#Lob
#Column(name = "stuff")
private String jsonString;
public List<Stuff> getStuff(){
return jsonToStuff(jsonString);
}
public setStuff(List<Stuff> stuff){
jsonString = stuffToJsonString(stuff);
}
}
Here, the representation is always as a JSON string in both the the database and the Object and although it works (so far) , it is pretty inefficient as the JSON has to be parsed every time the state of the object is modified. I appreciate that I could improve performance of my current solution by cacheing the Stuff objects in memory, but I would still have to serialize them to JSON every time the setter is called and ensure that the 2 representations of the state (Objects and JSON) were always in sync. What I would like to be able to do is to tell the framework to convert my field on the way in and out, something like:
class MyTableObject{
private List<Stuff> stuff;
#Lob
#Column(name = "stuff")
public String getStuffAsJsonString(){
return stuffToJsonString(stuff);
}
#Column(name = "stuff")
public setStuffFromJsonString(String jsonString){
stuff = stuffFromJsonString(jsonString);
}
}
However, as far as I know annotations are only valid on the getter. Can I achieve something like the above - with one representation in the Object and a different representation in the database? I am new to JPA so I could easily be missing something obvious.
Many thanks in advance

You can just annotate the getter. The corresponding setter will be used by JPA when the object is loaded from the database.
I would use a protected property that is only for JPA and a public accessor for the clients.
The public accessor converts the json string to the List of stuffs when it needs it.
In the same way the property accessor methods for JPA converts the list of stuff to the json string if it is needed.
class MyTableObject {
private String jsonStuff;
private List<Stuff> stuff;
#Lob
#Column(name = "stuff")
protected String getJsonStuff(){
if(jsonStuff == null){
jsonStuff = stuffToJsonString(stuff);
}
return jsonStuff;
}
protected void setJsonStuff(String jsonString){
if(jsonString != null && jsonString.equals(this.jsonStuff)){
// the new string is equal to the old one. No need to re-convert.
return;
}
this.jsonStuff = jsonString;
// jsonStuff changed we have to re-convert json to list
// thus we set stuff to null
stuff = null;
}
public List<Stuff> getStuff(){
if(stuff == null){
stuff = stuffFromJsonString(jsonStuff);
}
return Collections.unmodifiableList(stuff);
}
public void setStuff(List<String> stuff){
if(suffListNotChanged(stuff)){
// the new stuff list is equal to the old one. No need to rebuild json.
return;
}
this.stuff = new ArrayList<String>(stuff);
// the stuff list changed
// thus the jsonStuff string must be rebuild
this.jsonStuff = null;
}
private boolean suffListNotChanged(List<Stuff> stuffList){
...
}
private String stuffToJsonString(List<Stuff> stuffList){
if(stuffList == null){
....
}
}
private List<Stuff> stuffFromJsonString(String stuff){
if(stuff == null){
....
}
}
}

It's not exactly that the annotations are only valid on the getter. A single annotation takes care of the getter and the setter: you don't doubly-annotate.
So, you've basically figured it out already. Remove the extra #Column(name = "stuff") on the setter and you're off to the races.
class MyTableObject {
private List<Stuff> stuff;
#Lob
#Column(name = "stuff")
public String getStuffAsJsonString(){
return stuffToJsonString(stuff);
}
public setStuffFromJsonString(String jsonString){
stuff = stuffFromJsonString(jsonString);
}
}

You don't even to worry for which of the getter/setter should be annotated. The below code should be all set:
class MyTableObject{
#Lob
#Column(name = "stuff")
private List<Stuff> stuffAsJsonString;
public String getStuffAsJsonString(){
return stuffToJsonString(stuff);
}
public setStuffFromJsonString(String jsonString){
stuff = stuffFromJsonString(jsonString);
}
}

Related

Java nested POJO update based on dot annotation

I have a nested POJO structure defined something like this,
public class Employee {
private String id;
private Personal personal;
private Official official;
}
public class Personal {
private String fName;
private String lName;
private String address;
}
public class Official {
private boolean active;
private Salary salary;
}
public class Salary {
private double hourly;
private double monthly;
private double yearly;
}
I get updates from a service with dot annotaion on what value changed, for ex,
id change --> id=100
address change --> personal.address=123 Main Street
hourly salary change --> official.salary.hourly=100
This POJO structure could be 3-4 level deeps. I need to look for this incoming change value and update the corresponding value in POJO. What's the best way of doing it?
If you would like to create Java objects that allows you to edit fields. You can specify your object fields with the public/default/protected access modifiers. This will enable you to get and set fields such as personal.address or official.salary.hours
This approach is typically frowned upon as the object is no longer encapsulated and any calling methods are welcome to manipulate the object. If these fields are not encapsulated with getters and setters, your object is no longer a POJO.
public provides access from any anywhere.
default provides access from any package
protected provides access from package or subclass.
public class Employee {
public String id;
public Personal personal;
public Official official;
}
public class Personal {
public String fName;
public String lName;
public String address;
}
Here's a quick approach using reflection to set fields dynamically. It surely isn't and can't be clean. If I were you, I would use a scripting engine for that (assuming it's safe to do so).
private static void setValueAt(Object target, String path, String value)
throws Exception {
String[] fields = path.split("\\.");
if (fields.length > 1) {
setValueAt(readField(target, fields[0]),
path.substring(path.indexOf('.') + 1), value);
return;
}
Field f = target.getClass()
.getDeclaredField(path);
f.setAccessible(true);
f.set(target, parse(value, f.getType())); // cast or convert value first
}
//Example code for converting strings to primitives
private static Object parse(String value, Class<?> type) {
if (String.class.equals(type)) {
return value;
} else if (double.class.equals(type) || Double.class.equals(type)) {
return Long.parseLong(value);
} else if (boolean.class.equals(type) || Boolean.class.equals(type)) {
return Boolean.valueOf(value);
}
return value;// ?
}
private static Object readField(Object from, String field) throws Exception {
Field f = from.getClass()
.getDeclaredField(field);
f.setAccessible(true);
return f.get(from);
}
Just be aware that there's a lot to improve in this code (exception handling, null checks, etc.), although it seems to achieve what you're looking for (split your input on = to call setValueAt()):
Employee e = new Employee();
e.setOfficial(new Official());
e.setPersonal(new Personal());
e.getOfficial().setSalary(new Salary());
ObjectMapper mapper = new ObjectMapper();
setValueAt(e, "id", "123");
// {"id":"123","personal":{},"official":{"active":false,"salary":{"hourly":0.0,"monthly":0.0,"yearly":0.0}}}
setValueAt(e, "personal.address", "123 Main Street");
// {"id":"123","personal":{"address":"123 Main Street"},"official":{"active":false,"salary":{"hourly":0.0,"monthly":0.0,"yearly":0.0}}}
setValueAt(e, "official.salary.hourly", "100");
// {"id":"123","personal":{"address":"123 Main Street"},"official":{"active":false,"salary":{"hourly":100.0,"monthly":0.0,"yearly":0.0}}}

Add object to a set using multiple fields as equality comparison to overrride existing equals and hashCode methods

I have a POJO class that already has equals and hashcode defined and used for many legacy objects that are saved to a DB so changing how that object works is not an option.
Her's a simplified code:
#EqualsAndHashCode(of = {"id"}, callSuper = false)
public class BenefitContract {
private static final Logger LOGGER = LoggerFactory.getLogger(BenefitContract.class);
#Id
#GeneratedValue(generator = "ss_benefit_contract_sequence", strategy = GenerationType.SEQUENCE)
#Column(name = "SS_BENEFIT_CONTRACT_ID")
private Long id;
private UUID guid;
private Benefit benefit;
private LocalDate startDate;
private BigDecimal contractCost;
...
private static Set<BenefitContract> uniqueContracts;
...
}
So, I want to add benefit contracts to the uniqueContracts based on employee, startDate and contractCost so as to eliminate duplicate contracts for the same employee based on these fields.
How that could be done given I can't re-define equals and hashcode?
I ended up implementing an ugly old-style "Comparator" method as follows. That checks if an element duplicates any other already in the set and then adds the element for real:
private boolean addToSet(BenefitContract contract) {
for (BenefitContract bc : uniqueContracts) {
if (isEqual(contract, bc)) {
return false;
}
}
uniqueContracts.add(contract);
return true;
}
private boolean isEqual(BenefitContract t, BenefitContract t1) {
if (t == t1) {
return true;
}
if (Objects.equals(t.getGuid(), t1.getGuid())) {
return true;
}
if (Objects.equals(t.getBenefit(), t1.getBenefit()) &&
Objects.equals(t.getContractCost(), t1.getContractCost()) &&
Objects.equals(t.getStartDate(), t1.getStartDate())) {
return true;
}
return false;
}
//A method that actually does some processing
private process(BenefitContract contract){
...
addToSet(bc);
}
...
}
Surely it can be prettyfied using Java 8 etc, but that'll come later

Update immutable object without breaking immutability

how can I get updated immutable object from another immutable object without breaking the immutability in a good way. Something similar to how things are achieved in Scala or how they are achieved in Immutables library.
How can I achieve something like
Customer.Builder.from(anotherCustomer).replaceOrder(oldOrder,newOrder).with(newName).build()
To reuse the old objects and queue all the changes I want in order to construct a new one.
I am using Java.
public class Customer {
private String name;
private Order order
private Customer(CustomerBuilder builder) {
this.name = builder.name;
this.order = builder.order;
}
public Customer replace(Order order) {
return new CustomerBuilder().name(this.name).order(order).build();
}
public static class CustomerBuilder {
private String name;
private Order order
public CustomerBuilder name(String name) {
this.name = name;
return this;
}
public CustomerBuilder order(Order order) {
this.order = order;
return this;
}
public Customer build() {
return new Customer(this);
}
}
}
I recommend this book who cover the subject :
https://www.amazon.ca/Effective-Java-Edition-Joshua-Bloch/dp/0321356683
Best way I know to update immutable object is to return modified copy of the object, like java String
String uppercase = "hello".toUpperCase()
I make easy to use fluent typing
String uppercasewithoutletterL = "hello".toUpperCase().replace("L","")
Here's a little recursive function that creates a changed immutable. It re-creates every nested object that includes the change.
const changeImmutable = (immutable: any, path: string[], newVal: any): any => {
if(path.length) {
let copy = Array.isArray(immutable) ? [...immutable] : {...immutable};
copy[path[0]] = changeImmutable(copy[path[0]], path.slice(1), newVal);
return copy;
}
return newVal;
};
example of usage
let immutableObj = {a: {b: [{c: 'foo'}]}};
// change 'foo' to 'bar'
let newImmutableObj = changeImmutable(immutableObj, ['a', 'b', 0, 'c'], 'bar');
You can implement Cloneable interface in object type.
Another option is to have builder for your immutable objects which can create new instance from existing one. Something like those fellas done...

Duplicate and add fields between classes

I was wondering if the following scenario is possible.
Having two classes (Source and Destination) where in code I could do this:
public class Source{
private String fieldA;
private String fieldB;
public Source(){ ... }
}
...
public class Destination{
public Destination(Source src){ ... }
}
Source src = new Source();
Destination dest = new Destination(src);
dest.fieldA = "test";
dest.fieldB = "test";
So what I mean here is that I have two classes, one called Source that contains (private) fields and one called Destination with no fields. After creating two objects of these classes and passing in Source into the constructor of Destination, I want to be able to duplicate/copy the fields of Source into Destination.
Could something like this be possible in Java, whether or not using Reflection? And if possible, can someone give me a minor example I can start with.
A hackish version to accomplish this is to add all fields to a Map. The fields can be copied from the source object to the destination object and the field name can be the key. Something along the lines of this:
public class FieldAccessor {
public static class Destination {
private final Map<String, Object> fields = new HashMap<>();
public Destination(Object o) {
final Set<Field> accessibleFields = Arrays.stream(o.getClass().getDeclaredFields())
.map(field -> {
field.setAccessible(true);
return field;
})
.collect(Collectors.toSet());
accessibleFields.forEach(field -> {
try {
fields.put(field.getName(), field.get(o));
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unable to access field", e);
}
});
}
public Set<String> fieldNames() {
return fields.keySet();
}
public Optional<Object> fieldValue(String fieldName) {
return Optional.ofNullable(fields.get(fieldName));
}
}
public static class Source {
private final String fieldA;
private final Integer fieldB;
private final int fieldC;
public Source(String fieldA, Integer fieldB, int fieldC) {
this.fieldA = fieldA;
this.fieldB = fieldB;
this.fieldC = fieldC;
}
public String getFieldA() {
return fieldA;
}
public Integer getFieldB() {
return fieldB;
}
public int getFieldC() {
return fieldC;
}
}
#Test
public void testFields() {
Destination destination = new Destination(new Source("Abc", 123, 456));
destination.fieldNames().stream().forEach(fieldName -> {
System.out.println("Fieldname: " + fieldName + ", value: " + destination.fieldValue(fieldName).get());
});
}
}
For more info, check out this SO.
However, this is not something I would use in real production code. Instead, I would use some sort of serialization by e.g. using Jackson.
So you want to dynamically create fields in an object ? That's not directly possible in Java. If you just wanted to copy methods of an interface, the answer would have be to use a JDK proxy. It may still be of interest if :
you accept to only use getters and setters in Destination class
Source class implements an interface defining the setters and getters you want to copy
If you cannot accept those restrictions, you will have to look to CGLIB proxies or Javassist, that is libraries that dynamically modify the bytecode of the compiled class objects at load time. It is a really advanced feature, that is mainly used in frameworks or other libraries and not in high-level programs. Typically it is used in Object Relational Mappers like Hibernate to replace simple collection classes with enhanced ones that transparently gets (an save) their elements in database.
In any other case, trying to access private fields outside of the class should be seen as an indicator for a possible design flaw. private means implementation dependant and can change across versions and should not be used without knowing why.
The simplest and most efficient way to do it is copying the fields explicitly :
public Destination(Source src)
{
this.fieldA = src.getFieldA();
this.fieldB = src.getFieldB();
}
I don't see the point in using reflection for this purpose.
The only thing is in my mind for this at this time is extending Destination class from Source
public class Source{
private String fieldA;
private String fieldB;
//You need to have both Getter and Setter for fieldA and fieldB
public Source(){ ... }
}
...
public class Destination extends Source{
public Destination(){...}
}
Source src = new Destination();
dest.setFieldA("test");
dest.setFieldB("test");
Private members of Source cannot be accessed from Destination object even if you are passing a Source object to Destination.
You need to add string fieldA, fieldB to Destination to
string fieldA, fieldB;
public Destination(Source src)
{
fieldA = src.fieldA;
fieldB = src.fieldB;
}

How to make Morphia set the value of a field as the value returned by a function call?

I'm implementing the "auto-increment" id using strategy described here:
http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/
Basically the value of the seqId field is set by calling an utility function that updates the counter on an auxiliary collection and returns the incremented value. Sounds great.
My issue is in mapping this to be used with Morphia. The tutorial suggests performing the insert (such as in the shell) like so:
db.users.insert(
{
seqId: getNextSequence("userid"),
name: "Sarah C."
}
I'm basically looking to do something like setting the POJO seqId field to something that Morphia will translate into an insert like the one above when I invoke save().
My POJO looks like this:
#Entity
public class User {
#Id
private Long id;
// THIS IS THE FIELD I WANT TO AUTO-INCREMENT
private Long seqId;
private String name;
...
}
The question is: How to make Morphia set the value of a field as the value returned by a function call?
I looked into using the #PrePresist annotation to perform this function call and getting the value, then setting it in the +_id field. That has several drawbacks such as making multiple calls to MongoDB instead of just one, and also the fact that my model objects don't have a reference to the datastore and I'd rather not mix up the concerns.
Is this possible? Any suggestions?
I'm on MongoDB 2.6.6 using the latest Java drivers.
Thanks!
PS: I'm aware that auto-increment is not recommended in large environments. I need it anyways for this specific scenario.
I'll describe the solution that's working for us quite well. Note that this supports auto increments on the class level and a subset of it — so you can count users or admin-users (user with an admin enum or whatever).
This contains the current value for each auto increment field, it's basically a reference:
#Entity(noClassnameStored = true)
public class AutoIncrementEntity {
#Id
protected String key;
protected Long value = 1L;
protected AutoIncrementEntity() {
super();
}
/**
* Set the key name — class or class with some other attribute(s).
*/
public AutoIncrementEntity(final String key) {
this.key = key;
}
/**
* Set the key name and initialize the value so it won't start at 1.
*/
public AutoIncrementEntity(final String key, final Long startValue) {
this(key);
value = startValue;
}
public Long getValue() {
return value;
}
}
In your persistence service, you could use the following to set / create the auto increment automatically:
public <E extends BaseEntity> ObjectId persist(E entity) {
// If it's a user and doesn't yet have an ID, set one; start counting from 1000.
if ((entity instanceof UserEntity) && (((UserEntity) entity).getUserId() == null)) {
((UserEntity) entity).setUserId(
generateAutoIncrement(entity.getClass().getName(), 1000L));
}
// Additionally, set an ID within each user group; start counting from 1.
if ((entity instanceof UserEntity) && (((UserEntity) entity).getRoleId() == null)) {
((UserEntity) entity).setRoleId(
generateAutoIncrement(entity.getClass().getName() + "-" + entity.getRole(), 1L));
}
mongoDataStore.save(entity);
return entity.getId();
}
/**
* Return a unique numeric value for the given key.
* The minimum value, set to 1 if nothing specific is required.
*/
protected long generateAutoIncrement(final String key, final long minimumValue){
// Get the given key from the auto increment entity and try to increment it.
final Query<AutoIncrementEntity> query = mongoDataStore.find(
AutoIncrementEntity.class).field("_id").equal(key);
final UpdateOperations<AutoIncrementEntity> update = mongoDataStore
.createUpdateOperations(AutoIncrementEntity.class).inc("value");
AutoIncrementEntity autoIncrement = mongoDataStore.findAndModify(query, update);
// If none is found, we need to create one for the given key.
if (autoIncrement == null) {
autoIncrement = new AutoIncrementEntity(key, minimumValue);
mongoDataStore.save(autoIncrement);
}
return autoIncrement.getValue();
}
And finally your entity:
#Entity(value = "user", noClassnameStored = true)
public class UserEntity extends BaseEntity {
public static enum Role {
ADMIN, USER,
}
private Role role;
#Indexed(unique = true)
private Long userId;
private Long roleId;
// Role setter and getter
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long roleId) {
this.roleId = roleId;
}
}
There's nothing specific going on in the entity. All the logic is handled by the persistence service. I'm not using the #PrePersist, because you'd then need to put the persistence service into the entity, which doesn't sound like a good idea.

Categories

Resources