2012-01-24 6 views
9

Il nostro modello di dati è separato in schemi su due database. Gli schemi sono usati in isolamento, tranne per alcune poche relazioni a chiave singola che collegano tra loro. Non ci sono transazioni di scrittura che coprono entrambi i database.Sospensione di entità da più database

Simile a questa domanda Doing a join over 2 tables in different databases using Hibernate, vogliamo utilizzare Hibernate per gestire l'unione delle entità. Non possiamo usare la soluzione di database (viste federate su DB2).

Abbiamo impostato Hibernate con due configurazioni di database separate (Medico e Paziente), che funziona perfettamente quando si utilizzano DAO per accedere esplicitamente a una determinata sessione.

Vogliamo utilizzare Hibernate per recuperare automaticamente l'entità quando chiamiamo DoctorBO.getExam().getPatient() Dove esame contiene un ID che punta alla tabella Paziente sull'altro database.

Un modo in cui ho provato a fare questo sta usando un UserType personalizzato:

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); 
    } 
} 

che sarebbe poi essere utilizzato:

@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; 

L'UserType funziona - i dati vengono caricati correttamente con il solo L'ID del campo è rimasto nel database. Ho provato esempi molto semplici di doctor.getExam().getPatient() e doctor.getExam().setPatient() ed entrambi sembrano funzionare alla grande, tuttavia penso che questo sia un hack terribile e non ho una conoscenza adeguata di Hibernate per sapere se questo è sicuro da usare.

C'è un modo migliore per ottenere ciò che vogliamo? Il modo in cui ho descritto qui è adeguato, o causerà difficoltà in futuro?

+0

+1 per una soluzione interessante. – Firo

risposta

5

Non penso sia una buona idea. Stai cercando di rendere "come se" tutto fosse in un unico database, mentre non è il caso. E fai "come se" ci fosse una vera e propria associazione toOne tra un esame e un paziente, anche se non è una vera associazione.

Anche se si è consapevoli di questo fatto, loro o gli sviluppatori futuro non sarà necessariamente essere, e vi chiedo perché non è possibile effettuare una ricerca del tipo

select e from Exam e left join fetch e.patient 

o

select e from Exam e where e.patient.name like 'Smith%' 

In breve, la tua pseudo-associazione soddisfa solo una parte molto piccola del contratto che offre un'associazione regolare, e questo, IMO, causerà più confusione che conforto.

Nulla vi impedisce di avere un metodo di utilità come

Patient getExamPatient(Exam e) 

che fa la stessa cosa, ma chiarisce che non v'è alcun reale Asociation tra le due entità.

+0

Penso che stavo cercando di essere troppo intelligente. Questa soluzione è migliore di quella che ho proposto per le ragioni che hai affermato. –