JPA/Hibernate with Dynamic Object Model - java

I have a table with the number of column is variable (depend of customer),
so I looking for a way to mapping this table with a java object using JPA/Hibernate or other
I can not use POJO because it's limited to a stable table so I'd like to use java object like this
class MyObject {
int id;
Map<String, Object> fields = new Map<String, Object>();
public void setId(int id) {
this.id = id;
}
public void setField(String key, Object value) {
fields.put(key, value);
}
}
The table for storing MyObject :
TABLE MYTABLE (
ID INTEGER,
FIELD1 VARCHAR,
FIELD2 DATE,
FIELD3 INTEGER
)
MyObject myObject = new MyObject();
myObject.setId(id);
myObject.setField("FIELD1" , "hello world");
myObject.setField("FIELD2" , new Date());
myObject.setField("FIELD3" , 21)
Action to save myObject in db;
And of course the possibility to query

Hibernate supports what they refer to as Dynamic Models. See further the following link which notes:
Persistent entities do not necessarily have to be represented as
POJO/JavaBean classes. Hibernate also supports dynamic models (using
Maps of Maps at runtime). With this approach, you do not write
persistent classes, only mapping files.
http://docs.jboss.org/hibernate/orm/5.1/userguide/html_single/Hibernate_User_Guide.html#dynamic-model
Previous versions of the Hibernate docs give some more detail. See:
http://docs.jboss.org/hibernate/orm/4.3/manual/en-US/html_single/#persistent-classes-dynamicmodels
There's an example here that seems (from a quick skim) to allow for combining normal POJO with dynamic fields.
https://www.infoq.com/articles/hibernate-custom-fields

Related

How to convert a JPA OneToMany relationship to DTO

I have a class Plan in which there is a list of Activity. The Activity class has a reference to a single Plan. Hence there is a OneToMany relationship like this:
#Entity
public class Plan {
#OneToMany(mappedBy = "Plan")
private List<Activity> activities;
}
#Entity
public class Activity {
#ManyToOne
#JoinColumn(name= "PLAN_ID")
private Plan plan;
}
I need to convert them to DTOs to be sent to presentation layer. So I have an assembler class to simply convert domain objects to POJO.
public class PlanAssembler {
public static PlanDTO makeDTO(Plan p) {
PlanDTO result = new PlanDTO();
result.setProperty(p.getProperty);
...
for (Activity a: p.getActivity()) {
// Here I need to iterate over each activity to convert it to DTO
// But in ActivityAssembler, I also need PlanDTO
}
As you can see, in PlanAssembler, I need to iterate over all activities and convert them to ActivityDTO but the trouble is, in ActivityAssembler I also need the PlanDTO to construct the ActivityDTO. It's gonna be an infinite loop. How can I sort this out?
Please help.
Table relationships
Let's assume we have the following post and post_comment tables, which form a one-to-many relationship via the post_id Foreign Key column in the post_comment table.
Fetching a one-to-many DTO projection with JPA and Hibernate
Considering we have a use case that only requires fetching the id and title columns from the post table, as well as the id and review columns from the post_comment tables, we could use the following JPQL query to fetch the required projection:
select p.id as p_id,
p.title as p_title,
pc.id as pc_id,
pc.review as pc_review
from PostComment pc
join pc.post p
order by pc.id
When running the projection query above, we get the following results:
| p.id | p.title | pc.id | pc.review |
|------|-----------------------------------|-------|---------------------------------------|
| 1 | High-Performance Java Persistence | 1 | Best book on JPA and Hibernate! |
| 1 | High-Performance Java Persistence | 2 | A must-read for every Java developer! |
| 2 | Hypersistence Optimizer | 3 | It's like pair programming with Vlad! |
However, we don't want to use a tabular-based ResultSet or the default List<Object[]>JPA or Hibernate query projection. We want to transform the aforementioned query result set to a List of PostDTO objects, each such object having a comments collection containing all the associated PostCommentDTO objects:
We can use a Hibernate ResultTransformer, as illustrated by the following example:
List<PostDTO> postDTOs = entityManager.createQuery("""
select p.id as p_id,
p.title as p_title,
pc.id as pc_id,
pc.review as pc_review
from PostComment pc
join pc.post p
order by pc.id
""")
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(new PostDTOResultTransformer())
.getResultList();
assertEquals(2, postDTOs.size());
assertEquals(2, postDTOs.get(0).getComments().size());
assertEquals(1, postDTOs.get(1).getComments().size());
The PostDTOResultTransformer is going to define the mapping between the Object[] projection and the PostDTO object containing the PostCommentDTO child DTO objects:
public class PostDTOResultTransformer
implements ResultTransformer {
private Map<Long, PostDTO> postDTOMap = new LinkedHashMap<>();
#Override
public Object transformTuple(
Object[] tuple,
String[] aliases) {
Map<String, Integer> aliasToIndexMap = aliasToIndexMap(aliases);
Long postId = longValue(tuple[aliasToIndexMap.get(PostDTO.ID_ALIAS)]);
PostDTO postDTO = postDTOMap.computeIfAbsent(
postId,
id -> new PostDTO(tuple, aliasToIndexMap)
);
postDTO.getComments().add(
new PostCommentDTO(tuple, aliasToIndexMap)
);
return postDTO;
}
#Override
public List transformList(List collection) {
return new ArrayList<>(postDTOMap.values());
}
}
The aliasToIndexMap is just a small utility that allows us to build a Map structure that associates the column aliases and the index where the column value is located in the Object[] tuple array:
public Map<String, Integer> aliasToIndexMap(
String[] aliases) {
Map<String, Integer> aliasToIndexMap = new LinkedHashMap<>();
for (int i = 0; i < aliases.length; i++) {
aliasToIndexMap.put(aliases[i], i);
}
return aliasToIndexMap;
}
The postDTOMap is where we are going to store all PostDTO entities that, in the end, will be returned by the query execution. The reason we are using the postDTOMap is that the parent rows are duplicated in the SQL query result set for each child record.
The computeIfAbsent method allows us to create a PostDTO object only if there is no existing PostDTO reference already stored in the postDTOMap.
The PostDTO class has a constructor that can set the id and title properties using the dedicated column aliases:
public class PostDTO {
public static final String ID_ALIAS = "p_id";
public static final String TITLE_ALIAS = "p_title";
private Long id;
private String title;
private List<PostCommentDTO> comments = new ArrayList<>();
public PostDTO(
Object[] tuples,
Map<String, Integer> aliasToIndexMap) {
this.id = longValue(tuples[aliasToIndexMap.get(ID_ALIAS)]);
this.title = stringValue(tuples[aliasToIndexMap.get(TITLE_ALIAS)]);
}
//Getters and setters omitted for brevity
}
The PostCommentDTO is built in a similar fashion:
public class PostCommentDTO {
public static final String ID_ALIAS = "pc_id";
public static final String REVIEW_ALIAS = "pc_review";
private Long id;
private String review;
public PostCommentDTO(
Object[] tuples,
Map<String, Integer> aliasToIndexMap) {
this.id = longValue(tuples[aliasToIndexMap.get(ID_ALIAS)]);
this.review = stringValue(tuples[aliasToIndexMap.get(REVIEW_ALIAS)]);
}
//Getters and setters omitted for brevity
}
That's it!
Using the PostDTOResultTransformer, the SQL result set can be transformed into a hierarchical DTO projection, which is much convenient to work with, especially if it needs to be marshalled as a JSON response:
postDTOs = {ArrayList}, size = 2
0 = {PostDTO}
id = 1L
title = "High-Performance Java Persistence"
comments = {ArrayList}, size = 2
0 = {PostCommentDTO}
id = 1L
review = "Best book on JPA and Hibernate!"
1 = {PostCommentDTO}
id = 2L
review = "A must read for every Java developer!"
1 = {PostDTO}
id = 2L
title = "Hypersistence Optimizer"
comments = {ArrayList}, size = 1
0 = {PostCommentDTO}
id = 3L
review = "It's like pair programming with Vlad!"
It won't be an infinite loop because you have to use the PlanDTO object result which you have just created before the loop. See the code below.
Note : Still I suggest to go for a framework which will do this stuff for you.
public class PlanAssembler {
public static PlanDTO makeDTO(Plan p) {
PlanDTO result = new PlanDTO();
result.setProperty(p.getProperty);
...
for (Activity a: p.getActivity()) {
ActivityDTO activityDTO = new ActivityDTO();
// Here I need to iterate over each activity to convert it to DTO
// But in ActivityAssembler, I also need PlanDTO
//Code to convert Activity to ActivityDTO.
activityDTO.setPlan(result);
}
Now if you really want to sort things out on your own:
1) In the mapper class you could define implement mappers resolving this issue by making them unidirectional. With methods like
MapPlanWithActivities(), MapPlan(), MapActivitiesWithPlan() and MapActivities(). this way you could know what data you need and according to what function you use you know when to stop the recursion.
2) The other (much) more complex solution would be to solve the issue by logic and detect the loop. You can for instance define an annotation for that case as Jackson Library does. for that you will have to use some java reflection. See Java Reflection here
3) the easiest way would be to use Dozer as said in my comment:Dozer
This is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
A mapping for your model could look as simple as the following
#EntityView(Plan.class)
interface PlanDTO {
#IdMapping
Long getId();
String getName();
List<ActivityDTO> getActivities();
}
#EntityView(Activity.class)
interface ActivityDTO {
#IdMapping
Long getId();
String getName();
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
PlanDTOdto = entityViewManager.find(entityManager, PlanDTO.class, id);
But the Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/#spring-data-features
The biggest benefit of this is, that this approach will only fetch what you define through the getter definitions in your entity views, whereas other approaches usually fetch too much data and/or require a lot of boilerplate.

JPA : Is there any way to run a simple SELECT statement that only access a few columns?

I'm new to JPA so forgive me if my question seems silly.
We have used JPA in our project. I see that every entity object has a direct mapping with a table and each row in the table is an object of that entity type.
But, suppose I only want to access one or two columns of a table, how do i go about doing it ? The reason I'm asking is because of the task i have in hand.
There are two tables. The first table has everything set up with JPA so that each row can be cast into an object type. The first table has a column that is referenced in the second table i.e. say, table A has column CLOTH_ID and Table B has columns CLOTH_ID and CLOTH_DESCRIPTION. CLOTH_ID is used in both Table A and B; But B has the CLOTH_DESCRIPTION columns which corresponds to CLOTH_ID.
I'm displaying Table A in my webpage but I also need to display : CLOTH_DESCRIPTION in my webpage. Is there a JPA oriented way to do this or Am i better off using regular JDBC to extract the CLOTH DESCRIPTION values ?
I assume you have the following setup:
#Entity
#Table(name="A")
class A {
#ManyToOne
#JoinColumn(name="CLOTH_ID")
private B cloth;
//...
}
#Entity
#Table(name="B")
class B {
#Id
#Column(name="CLOTH_ID")
private int id;
#Column(name="CLOTH_DESCRIPTION")
private String description;
//...
}
If you don't... you're doing it wrong (i.e. it is not idiomatic JPA usage). You have the following options:
Simply fetch A
In this case #ManyToOne relationship will be fetched eagerly by default as well. Then simply call in Java:
a.getCloth().getDescription()
Prefer this approach as it is the simplest and most idiomatic unless the number of columns in B is huge.
Use JPA query with custom columns:
SELECT a, a.b.description
FROM A a
WHERE a.id = :id
In this case the query returns List<Object[]>, where Object[] actually contains two elements: A and String.
Same as above but with custom DTO:
class Adto {
private final A a;
private final String description;
public Adto(A a, String description) {
this.a = a;
this.description = description;
}
}
And slightly modified query:
SELECT new Adto(a, a.b.description)
FROM A a
WHERE a.id = :id

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.

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.

How to map Hashtable component of a class, using JPA?

I have this class:
public class DBRow {
public String url;
public String title;
public static Hashtable<String, Integer> words;
public Vector<String> keywords;
}
What I must do is store many instances of this class in a database. I'm using Hibernate and JPA to get the job done. Using JPA I've come so far (really not far):
#Entity
#Table(name="MAIN_TABLE")
public class DBRow {
#Column(name="url")
public String url;
#Column(name="title")
public String title;
public static Hashtable<String, Integer> words;
public Vector<String> keywords;
}
I want my database to have 3 tables - MAIN_TABLE - auto-icremented id as primary key, url and title; HASH - containing a key-value pair from the Hashtable<String, Integer> and id to refer to which instance of DBRow class it belongs (and also to relate to the MAIN_TABLE); VECTOR - pretty much the same story like HASH but with a Vector<String>. So what I'm asking is hot to map the hashtable and the vector, using JPA to get it done?? I've been trying to find a way to do this but haven't found one so far... or maybe I'm not on the right way! Any suggestions are welcome. Thank you in advance.
This is not possible with JPA 1.0 (at least, not with standard annotations) and since you did not mention your JPA provider, I will only cover JPA 2.0. In JPA 2.0, the #ElementCollection annotation can be used to map a collection of basic types or embeddable objects.
Below some examples taken from EclipseLink/Development/JPA 2.0/new collection mappings:
Example 1 - An element collection representing a basic collection mapping.
#ElementCollection
#Column(name="DESIGNATION")
// CollectionTable will default in this case, Entity name + "_" + attribute name
// JoinColumns will default in this case which are different from BasicCollection collection table default
private Collection<String> designations;
Example 2 - An element collection representing an basic map mapping.
#ElementCollection
#MapKeyColumn(name="Q_DATE")
#Column(name="QUOTE")
#CollectionTable(name="EXPERT_QUOTES", joinColumns=#JoinColumn(name="EBC_ID"))
public Map<Date, String> getQuotes() {
return quotes;
}
Now, I second one of the comment to your question and I would consider using "moderner" data structure like ArrayList and HashMap.
Also note that static (and final) entity fields are always considered to be transient i.e. can't be persisted.

Categories

Resources