Map database column1, column2, columnN to a collection of elements - java

In legacy database tables we have numbered columns like C1, C2, C3, C100 or M1, M2, M3, M100.
This columns represent BLOB data.
It is not possible to change anything it this database.
By using JPA Embeddable we map all of the columns to single fields. And then during embedding we override names by using 100 override annotations.
Recently we have switched to Hibernate and I've found things like UserCollectionType and CompositeUserType. But I hadn't found any use cases that are close to mine.
Is it possible to implement some user type by using Hibernate to be able to map a bundle of columns to a collection without additional querying?
Edit:
As you probably noticed the names of columns can differ from table to table. I want to create one type like "LegacyArray" with no need to specify all of the #Columns each time I use this type.
But instead I'd use
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "A"),
#Parameter(name = "size", value = "128")
})
List<Integer> legacyA;
#Type(type = "LegacyArrayUserType",
parameters =
{
#Parameter(name = "prefix", value = "B"),
#Parameter(name = "size", value = "64")
})
List<Integer> legacyB;

I can think of a couple of ways that I would do this.
1. Create views for the collection information that simulates a normalized table structure, and map it to Hibernate as a collection:
Assuming your existing table is called primaryentity, I would create a view that's similar to the following:
-- untested SQL...
create view childentity as
(select primaryentity_id, c1 from primaryentity union
select primaryentity_id, c2 from primaryentity union
select primaryentity_id, c3 from primaryentity union
--...
select primaryentity_id, c100 from primaryentity)
Now from Hibernate's perspective, childentity is just a normalized table that has a foreign key to primarykey. Mapping this should be pretty straight forward, and is covered here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/collections.html
The benefits of this approach:
From Hibernate's point of view, the tables are normalized, it's a fairly simple mapping
No updates to your existing tables
The drawbacks:
Data is read-only, I don't think your view can be defined in an updatable manner (I could be wrong)
Requires change to the database, you may need to create lots of views
Alternately, if your DBA won't even let you add a view to the database, or if you need to perform updates:
2. Use Hibernate's dynamic model mapping facility to map your C1, C2, C3 properties to a Map, and have some code you your DAO layer do the appropriate conversation between the Map and the Collection property:
I have never done this myself, but I believe Hibernate does allow you to map tables to HashMaps. I'm not sure how dynamically Hibernate allows you to do this (i.e., Can you get away with simply specifying the table name, and having Hibernate automatically map all the columns?), but it's another way I can think of doing this.
If going with this approach though, be sure to use the data access object pattern, and ensure that the internal implementation (use of HashMaps) is hidden from the client code. Also be sure to check before writing to the database that the size of your collection does not exceed the number of available columns.
The benefits of this approach:
No change to the database at all
Data is updatable
O/R Mapping is relatively simple
The drawbacks:
Lots of plumbing in the DAO layer to map the appropriate types
Uses experimental Hibernate features that may change in the future

Personally, I think that design sounds like it breaks first normal form for relational databases. What happens if you need C101 or M101? Change your schema again? I think it's very intrusive.
If you add Hibernate to the mix it's even worse. Adding C101 or M101 means having to alter your Java objects, your Hibernate mappings, everything.
If you have 1:m relationships with C and M tables, you'd be able handle the cases I just cited by adding additional rows. Your Java objects contain Collection<C> or Collection<M>. Your Hibernate mappings are one-to-many that don't change.
Maybe the reason that you don't see any Hibernate examples to match your case because it's a design that's not recommended.
If you must, maybe you should look at Hibernate Component Mapping.
UPDATE: The fact that this is legacy is duly noted. My point in bringing up first normal form is as much for others who might find this question in the future as it is for the person who posted the question. I would not want to answer the question in such a way that it silently asserted this design as "good".
Pointing out Hibernate component mapping is pertinent because knowing the name of what you're looking for can be the key when you're searching. Hibernate allows an object model to be finer grained than the relational model it maps. You are free to model a denormalized schema (e.g., Name and Address objects as part of a larger Person object). That's just the name they give such a technique. It might help find other examples as well.

Sorry if I'm misunderstanding your problem here, I don't know much about Hibernate. But couldn't you just concatenate during selection from database to get something like what you want?
Like:
SELECT whatever
, C1||C2||C3||C4||...||C100 AS CDATA
, M1||M2||M3||M4||...||M100 AS MDATA
FROM ...
WHERE ...
(Of course, the concatenation operator differs between RDBMSs.)

[EDIT] I suggest to use a CompositeUserType. Here is an example. There is also a good example on page 228f in the book "Java Persistence With Hibernate".
That allows you to handle the many columns as a single object in Java.
The mapping looks like this:
#org.hibernate.annotations.Columns(columns = {
#Column(name="C1"),
#Column(name="C2"),
#Column(name="C3"),
...
})
private List<Integer> c;
Hibernate will load all columns at once during the normal query.
In your case, you must copy the int values from the list into a fixed number of columns in nullSafeSet. Pseudocode:
for (int i=1; i<numColumns; i++)
if (i < list.size())
resultSet.setInt(index+i, list.get(i));
else
resultSet.setNull(index+i, Hibernate.INTEGER.sqlType());
In nullSafeGet you must create a list and stop adding elements when a column is NULL. For additional safety, I suggest to create your own list implementation which doesn't allow to grow beyond the number of columns (inherit from ArrayList and override ensureCapacity()).
[EDIT2] If you don't want to type all the #Column annotations, use a code generator for them. That can be as simple as script which you give a name and a number and it prints #Column(...) to System.out. After the script ran, just cut&paste the data into the source.
The only other solution would be to access the internal Hibernate API to build that information at runtime but that API is internal, so a lot of stuff is private. You can use Java reflection and setAccessible(true) but that code probably won't survive the next update of Hibernate.

You can use UserTypes to map a given number of columns to any type you wish. This could be a collection if (for example) for collections are always bounded in size by a known number of items.
It's been a while (> 3 years) since I used Hibernate so I'm pretty rusty but I recall it being very easy to do; your BespokeUserType class gets passed the ResultSet to hydrate your object from it.

I too have never used Hibernate.
I suggest writing a small program in an interpreted language (such as Python) in which you can execute a string as if it were a command. You could construct a statement which takes the tedious work out of doing what you want to do manually.

Related

Indexing a simple Java Record

I have a Java Object, Record . It represents a single record as a result of SQL execution. Can CQEngine index collection of Record ?
My class is of the form
public class Record {
private List<String> columnNames;
private List<Object> values;
... Other getters
}
I have looked through some examples, but I have no luck there.
I want to index only specific column(s) with its name and corresponding value. Can this be achived using cqengine or is there any other alternatives to achieve the same.
Thanks.
That seems to be a strange way to model data, but you can use CQEngine with that model if you wish.
(First off, CQEngine will have no use for your column names so you can remove that field.)
To do this, you will need to define a CQEngine virtual attribute for each of the indexes in your list of values.
Each attribute will need to be declared with the data type which will be stored in that column/index, and will need to be able to cast the object at that index in your list of values, to the appropriate data type (String, Double, Integer etc.).
So let's say your Record has a column called 'price', which is of type Double, and is stored at index 5 in the list of values. You could define an attribute which reads it as follows:
public static final Attribute<Record, Double> PRICE =
attribute("PRICE", record -> ((Double) record.values.get(5));
If this sounds complicated, it's because that way of modelling data makes things a bit complicated :) It's usually easier to work with a data model which leverages the Java type system (which your model does not). As such, you will need to keep track of the data types etc. of each field programmatically yourself.
CQEngine itself will work fine with that model though, because at the end of the day CQEngine attributes don't need to read fields, the attributes are just functions which are programmed to fetch values.
There's a bunch of stuff not covered above. For example can your values be null? (if so, you should use the nullable variety of attributes as discussed in the CQEngine docs. Or, might each of your Record objects have different sets of columns? (if so, you can create attributes on-the-fly when you encounter a new column, but you should probably cache the attributes you have created somewhere).
Hope that helps,
Niall (CQEngine author)

Is it okay to have type checking code when working with databases?

I'm trying to insert some code into a database. But, i have encountered a problem while working with models that are subclasses of each other. I have a list that holds all these subclases.
List<Metric> metrics = experiment.getMetrics();
for(Metric m : metrics) {
int id = m.getId();
// type checking code
}
Metric has sublcases of Rating and Quantity. Each of these in turn have there own uniquely defined tables. I am conflicted over the idea of using type checking. But I don't see any immediate solution. One alternative, which doesn't seem any better, would be to create a new column in the Metric table called metric_type. But this would lead to something quite similar to type checking. Any suggestions?
You have encountered Object-relational impedance mismatch due to mapping between not fully compatible systems. Since inheritance is not possible between tables in the relational model you will have to sacrifice something in the object model that uses inheritance. There will be edge cases no matter what you do unless you switch to an object database.
If you define a custom CRUD operations in classes that extend Metric loading entites can be tricky. What exactly will be loaded by Metric.get(id) if each table has it's own PK sequence and both Rating and Quantity can have the same numeric PK value.
You can take a look on how JPA solves this problem. It uses custom annotations e.g. #MappedSuperclass and #Entity. I guess that's a form of type checking.
I wouldn't suggest you to type check
The OOP way to solve this would be to make an insert method in the Metric class.
Then override the method both in Rating and Quality with the appropriate code that inserts the object in the respective table.
for(Metric m : metrics) {
int id = m.getId();
m.insert();
}
Inside your loop simply call insert and due to late-binding the appropriate method will be called and the right code will be executed.

Update query performance with Hibernate

We are examining 2 different methods to do our entities updates:
"Standard" updates - Fetch from DB, set Fields, persist.
We have a Query for each entity that looks like this:
String sqlQuery = "update Entity set entity.field1 = entity.field1, entity.field2 = entity.field2, entity.field3 = entity.field3, .... entity.fieldn = entity.fieldn"
We receive the fields that changed (and their new values) and we replace the string fields (only those required) with the new values. i.e. something like :
for each field : fields {
sqlQuery.replace(field.fieldName, getNewValue(field));
}
executeQuery(sqlQuery);
Any ideas if these options differ in performance to a large extent? Is the 2nd option even viable? what are the pros / cons?
Please ignore the SQL Injection vulnerability, the example above is only an example, we do use PreparedStatements for the query building.
And build a mix solution?
First, create a hasmap .
Second, create a new query for a PreparedStament using before hasmap (for avoid SQL injection)
Third, set all parameters.
The O(x)=n, where n is the number of parameters.
The first solution is much more flexible You can rely on Hibernate dirty checking mechanism for generating the right updates. That's one good reason why an ORM tool is great when writing data.
The second approach is no way way better because it might generate different update plans, hence you can't reuse the PreparedStatement statement cache across various column combinations. Instead of using string based templates (vulnerable to SQL injections) you could use JOOQ instead. JOOQ allows you to reference your table columns in Java, so you can build the UPDATE query in a type-safe fashion.

Hibernate: Doman Model to JPA Entity/DTO & Merge() Design pattern or best practice

The recommended way of using merge() is to first get the DTO first before inputting the changes.
public void merge(PersonModel model) {
Person inputDTO = PersonBuilder.build(model)
Person dto = get(pk)
dto.setName(inputDTO.getName())
dto.getChildren().clear()
Iterator<Child> iter = inputDTO .getChildren().Iterator();
while(iter.hasNext()){
dto.getChildren().add(iter.next());
}
dto.merge();
}
Is there a more elegant way of performing such operation translating domain model to dto and merging it so that no data are accidentally deleted.
Example of problem:
Hibernate: prevent delete orphan when using merge();
I find the need to clear the list and adding it very wasteful.
Can someone recommend me a design pattern or a way to code it properly?
Thank you
ADD ON:
1) Possible to use Hibernate Hashset to replace List? Will hibernate hashset replace elements base on primary keys?
any help?
"The recommended way of using merge() is to first get the DTO first before inputting the changes"
Who recommended you to do this?
"Is there a more elegant way of performing such operation translating domain model to dto and merging it so that no data are accidentally deleted."
I don't think you can translate domain objects to DTOs. A DTO is just about data, a domain object is data, behaviour and context. Completely different.
If you don't have behaviour and context in your domain objects (a.k.a. anemic domain model), you don't need an extra DTO layer that just duplicates the objects.
Because you tagged this question with Hibernate and mentioned it in your question, you don't need to call merge yourself because you just got the object from the database and Hibernate will flush the session to synchronize the changes with the database.
"Possible to use Hibernate Hashset to replace List? Will hibernate hashset replace elements base on primary keys?"
I would replace the List with a Hashset, since the table where the data is going to be stored is a set, not a list (you can't have duplicate records). A hashset will not replace elements based on primary keys. A set (any set, Hibernate's implementation is no different) works by preventing duplicates. It uses your equals() and getHashCode() implementation to find out if there is already an object in that set. If that is the case, it won't be added but it keeps the original.

Is there a better way to persist a map with the value being a set?

I'm looked a lot into being able to use Hibernate to persist a map like Map<String, Set<Entity>> with little luck (especially since I want it all to be on one table).
Mapping MultiMaps with Hibernate is the thing that seems to get referenced the most, which describes in detail how to go about implementing this using a UserCollectionType.
I was wondering, since that was written over four years ago, is there any better way of doing it now?
So, for example, I would like to have on EntityA a map like Map<String, Set/List<EntityB>>.
There would be two tables: EntityA and EntityB (with EntityB having a foreign key back to EntityA).
I don't want any intermediate tables.
The way how its done on my current project is that we transforming beans/collections to xml using xstream:
public static String toXML(Object instance) {
XStream xs = new XStream();
StringWriter writer = new StringWriter();
xs.marshal(instance, new CompactWriter(writer));
return writer.toString();
}
and then using Lob type in hibernate for persisting :
#Lob
#Column(nullable = false)
private String data;
I found this approach very generic and you could effectively implement flexible key/value storage with it. You you don't like XML format then Xstream framework has inbuilt driver for transforming objects to JSON. Give it a try, its really cool.
Cheers
EDIT: Response to comment.
Yes, if you want to overcome limitations of classic approach you are probably sacrifice something like indexing and/or search. You stil could implement indexing/searching/foreign/child relationships thru collections/generic entity beans by yourself - just maintain separate key/value table with property name/property value(s) for which you think search is needed.
I've seen number of database designs for products where flexible and dynamic(i.e. creation new attributes for domain objects without downtime) schema is needed and many of them use key/value tables for storing domain attributes and references from owner objects to child one. Those products cost millions of dollars (banking/telco) so I guess this design is already proven to be effective.
Sorry, that's not answer to your original question since you asked about solution without intermediate tables.
It depends :) When things are getting complex, you should understand what your application is doing.
In some situation, you may represent your Set as a TreeSet, and represent this TreeSet in an ordered coded String, such as ["1", "8", "12"] where 1, 8, 12 are primary keys, and then let's write code !
Obviously, it's not a general answer for, in my opinion, a too general question.

Categories

Resources