As per the startup instructions I was able to successfully generate table classes in JOOQ that end up looking like so:
public class AgencyMeta extends TableImpl<AgencyMetaRecord> {
private static final long serialVersionUID = 214852552;
/**
* The reference instance of <code>PUBLIC.AGENCY_META</code>
*/
public static final AgencyMeta AGENCY_META = new AgencyMeta();
/**
* The class holding records for this type
*/
#Override
public Class<AgencyMetaRecord> getRecordType() {
return AgencyMetaRecord.class;
}
/**
* The column <code>PUBLIC.AGENCY_META.ID</code>.
*/
public final TableField<AgencyMetaRecord, Long> ID = createField("ID", org.jooq.impl.SQLDataType.BIGINT.nullable(false).defaulted(true), this, "");
/**
* The column <code>PUBLIC.AGENCY_META.AGENCY_ID</code>.
*/
public final TableField<AgencyMetaRecord, Long> AGENCY_ID = createField("AGENCY_ID", org.jooq.impl.SQLDataType.BIGINT.nullable(false), this, "");
/**
* The column <code>PUBLIC.AGENCY_META.KEY</code>.
*/
public final TableField<AgencyMetaRecord, String> KEY = createField("KEY", org.jooq.impl.SQLDataType.VARCHAR.length(255).nullable(false), this, "");
/**
* The column <code>PUBLIC.AGENCY_META.VALUE</code>.
*/
public final TableField<AgencyMetaRecord, String> VALUE = createField("VALUE", org.jooq.impl.SQLDataType.CLOB, this, "");
/**
* Create a <code>PUBLIC.AGENCY_META</code> table reference
*/
public AgencyMeta() {
this("AGENCY_META", null);
}
/**
* Create an aliased <code>PUBLIC.AGENCY_META</code> table reference
*/
public AgencyMeta(String alias) {
this(alias, AGENCY_META);
}
private AgencyMeta(String alias, Table<AgencyMetaRecord> aliased) {
this(alias, aliased, null);
}
private AgencyMeta(String alias, Table<AgencyMetaRecord> aliased, Field<?>[] parameters) {
super(alias, Public.PUBLIC, aliased, parameters, "");
}
/**
* {#inheritDoc}
*/
#Override
public Identity<AgencyMetaRecord, Long> getIdentity() {
return Keys.IDENTITY_AGENCY_META;
}
/**
* {#inheritDoc}
*/
#Override
public UniqueKey<AgencyMetaRecord> getPrimaryKey() {
return Keys.CONSTRAINT_A0;
}
/**
* {#inheritDoc}
*/
#Override
public List<UniqueKey<AgencyMetaRecord>> getKeys() {
return Arrays.<UniqueKey<AgencyMetaRecord>>asList(Keys.CONSTRAINT_A0, Keys.CONSTRAINT_A0F);
}
/**
* {#inheritDoc}
*/
#Override
public List<ForeignKey<AgencyMetaRecord, ?>> getReferences() {
return Arrays.<ForeignKey<AgencyMetaRecord, ?>>asList(Keys.CONSTRAINT_A0FC);
}
/**
* {#inheritDoc}
*/
#Override
public AgencyMeta as(String alias) {
return new AgencyMeta(alias, this);
}
/**
* Rename this table
*/
public AgencyMeta rename(String name) {
return new AgencyMeta(name, null);
}
}
Apparently it has all the ingredients to create a table, and in part I can do so like this:
create.createTable(PRODUCT).column(PRODUCT.ID, SQLDataType.BIGINT.nullable(false).defaulted(true)).execute();
And so forth...
When the program starts up for the first time, I would like to build the h2 database on the spot.
Is there a way to create a table or even the whole database in a one-shot command? It would seem there should be based on what is provided.
As far as i know, there is only posibility to issue DDL commands manually, as described in documentation: ddl statements
The documentation describes that:
jOOQ's DDL support is currently still very limited. In the long run, jOOQ will support the most important statement types for frequent informal database migrations, though. Note that jOOQ will not aim to replace existing database migration frameworks. At Data Geekery, we usually recommend using Flyway for migrations. See also the tutorial about using jOOQ with Flyway for more information.
Related
I have an object in use throughout my codebase, UnsecureObject. This object is auto-generated with no getters/setters, and all member fields are public. So editing is done by doing something like the following:
unsecureObjInstance.firstName = "Jane";
This is not desirable for numerous reasons that I probably don't have to explain here. But using this generated class is required for some other technical details with our messaging pipeline that I won't go into.
I have a desire is to leverage a mapping utility written by someone else on my team to convert this UnsecureObject to a pojo that I am writing.
An example of the mapper in action (with two normal classes w/ getters/setters) would be something like:
new MapperBuilder<>(PojoOne.class, PojoTwo.class)
.from(PojoOne::getName).to(PojoTwo::getFirstName)
.build();
This will map the PojoOne#name field to the PojoTwo#firstName field.
Is there a way to translate this to input my UnsecureObject here? I have tried something like the following:
new MapperBuilder<>(UnsecureObject.class, SecureObject.class)
.from(u -> u.firstName).to(SecureObject::getFirstName)
.build();
But get an error here, something along the lines of 'u -> u.firstName' could not be invoked.
So the question is:
Is there a way to essentially "construct" a getter on the fly using these public members? So in the .from() method, I can construct the call to look like a standard method that will yield my u.firstName?
Thanks for the help!
EDIT:
this is approx what the MapperBuilder class looks like (attempted to edit a bit to take away project specific wrappers/simplify)
/**
* This class is used to convert between POJO getter method references to the corresponding field names.
* #param <B> type
*/
public interface PojoProxy<B> {
/**
* Invokes the given getter method and returns information about the invocation.
* #param getter the getter to invoke
* #return information about the method invoked
*/
<T> GetterInvocation<T> invokeGetter(Function<B, T> getter);
}
/**
* Stores information about a method invocation.
* #param <T> method return type
*/
public interface GetterInvocation<T> {
public Class<T> getReturnType();
public String getFieldName();
}
/**
* A builder class to create {#link Mapper} instances.
* #param <FROM> source type
* #param <TO> target type
*/
public class MapperBuilder<FROM, TO> {
private final Class<FROM> _fromClass;
private final Class<TO> _toClass;
private final PojoProxy<FROM> _fromProxy;
private final PojoProxy<TO> _toProxy;
public MapperBuilder(Class<FROM> fromClass, Class<TO> toClass) {
_fromClass = fromClass;
_toClass = toClass;
//We will pretend there is an impl that provides the proxy.
//Proxies wrap the from and to classes in order to get reflection information about their getter calls.
_fromProxy = PojoProxy.of(fromClass);
_toProxy = PojoProxy.of(toClass);
}
public <FROM_VALUE> ToFieldBuilder<FROM_VALUE> from(Function<FROM, FROM_VALUE> getter) {
GetterInvocation<FROM_VALUE> methodInvocation = _fromProxy.invokeGetter(getter);
return new ToFieldBuilder<>(methodInvocation.getFieldName(), methodInvocation.getReturnType());
}
public class ToFieldBuilder<FROM_VALUE> {
private final String _fromFieldPath;
private final Class<FROM_VALUE> _fromClass;
public ToFieldBuilder(String fromFieldPath, Class<FROM_VALUE> fromClass) {
_fromFieldPath = fromFieldPath;
_fromClass = fromClass;
}
public <TO_VALUE> FromFieldBuilder<FROM_VALUE, TO_VALUE> to(Function<TO, TO_VALUE> getter) {
//similar to above, but now using a FromFieldBuilder.
}
}
public class FromFieldBuilder<FROM_VALUE, TO_VALUE> {
//impl..
}
}
I dont see MapperBuilder.from() method details, you can try this implementation of MapperBuilder.java Function (getter) -> (BiConsumer) setter
public class MapperBuilder<S, D> {
private final S src;
private final D dest;
public MapperBuilder(S src, Class<D> dest) {
this.src = src;
try {
this.dest = dest.newInstance();
} catch (Exception e) {
throw new RuntimeException("Required default constructor for: " + dest);
}
}
//getter - function to get value from source instance
//setter - biConsumer to set value to destination instance
//example - map(SrcClass::getSrcValue, DestClass::setDestValue)
public <V> MapperBuilder<S, D> map(Function<S, V> getter, BiConsumer<D, V> setter) {
setter.accept(dest, getter.apply(src));
return this;
}
public D build() {
return dest;
}
}
SrcClass.java some source class:
public class SrcClass {
private String srcValue;
public String getSrcValue() {
return srcValue;
}
public void setSrcValue(String srcValue) {
this.srcValue = srcValue;
}
}
DestClass.java some destination class:
package com.example.demo;
public class DestClass {
private String destValue;
public String getDestValue() {
return destValue;
}
public void setDestValue(String destValue) {
this.destValue = destValue;
}
}
DemoApplication.java demo:
public class DemoApplication {
public static void main(String[] args) {
SrcClass src = new SrcClass();
src.setSrcValue("someValue");
DestClass dest = new MapperBuilder<>(src, DestClass.class)
.map(SrcClass::getSrcValue, DestClass::setDestValue)
// map another fields
.build();
// for your UnsecureObject case
UnsecureObject unsecureObject = new MapperBuilder<>(src, UnsecureObject.class)
.map(SrcClass::getSrcValue,
(unsecure, srcValue) -> unsecure.unsecureValue = srcValue)
.build();
}
}
I have these classes:
My problem is that for each class I am having to manually register the node.
/**
* Contains the encoder for messages from the server.
*
* #since 18/08/2018
*/
public abstract class MessageEncoder<T> {
/**
* Register this encoder to a message.
*/
public abstract void register();
/**
* Get the encoded message to send to the client.
*
* #param message The message.
* #return the {#link GamePacket} ready to be sent.
*/
public abstract GamePacket encode(T message);
}
Where < T > is always someClass extends Message.
And here is how a message encoder looks like:
public final class ComponentMessageEncoder extends MessageEncoder<ComponentTextMessage> {
#Override
public void register() {
GameConstants.RELEASE.register(ComponentTextMessage.class, this);
}
#Override
public GamePacket encode(ComponentTextMessage message) {
// TODO Auto-generated method stub
return null;
}
}
As you can see the items in the register method, I have to manually type that for every encoder I make.
Is there a shortcut for this that I can just put in MessageEncoder abstract class instead?
I could not find anything that works here
Edit:
Where register sig. is:
/**
* The encoders.
*/
private final Map<Class<? extends Message>, MessageEncoder<?>> encoders = new HashMap<>();
/**
* Register a encoder to a message.
*
* #param message The message.
* #param encoder The encoder.
*/
public void register(Class<? extends Message> message, MessageEncoder<?> encoder) {
encoders.put(message, encoder);
}
You can do slightly better with:
public abstract class MessageEncoder<T extends Message> {
protected MessageEncoder(Class<? extends T> messageClass) {
GameConstants.RELEASE.register(messageClass, this);
}
/**
* Get the encoded message to send to the client.
*
* #param message The message.
* #return the {#link GamePacket} ready to be sent.
*/
public abstract GamePacket encode(T message);
}
Now subclasses would do:
public final class ComponentMessageEncoder extends MessageEncoder<ComponentTextMessage> {
ComponentMessageEncoder() {
super(ComponentTextMessage.class);
}
// etc as before
}
This cuts down slightly on the repetition, and it allows the compiler to stop you from getting it wrong, so it's something of a win.
Getting an error when trying to set up a unit test using Jooq mocking with custom types (Joda Time). I have registered a converter during code generation. It almost looks like Jooq can't find / doesn't see the converter and is trying to fall back on ConvertAll, which doesn't work.
Querying the mock result throws the exception (below).
Converter:
import org.joda.time.DateTime;
import java.sql.Timestamp;
import org.joda.time.DateTimeZone;
import org.jooq.Converter;
public class DateTimeConverter implements Converter<Timestamp, DateTime> {
#Override
public DateTime from(Timestamp databaseObject) {
return new DateTime(databaseObject.getTime()).withZone(DateTimeZone.UTC);
}
#Override
public Timestamp to(DateTime userObject) {
return new Timestamp(userObject.getMillis());
}
#Override
public Class<Timestamp> fromType() {
return Timestamp.class;
}
#Override
public Class<DateTime> toType() {
return DateTime.class;
}
}
The class is generated successfully:
/**
* This class is generated by jOOQ
*/
package redacted.generated.jooq.tables;
/**
* This class is generated by jOOQ.
*/
#javax.annotation.Generated(value = {"http://www.jooq.org", "3.0.0"},
comments = "This class is generated by jOOQ")
#java.lang.SuppressWarnings({ "all", "unchecked" })
public class Bug extends org.jooq.impl.TableImpl<redacted.generated.jooq.tables.records.BugRecord> {
private static final long serialVersionUID = 1992533553;
/**
* The singleton instance of <code>public.bug</code>
*/
public static final redacted.generated.jooq.tables.Bug BUG = new redacted.generated.jooq.tables.Bug();
/**
* The class holding records for this type
*/
#Override
public java.lang.Class<redacted.generated.jooq.tables.records.BugRecord> getRecordType() {
return redacted.generated.jooq.tables.records.BugRecord.class;
}
/**
* The column <code>public.bug.testdate</code>.
*/
public final org.jooq.TableField<redacted.generated.jooq.tables.records.BugRecord, org.joda.time.DateTime> TESTDATE = createField("testdate", org.jooq.impl.SQLDataType.TIMESTAMP.asConvertedDataType(new name.benjaminAbbitt.jooqJodaTime.DateTimeConverter()), this);
/**
* The column <code>public.bug.id</code>.
*/
public final org.jooq.TableField<redacted.generated.jooq.tables.records.BugRecord, java.lang.Integer> ID = createField("id", org.jooq.impl.SQLDataType.INTEGER, this);
/**
* Create a <code>public.bug</code> table reference
*/
public Bug() {
super("bug", redacted.generated.jooq.Public.PUBLIC);
}
/**
* Create an aliased <code>public.bug</code> table reference
*/
public Bug(java.lang.String alias) {
super(alias, redacted.generated.jooq.Public.PUBLIC, redacted.generated.jooq.tables.Bug.BUG);
}
/**
* {#inheritDoc}
*/
#Override
public org.jooq.Identity<redacted.generated.jooq.tables.records.BugRecord, java.lang.Integer> getIdentity() {
return redacted.generated.jooq.Keys.IDENTITY_BUG;
}
/**
* {#inheritDoc}
*/
#Override
public java.util.List<org.jooq.UniqueKey<redacted.generated.jooq.tables.records.BugRecord>> getKeys() {
return java.util.Arrays.<org.jooq.UniqueKey<redacted.generated.jooq.tables.records.BugRecord>>asList(redacted.generated.jooq.Keys.BUG_ID_KEY);
}
/**
* {#inheritDoc}
*/
#Override
public redacted.generated.jooq.tables.Bug as(java.lang.String alias) {
return new redacted.generated.jooq.tables.Bug(alias);
}
}
Exception is:
org.jooq.exception.DataTypeException: Cannot convert from 2014-03-05T17:57:24.668Z (class org.joda.time.DateTime) to class java.sql.Timestamp
at org.jooq.tools.Convert$ConvertAll.fail(Convert.java:809)
at org.jooq.tools.Convert$ConvertAll.from(Convert.java:747)
at org.jooq.tools.Convert.convert0(Convert.java:296)
at org.jooq.tools.Convert.convert(Convert.java:288)
at org.jooq.tools.Convert.convert(Convert.java:349)
at org.jooq.impl.AbstractRecord.getValue(AbstractRecord.java:219)
at org.jooq.tools.jdbc.MockResultSet.getValue(MockResultSet.java:383)
at org.jooq.tools.jdbc.MockResultSet.getTimestamp(MockResultSet.java:566)
at org.jooq.impl.Utils.getTimestamp(Utils.java:2195)
at org.jooq.impl.Utils.getFromResultSet(Utils.java:1952)
at org.jooq.impl.Utils.getFromResultSet(Utils.java:1881)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.setValue(CursorImpl.java:1464)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.operate(CursorImpl.java:1447)
at org.jooq.impl.CursorImpl$CursorIterator$CursorRecordInitialiser.operate(CursorImpl.java:1439)
at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:119)
at org.jooq.impl.CursorImpl$CursorIterator.fetchOne(CursorImpl.java:1412)
at org.jooq.impl.CursorImpl$CursorIterator.next(CursorImpl.java:1389)
at org.jooq.impl.CursorImpl$CursorIterator.next(CursorImpl.java:1353)
at org.jooq.impl.CursorImpl.fetch(CursorImpl.java:202)
at org.jooq.impl.CursorImpl.fetch(CursorImpl.java:176)
at org.jooq.impl.AbstractResultQuery.execute(AbstractResultQuery.java:268)
at org.jooq.impl.AbstractQuery.execute(AbstractQuery.java:321)
at org.jooq.impl.AbstractResultQuery.fetch(AbstractResultQuery.java:324)
at org.jooq.impl.SelectImpl.fetch(SelectImpl.java:1034)
at org.jooq.ResultQuery$fetch.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:45)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:108)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:112)
at redacted.bug.BugTest.getTest(BugTest.groovy:47)
BugTest.groovy:
public class BugTest {
#Test
public void getTest() {
DSLContext testContext = setupDSL(new MockDataProvider() {
#Override
public MockResult[] execute(MockExecuteContext ctx) throws SQLException {
DSLContext create = DSL.using(SQLDialect.POSTGRES)
def result = create.newResult(BUG)
result.add(create.newRecord(BUG, [id: 0, testdate: new DateTime()]))
[new MockResult(result.size(), result)]
}
})
testContext.select().from(BUG).fetch() //this line fails
}
private DSLContext setupDSL(MockDataProvider provider) {
MockConnection connection = new MockConnection(provider)
return DSL.using(connection, SQLDialect.POSTGRES)
}
}
This is bug #5771. As of jOOQ 3.9.0, MockResultSet doesn't use your converter to revert your user-defined type <U> (DateTime) to the JDBC/database type <T> (Timestamp) prior to exposing it through the JDBC API. Without using your converter, jOOQ doesn't know how to perform this conversion, hence the exception.
The correct behaviour should be:
You create a result using <U> (DateTime) values.
jOOQ's MockResultSet uses your converter to convert <U> to <T> (Timestamp) prior to passing that value through the JDBC API
jOOQ's ResultQuery fetches <T> from the JDBC API
jOOQ uses your converter to convert <T> back to <U> again
Workaround:
For the time being, you shouldn't put any user-defined types in your MockResult, but use the JDBC Timestamp type only (and no generated columns that refer to your user-defined type)
I have a question regarding static method access. I have a class within i have 4 static method. as shown in code:
package com.itrucking.util;
public class ZKUtil implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
* #author Shekhar
* #param _class
* #param listbox
* To make Listbox sorting enabled
* #throws NoSuchMethodException
* #throws SecurityException
*/
public static void setSortingEnabled(Class<?> _class, Listbox listbox){
Map<Listheader, String> sortingPair = new HashMap<Listheader, String>();
sortingPair = getMapForSorting(_class, listbox);
if (!sortingPair.isEmpty()) {
for (Map.Entry<Listheader, String> entry : sortingPair.entrySet()) {
entry.getKey().setSortAscending(
new FieldComparator(entry.getValue(), true));
entry.getKey().setSortDescending(
new FieldComparator(entry.getValue(), false));
}
}
}
/**
* #author Shekhar
* #param _class
* #param listbox
* #return Map<Listheader, String>
*/
private static Map<Listheader, String> getMapForSorting(Class<?> _class,Listbox listbox) {
List<Listheader> headerList = getListHeaderList(listbox);
Map<Listheader, String> sortingPair = new HashMap<Listheader, String>();
System.out.println(_class);
Field[] fields = _class.getDeclaredFields();
for (Field f : fields) {
// System.out.println(f.getName()+":"+f.getType());
for (Listheader lh : headerList) {
if (f.getName().equals(getId(lh)))
sortingPair.put(lh, f.getName());
}
}
System.out.println(sortingPair);
return sortingPair;
}
private static String getId(Listheader listheader) {
String listheaderId = null;
if (listheader.getId().contains("_")) {
listheaderId = listheader.getId().split("_")[1];
// System.out.println("listheaderId->"+listheaderId);
}
return listheaderId;
}
/**
* #author Shekhar
* #param listbox
* #return List<Listheader>
*/
#SuppressWarnings("unchecked")
private static List<Listheader> getListHeaderList(Listbox listbox) {
List<Listheader> headerList = new ArrayList<Listheader>();
Listhead listhead = null;
List<Component> listboxComponentList = listbox.getChildren();
for (Component listboxComponent : listboxComponentList) {
if (listboxComponent instanceof Listhead) {
listhead = (Listhead) listboxComponent;
break;
}
}
List<Component> listOfComp = listhead.getChildren();
if (listhead != null) {
for (Component c : listOfComp) {
if (c instanceof Listheader)
headerList.add((Listheader) c);
}
}
return headerList;
}
}
and i am calling setSortingEnabled() method from onLoadShipperDetailsListCtrl() from code bellow :
package com.itrucking.webui.controller;
public class ShipperDetailsListCtrl{
/**
* #param e
* #return void
*/
public void onCreate$window_shipperDetailsList(Event e){
onLoadShipperDetailsListCtrl();
}
/**
* #return void
*/
public void onLoadShipperDetailsListCtrl(){
System.out.println("onLoadShipperDetailsListCtrl called.");
shipperList = shipperService.getShipperList();
doRenderListboxShipperDetailsList(shipperList);
ZKUtil.setSortingEnabled(ShipperMaster.class, listbox_shipperDetailsList);
}
}
so what i think if i am calling setSortingEnabled() method from other class so i kept is as public and other method's i kept as private but it's giving me error as :
java.lang.NoSuchMethodError: com/itrucking/util/ZKUtil.getMapForSorting(Ljava/lang/Class;Lorg/zkoss/zul/Listbox;)Ljava/util/Map;
Why there is error NoSuchMethodError for ZKUtil.getMapForSorting() call in setSortingEnabled()
I know we can call private method from public in the same class. So i am not able to understand what is the problem.
Thanks in advance.
A NoSuchMethodError (the runtime error saying a method can't be found, instead of a compiler error) usually means that the .class files you're using are of a different version than the files you compiled against. In this case, you probably made changes to ZKUtil.java, but the JVM is loading an outdated version of ZKUtil.class. Clean and rebuild all of your .class files.
Our data model is separated into schemas on two databases. The schemas are used in isolation except for a few single-key relationships that bridge between the two. There are no write transactions that will span both databases.
Similar to this question Doing a join over 2 tables in different databases using Hibernate, we want to use Hibernate to handle joining the entities. We cannot use the database solution (Federated views on DB2).
We have set up Hibernate with two separate database configurations (Doctor and Patient), which works perfectly when using DAOs to explicitly access a particular session.
We want to use Hibernate to automatically retrieve the entity when we call DoctorBO.getExam().getPatient() Where examination contains an id pointing to the Patient table on the other database.
One way I've tried doing this is using a custom UserType:
public class DistributedUserType implements UserType, ParameterizedType
{
public static final String CLASS = "CLASS";
public static final String SESSION = "SESSION";
private Class<? extends DistributedEntity> returnedClass;
private String session;
/** {#inheritDoc} */
#Override
public int[] sqlTypes()
{
// The column will only be the id
return new int[] { java.sql.Types.BIGINT };
}
/** {#inheritDoc} */
#Override
public Class<? extends DistributedEntity> returnedClass()
{
// Set by typedef parameter
return returnedClass;
}
/** {#inheritDoc} */
#Override
public boolean equals(Object x, Object y) throws HibernateException
{
if (x == y)
{
return true;
}
if ((x == null) || (y == null))
{
return false;
}
Long xId = ((DistributedEntity) x).getId();
Long yId = ((DistributedEntity) y).getId();
if (xId.equals(yId))
{
return true;
}
else
{
return false;
}
}
/** {#inheritDoc} */
#Override
public int hashCode(Object x) throws HibernateException
{
assert (x != null);
return x.hashCode();
}
/** {#inheritDoc} */
#Override
public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException
{
Long id = rs.getLong(names[0]);
return HibernateUtils.getSession(session).get(returnedClass, id);
}
/** {#inheritDoc} */
#Override
public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException
{
DistributedEntity de = (DistributedEntity) value;
st.setLong(index, de.getId());
}
/** {#inheritDoc} */
#Override
public Object deepCopy(Object value) throws HibernateException
{
return value;
}
/** {#inheritDoc} */
#Override
public boolean isMutable()
{
return false;
}
/** {#inheritDoc} */
#Override
public Serializable disassemble(Object value) throws HibernateException
{
return (Serializable) value;
}
/** {#inheritDoc} */
#Override
public Object assemble(Serializable cached, Object owner) throws HibernateException
{
return cached;
}
/** {#inheritDoc} */
#Override
public Object replace(Object original, Object target, Object owner) throws HibernateException
{
return original;
}
/** {#inheritDoc} */
#Override
public void setParameterValues(Properties parameters)
{
String clazz = (String) parameters.get(CLASS);
try
{
returnedClass = ReflectHelper.classForName(clazz);
}
catch (ClassNotFoundException e)
{
throw new IllegalArgumentException("Class: " + clazz + " is not a known class type.");
}
session = (String) parameters.get(SESSION);
}
}
Which would then be used:
#TypeDef(name = "testUserType", typeClass = DistributedUserType.class, parameters = {
#Parameter(name = DistributedUserType.CLASS, value = PatientBO.CLASSNAME),
#Parameter(name = DistributedUserType.SESSION, value = HibernateUtils.PATIENT_SESS) })
#Type(type = "testUserType")
#Column(name = "PATIENT_ID")
private PatientBO patient;
The UserType works - the data is loaded correctly with only the Id of the field persisted to the database. I have tested very simple examples of doctor.getExam().getPatient() and doctor.getExam().setPatient() and both seem to work great, however I think this is a terrible hack and I do not have adequate knowledge of Hibernate to know if this is safe to use.
Is there a better way to achieve what we want? Is the way I've described here adequate, or will it cause difficulties in the future?
I don't think it's a good idea. You're trying to make "as if" everything was in a single database, whereas it's not the case. And you make "as if" there was a real toOne association between an exam and a patient, although it's not a real association.
Although you are conscious of this fact, other or future developers won't necessarily be, and will wonder why it's not possible to make a query such as
select e from Exam e left join fetch e.patient
or
select e from Exam e where e.patient.name like 'Smith%'
In short, your pseudo-association only fulfills a very small part of the contract a regular association offers, and this will, IMO, cause more confusion than comfort.
Nothing stops you from having a utility method like
Patient getExamPatient(Exam e)
that does the same thing, but makes it clear that there is no real asociation between both entities.