I haven't found anywhere any examples dealing with dynamic Hibernate configuration in order to change the mappings dynamically, only a few lines here and there, and even HIA does not address the subject more deeply. Despite the fact that there are indeed some API's methods which can be used to deal with these matters, I could find no examples, so I had to come up to a solution by myself.
I know I would probably be better here accessing the database directly through JDBC and SQL, but anyway, I think the information I share here can be of value to someone else.
The solution to my problem was implemented as follows:
Since with Hibernate you always have to define a primary key in any mapping you want to do, and since my mapping would be generated dynamically, I am using as primary key the "rowid" from Oracle which I can get for any table (and can indeed act as a primary key).
My mapping document is then composed in memory (using DOM):
Code:
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
// <hibernate-mapping> (root element)
Element rootElement = doc.createElement("hibernate-mapping");
doc.appendChild(rootElement);
etc...
And the resulting XML will vary, according to the name of the table and name of the field inside that table that user has chosen at the 2 dropdown listboxes:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<hibernate-mapping>
<class name="my_package.TableField"
table="TABLE_CHOSEN_BY_USER_AT_FIRST_DROPDOWN_BOX">
<id column="rowid" name="rowid" type="java.lang.String">
<generator class="native"/>
</id>
<property
column="FIELD_CHOSEN_BY_USER_AT_SECOND_DROPDOWN_BOX"
name="fieldContent" type="java.lang.String"/>
</class>
</hibernate-mapping>
The only thing that will change dynamically is indeed the mapping document. I.e., from run to run, the same class will be mapping to different tables (and columns inside these tables). Follows the code of the statically defined class:
Code:
public class TableField implements Serializable {
/** identifier field */
private String rowid;
private String fieldContent;
/** full constructor */
public TableField(String fieldContent) {
this.fieldContent = fieldContent;
this.rowid = rowid;
}
/** default constructor */
public TableField() {
}
public String getFieldContent() {
return this.fieldContent;
}
public void setFieldContent(String fieldContent) {
this.fieldContent = fieldContent;
}
public String getRowid() {
return this.rowid;
}
public void setRowid(String rowid) {
this.rowid = rowid;
}
}
Now, with the DOM mapping document in memory, I just have to create a new Configuration object and add this mapping document to this configuration. I then use this configuration object to build a
new Session Factory and open a new Session, with which I will be able to execute my query.
Note also that I DON'T HAVE to add the class to my initial configuration as in 'myConfiguration.addClass(TableField.class)', since I don't have any corresponding .HBM.XML mapping document in disk (I would get an 'Initial Session Configuration failed' exception). All I have to do in order for the mapping to succeed is to add the mapping document created on the fly to the configuration.
My query will always be the same ('from TableField'). The difference is that it will be mapping to a different column and table at each run, depending on the field and table chosen by the user and which were used to build the mapping document on the fly:
Code:
Configuration configuration = new Configuration();
configuration.addDocument(doc);
SessionFactory newSessionFactory = configuration.
buildSessionFactory();
Session newSession = newSessionFactory.openSession();
String temp = "from TableField table_field";
Query q = newSession.createQuery(temp);
I can then access each value of my result set, running my validation routine upon the desired column, and making the passed results go to a OK queue and the not valid results go to a NOT OK queue, which I can then use to show specific information in my JSP.