How to alter databases tables at run time using JPA? - java

I am creating CRUD application for customer . and he asked me to allow him to create new Fields in a form (database columns) without restarting the application server. which JPA implementation should I use (hibernate , eclipselink ,openjpa)
to accomplish this task and how it will be done?

Please don't change the database schema at runtime.
Assuming, you would add a column to a table. Then you have to add a field in your entity class, too. And the mapping. So you not only have to change a Java class at runtime, at next application start, you must add this field again. No JPA implementation can do that.
Of course, you can use plain JDBC. And instead of entity classes with concrete fields you can use something like a map for your dynamic fields. But you should adapt all your SQL queries according to the presence of dynamic fields. So you need a way to store the information, which dynamic fields are already created. You can do this with another table or use the table meta information. Additionally you have to manage user defined field names. E.g you should avoid SQL keywords, there is a maximum field name length, etc.
Or you can step back and rethink your approach. You have a requirement: Static given fields in a form and the possibility to create dynamic fields.
Why not adapt your data model to that requirement? A data model which is able to handle dynamic form fields. Such flexible datamodel wouldn't need dynamic SQL table field creation. (And JPA can handle that, too.)
The simplest example would be a table with two columns. One for the field name and one for the value (as string). Maybe a third one to identify the type.
Another alternative would be to use a NoSQL database system like a key value store or a document oriented database.

Related

Is it possible to have a composed name table?

We inherit a database and we need to do an API to communicate with it. We are considering the possibility to use JPA / Hibernate, but there is maybe a blocking constraint.
To facilitate maintenance, our predecessors have split a table into multiple with a business parameter.
So, there are some tables named ELEMENT_xxx where xxx is a specific name.
Is it possible to make a JPA entity with a constructor which take the specific name in parameter and then allow to query on the right table?
Thank you
PS:
If not, i think the solution could be to create an entity for each table. But we don't know how many tables they have, and we understood that they can create new.

Dynamic mapping an entity to multiple tables

I just searched for dynamic mapping with eclipselink, I cannot find a solution for my case, now in my system, an entity have 3 versions, one is working/draft (i.e. W100), another is is production (i.e. M100), and remaining one is history version (H100), at any time, we may access both working,production and history versions.
i.e.
Account account = // query from working.
account.setStatus("APPROVED");
account.increaseVersionNo();
myJPAEngine.updateApproved(account); // this step update status to both working and production
//, and W100.PRODUCTION_IPKEY = M100.IPKEY
// (if production version does't exist
//, create a new one with same columns as working version).
//, if increaseVersionNo() called, then we need copy old 'M'- production record to 'H' history version table.
//, my JPA engine need access both 'W'/'M'/'H' tables at same time by passing a parameter to JPA API metadata classes by ThreadLocal or other related workaround.
Does eclipselink (or Hibernate) support this? my dynamic mapping accept a parameter to decide which table is used, that is to say this is an interactive dynamic mapping.
Can and how we plugin some customization into eclipselink metadata classes?
Thanks.
SQL Table name is mapped into an entity class using xml configuration / annotation, so I doubt you can change it at runtime
The only way I can think of is by using entity manager native query, eg:
String sql = "select xyz from " + tableName;
List<MyEntity> reuslt = em.createNativeQuery(sql, MyEntity.class).getResultList();
I would create three subclasses of Account (DraftAccount, ActiveAccount, HistoricalAccount) and use a #MappedSuperclass or TABLE_PER_CLASS inheritance. To switch the account you would delete the old account and create a new one.
Another alternative is to to define a VIEW to make the three tables look like one (or actually change to have one table with a TYPE column).
You can customize EclipseLink operations using custom SQL, stored procedure or query redirectors, but I would recommend changing your model over faking it underneath.

What are JPA entities?

I am starting to use JPA and I always get confused with the term of entities and their usage, I have read a lot but I still don't quite get it.
I read the Oracle documentation of it but it does not really explain its role in the transaction.
What are JPA enities? does they actually hold the data for each row, I mean, are they stored instances that hold the row data? or they just map tables of the db and then insert and delete in them?
for example if I use this:
entity.setUserName("michel");
Then persisting it, then changing the user name, and persisitig it again (i.e merging it)
Does this change the previously entered user name? or does it create a new row in the db?
An Entity is roughly the same thing as an instance of a class when you are thinking from a code perspective or a row in a table (basically) when you are thinking from a database perspective.
So, it's essentially a persisted / persistable instance of a class. Changing values on it works just like changing values on any other class instance. The difference is that you can persist those changes and, in general, the current state of the class instance (entity) will overwrite the values the row for that instance (entity) had in the database, based on the primary key in the database matching the "id" or similar field in the class instance (entity).
There are exceptions to this behavior, of course, but this is true in general.
It's a model. It's a domain object that can be persisted. Don't over think it. Akin to a Rails model. And remember, models (in this paradigm) are mutable!

JPA and unique fields

I have two persistence objects in my app: Things and tags attached to things. The app can generate collections of things with tags attached. Tag objects have a unique name (it doesn't make sense to tag something twice with the same tag).
When inserting a Thing (with tag objects attached) some of these tag objects with the same name maybe already exist in the db. Now here is the part I don't recall about JPA, is there a way to tell JPA that it should not try to add the corresponding objects to the db if it violates the unique constraint? Or is there a way to do this efficiently w/o having to first fetch all objects, then merge the collection in memory and then write everything back?
I'm also wondering if it's possible to persist a whole collection at once or do I have to call persist for every object when using JPA?
I don't know of any way of doing this 'cleanly', neither with JPA nor with Hibernate or any other provider. You can achieve what you want with a custom SQL query though (similar to this question):
#Entity
#Table(name="tag")
#SQLInsert( sql="INSERT INTO tag(name, count) VALUES (?, ?)
ON DUPLICATE KEY UPDATE set count = count + 1")
public class Tag {}
Now you are unfortunately bound to both Hibernate and MySQL. You can vary the sql syntax for other DB:s, and/or use stored procedures, try an update first and insert on failure etc. They all have their drawbacks, so it would be handy if JPA would support this, but alas.
Regarding you second question, JPA support persisting whole object graphs, including collections.

How to map dynamically created table in Hibernate?

I am working on a web application. We are using Hibernate as ORM in our project. Actually, our application creates some tables dynamically based on user selection. The user can select table name, column name and then s/he can import data from a csv file. So my question is: how to map this dynamically created table with Hibernate and Java objects?
It can be done dynamically, but it's somewhat messy:
You'll need to dynamically alter Hibernate's Configuration object before SessionFactory is built. If you're using Spring, this can be done by overriding postProcessAnnotationConfiguration() method of AnnotationSessionFactoryBean; otherwise you'll just need to do it using your Configuration object prior to invoking buildSessionFactory() on it.
If you need to do this without application restart, you're looking at either rebuilding your SessionFactory (which means your users will have to wait until that's done) or using a separate SessionFactory instance specifically dedicated to your custom classes (which is next to impossible if your custom classes need to reference your built-in classes).
You can get access to class / table mappings via configuration.getMappings(). You will then need to create a new table mapping via Table API and add it to configuration via addTable(). Same thing will have to be done with PersistentClass which represents a class mapping. If you're using the same class to represent multiple entities (e.g. map multiple tables) make sure to use unique entity names for each. You'll have to do this (alter the configuration) on every app restart.
How much of the tables are dynamically created? Are the tables similar and you just change the database name?
Here is a discussion of changing the table name:
http://www.experts-exchange.com/Programming/Languages/Java/J2EE/Frameworks/Spring/Q_24237099.html
If you are completely building a new table, can you use a view, and just direct people to a view?
Why are you using Hibernate for this, rather than just dynamically creating queries in JDBC?
The database solution - you can create a view and point it to one table or another (assuming the structure is identical).
CREATE VIEW HIBERNATE_NAME
as
SELECT * FROM TABLE_A
or
CREATE VIEW HIBERNATE_NAME
as
SELECT * FROM TABLE_B
You would need you application to execute native SQL (DDL),
however this should be easier than Hibernate hacks

Categories