Object Oriented Design for generic hibernate object - java

I'm currently working good object oriented principles, and hibernate, I have this POJO, in which the properties would be dynamically populated. This is a design pattern I've read for good Object Oriented Design, in which it would be easy to add attributes to specificic object without breaking the application. My question is, how can you map this to a table, when your attributes is supposedly dynamic, I'm using an enum to limit the key value pairs for the map, but ideally it can still grow. I am only using in-memory database (h2) and I'm not going to be using the code for production use. This is for learning purposes only. see code below:
public class Transaction {
private static Map<Object, Object> properties;
public Transaction(){
if(null != properties)
properties = new LinkedHashMap<Object, Object>();
}
public Transaction(Map<Object, Object> properties){
if(null != properties)
setProperties(properties);
}
public void setProperties(Map<Object, Object> prop){
properties = prop;
}
public void setProperties(Properties property, String value){
properties.put(property, value);
}
public Map<Object, Object> getProperties(){
return properties;
}
public String getProperties(Properties property){
return (String) properties.get(property);
}
}
So I want to be able to create a table that would have this properties, dynamically,
My Enum:
public enum Properties {
Entry("Entry"), Id("Entry_ID"), Name("Name"), Credit("Credit");
private final String description;
private Properties(final String description){
this.description = description;
}
#Override
public String toString(){
return description;
}
}
I have this hibernate mapping, but as you can see this would be need to be updated everytime a field is updated, I need a generic mapping so that when I change/add the attributes, annotation or xml would be okay, see below:
<class name="Transaction" table="TRANSACTION">
<id name="id" column="ENTRY_ID">
<generator class="increment"/>
</id>
<property name="name"/>
<property name="credit" type="boolean" column="IS_CREDIT"/>
</class>

UserDefinedField on Marin Fowler's web site may be a perfect starting point for exploring general answers to this question.
As for Hibernate: It's really designed for statically binding tables to objects and you may have significant problems if you change the schema while running. You can implement the following solutions, though:
Serialized LOB (you serialize your Map into a binary field or - using JSON/XML - a text field). This is a half-and-half approach - half tabular/normal form/SQL and half not-SQL. So, if this approach is attractive, you might want to consider going all-in with a NoSQL database as discussed later
Attribute table, where your customized attributes are stored in a key-value pair table that joins to to the master table. This can be mapped in Hibernate using Indexed Collections (see section 7.2.2.2 Maps) and you would end up with something quite like in your question:
#Entity
public class Transaction {
#Id #GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
// ... snip ...
#OneToMany(mappedBy="transaction")
#MapKey(name="name")
public Map<String, String> getProperties(){
return properties;
}
public void setProperties(Map<String, String> prop){
properties = prop;
}
private Map<String, String> properties; // NB: Type has to be <String, String> because the column name is a String and you have defined the property value to be a String.
public void setProperty(Properties property, String value){
properties.put(property, value);
}
public String getProperty(String name){
return (String) properties.get(property);
}
}
#Entity
public class Property {
#Id #GeneratedValue public Integer getId() { return id; }
public void setId(Integer id) { this.id = id; }
private Integer id;
#ManyToOne
public Transaction getTransaction() { return transaction; }
public void setTransaction(Transaction transaction) { this.transaction = transaction; }
private Transaction transaction;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
private String name;
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
private String description;
}
Pre-defined custom-fields, where you start with a really wide table with loads of unused columns. In this implementation you end up defining a mapping between your arbitrary property names and the pre-defined column names (getString1(), getString10(), etc)
However, a much better solution for you may be to use a NoSQL database - specifically a document-based one. These allow you to store and retrieve arbitrary data-structures (maps and lists). Interestingly, using such an approach makes binding to the data store significantly easier.
MongoDB or Redis (Java bindings at Jedis) are examples.

Related

Mapstruct: Clear Collection on update when using Adders

I try to map my DTO objects to my JPA entities. I have a Collection of children in my ParentEntity. They can be added addChild(). Using the Adder is supported by Mapstruct via the CollectionMappingStrategy (http://mapstruct.org/documentation/dev/reference/html/#collection-mapping-strategies).
This works fine if I create new entities, but fails to clear the children on updating before adding the new children.
The Mapstruct manual says (http://mapstruct.org/documentation/dev/reference/html/#updating-bean-instances):
Collection- or map-typed properties of the target bean to be updated will be cleared and then populated with the values from the corresponding source collection or map.
What am I missing? Is there an additional option I have to set? There is a full example with test case to reproduce the problem at https://github.com/davidfuhr/mapstruct-jpa-child-parent
Here are the classes:
public class ParentEntity {
private String name;
private List<ChildEntity> children = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ChildEntity> getChildren() {
return children;
}
public void addChild(ChildEntity child) {
children.add(child);
child.setMyParent(this);
}
public void removeChild(ChildEntity child) {
children.remove(child);
child.setMyParent(null);
}
}
public class ChildEntity {
private String name;
private ParentEntity myParent;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ParentEntity getMyParent() {
return myParent;
}
public void setMyParent(ParentEntity myParent) {
this.myParent = myParent;
}
}
public class ParentDto {
private String name;
private List<ChildDto> children;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<ChildDto> getChildren() {
return children;
}
public void setChildren(List<ChildDto> children) {
this.children = children;
}
}
public class ChildDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Mapper(collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED)
public interface SourceTargetMapper {
SourceTargetMapper MAPPER = Mappers.getMapper(SourceTargetMapper.class);
ParentEntity toEntity(ParentDto s);
ParentEntity updateEntity(ParentDto s, #MappingTarget ParentEntity e);
#Mapping(target = "myParent", ignore = true)
ChildEntity toEntity(ChildDto s);
}
The text in the documentation need to be rephrased. The problem is that especially for collections, there's no good way to handle this out of the box in MapStruct. I'm currently writing some new text for the documentation.
Consider this (when thinking what MapStruct should do for updating collections in general):
What if there's no match: should the non-matching elements be removed?
Should the non matching source elements be added?
What exactly constitutes to a match: equals? hashcode? comparator==0?
Can there be more than one match (Lists, but also depending on what is considered a match.)
How should the resulting collection be sorted?
Should a newly created object be added to a persistence context?
What about JPA child-parent relations?
About the latter one, Dali (Eclipse) also generates remove methods. So should MapStruct call these in the light of the above?
At this moment it works like this: whenever the user wants a collection update method, MapStruct generates a regular call to element mappings (in stead of an update call), because it is the only sensible thing to do. All the remainder is highly dependent on the use-case. If you need to clear the collection at before hand, use the #BeforeMapping to clear it.
Note: I just fixed an issue that handles also adders in this fashion in stead of the vague error message you get now.
If you want a nice way to handle child/parent relations and integrate them with JPA.. have a look at the examples.

SpringMongo - discover document structure

I have a customer's database that has a collection, in which the document fields can vary between each other. There are some constant fields I can rely on, but as for the rest - I have no way of narrowing the field list as the customer wants the solution to be dynamic.
My question is - can I somehow implement a generic mapping that would return, let's say, a map of document's fields using Spring Data?
edit:
Thanks for the tips. I've tried getting the generic Object (hoping I'd be able to convert it into a map) using the entity:
#Document(collection = "Data")
public class DataEntity {
#Id
private String id;
private Object data;
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
but fetching the object by the ID using MongoRepository produces an object with data field set to null.
I'm using SpringBoot 1.3.1.RELEASE with spring-boot-starter-data-mongodb 1.3.1.RELEASE.
You can use a Map for dynamic properties like below. Is this what you are looking for?
#Document(collection = "computers")
public class Computer {
#Id
private String id;
#Field("name")
private String name;
//Other constant fields
#Field("properties")
private Map<String, Object> properties;
}

JavaFX bind not property member to control

Imagine I have a POJO like:
public class Person()
{
private int id;
private String name;
public int getId()
{
return this.id;
}
public String getName()
{
return this.name;
}
}
If I need to bind name member to JavaFX label using bind() method, I can't apply because I need and observable value.
I know that I can use StringProperty instead String, but I need primitive types because I use Hibernate and I don't know if Hibernate could support properties from JavaFX to map data from DB.
Which alternatives I have to bind from my pojo to a JavaFX control?
You have a couple of options here.
Firstly, it's possible to use FX Properties in JPA/Hibernate entities, though you have to be a little careful. In short, you need to make sure you use property access so that the ORM calls the get/set methods, instead of trying to set the field directly. Steven van Impe discusses this on his blog, and I also blogged on the same topic. One thing I haven't tried here is mapping collections and using ObservableLists: that might be tricky as JPA implementations use a subinterface of List.
Your other option is to make the properties "bound properties" in the Java Bean sense, and then to use a Java Bean Property Adapter:
import java.beans.PropertyChangeSupport ;
public class Person()
{
private int id;
private String name;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.pcs.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
this.pcs.removePropertyChangeListener(listener);
}
public int getId()
{
return this.id;
}
public void setId(int id)
{
int oldId = this.id ;
this.id = id ;
pcs.firePropertyChange("id", oldId, id);
}
public String getName()
{
return this.name;
}
public void setName(String name)
{
String oldName = this.name ;
this.name = name ;
pcs.firePropertyChange("name", oldName, name);
}
}
Then you can do
Label nameLabel = new Label();
Person person = new Person();
nameLabel.textProperty().bind(JavaBeanStringPropertyBuilder.create()
.bean(person)
.name("name") // name of property to bind to
.build());

Hibernate - persist embeddable resource

I'm just starting to learn Java database persistence using Hibernate ORM and have run into a problem which I haven't been able to resolve.
I have these two classes:
#Embeddable
public class Resource {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Resource() {
}
}
#Entity
public class Group {
#Embedded
private Map<String, Resource> resources;
#Id
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Map<String, Resource> getResources() {
return resources;
}
public void setResources(Map<String, Resource> resources) {
this.resources = resources;
}
public Group() {
resources = new HashMap<String, Resource>();
}
}
The Resource shouldn't have its own table because it shouldn't exist outside the Group scope. That's why I used the Embeddable, to be treated as a component.
To sum up I'd like to know how I can store these classes in a database using Hibernate ORM. The Resource class shouldn't be an Entity as it doesn't need its own class.
And I would prefer to use the mapping notations and not XML files.
As it is I get this error:
Syntax error in SQL statement "INSERT INTO GROUP[*] (NAME) VALUES (?) "; expected "identifier"; SQL statement:
It is possible to save all instances of Resource belonging to certain Group to the same database row with Group itself by creating class that wraps inside groups and saving it as Serializable to BLOB field in database.
Because such a solution asks more code and produces confusing data model, it is unlikely that you really want to limit yourself to have only one table.
If, as you said, Resource is fully owned by Group and you want to access them by some string key then #ElementCollection containing instances of embeddable Resource is solution (assuming that your version of Hibernate already have it):
#ElementCollection
private Map<String, Resource> resources;
If you do not have access collection of Resource by name, then following is sufficient
#ElementColection
private Set<Resource> resources;
For more examples about fine tuning your element collection can be found from: Java Persistence/ElementCollection

'dynamic'-like java annotations?

I have a pojo that is dependent on annotations. It has predefined fields as well as a Set that contains user provided fields:
public class MyPOJO implements Document {
private String id;
private LocalString name;
private LocalString desc;
private List<Field> fields;
public MyPOJO(final String id,
final LocalString name,
final LocalString desc,
final List<Field> fields) {
this.id = id;
this.name = name;
this.desc = desc;
this.fields = fields;
}
public String getId() {
return id;
}
#Indexed(searchable = false, stored = true)
public LocalString getName() {
return name;
}
#Indexed(searchable = true)
public LocalString getDescription() {
return desc;
}
public List<Field> getFields() {
return fields;
}
}
MyPOJO is a 'generic' object, ie, the developer (or consumer) of MyPOJO has fields that are not predefined in MyPOJO and therefore the developer needs to place these additional fields the in attribute 'fields'. The problem arises from the fact that each object in the Set fields needs to have its own annotations to indicate whether the particular field is either stored or searchable in order to remain consistent with the predefined attributes, such as name.
I can think of two options:
For each additional field, the developer will have to create an
anonymous class implementing the interface Field and inside this
anonymous class, the developer will declare the applicable
annotations.
the Set 'fields' contains a complex object of fieldname, fieldvalue
and annotations as shown below. I can't figure out how to invoke the constructor for Field. The below code does not compile but it is intended as pseudo-code to signify what I am trying to do.
Field myfield1 = new Field("dateofBirth", new Date(), new ArrayList({Index.stored, Index.searchable});
Field myfield2 = new Field("model", "330i", new ArrayList({Index.stored});
There is no construct to pass annotations as a parameter: new ArrayList({Index.stored}.
public class Field {
private String name;
private Object value;
Collection<Annotation> annotations;
public Field(final String name, final Object value, Collection<Annotation> annotations;) {
this.name = name;
this.value = value;
this.annotations = Collections.unmodifiableCollection(annotations);
}
public String getName() {
return name;
}
public Object getValue() {
return value;
}
}
I'm not particularly excited with either option and hoping someone can give me some pointers
If you need an extensible object model, I'd say a POJO design is just setting yourself up for extra work as opposed to exposing a metamodel.
That said, what you could do is have clients of the API subclass MyPOJO, and annotate the properties they define in their subclasses. You would then use reflection to go through all JavaBeans properties of the objects you're receiving and determine the annotations on the getters - similarly to how JPA works.

Categories

Resources