Hibernate Entities from Multiple Databases

9

Nosso modelo de dados é separado em esquemas em dois bancos de dados. Os esquemas são usados isoladamente, exceto por alguns relacionamentos de chave única que fazem a ponte entre os dois. Não há transações de gravação que abranjam os dois bancos de dados.

Semelhante a essa pergunta Fazendo uma junção de mais de duas tabelas em bancos de dados diferentes usando o Hibernate , queremos usar o Hibernate para lidar com a junção do entidades. Não podemos usar a solução de banco de dados (visualizações federadas no DB2).

Nós configuramos o Hibernate com duas configurações de banco de dados separadas (Doctor e Patient), que funciona perfeitamente quando usamos DAOs para acessar explicitamente uma sessão em particular.

Queremos usar o Hibernate para recuperar automaticamente a entidade quando chamamos DoctorBO.getExam().getPatient() Onde o exame contém um id apontando para a tabela Paciente no outro banco de dados.

Uma maneira de fazer isso é usar um UserType personalizado:

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

Qual seria então usado:

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

O UserType funciona - os dados são carregados corretamente apenas com o ID do campo mantido no banco de dados. Eu testei exemplos muito simples de doctor.getExam().getPatient() e doctor.getExam().setPatient() e ambos parecem funcionar muito bem, no entanto acho que isso é um hack terrível e eu não tenho conhecimento adequado do Hibernate para saber se isso é seguro para usar.

Existe uma maneira melhor de alcançar o que queremos? A maneira que descrevi aqui é adequada ou causará dificuldades no futuro?

    
por J Barclay 24.01.2012 в 02:03
fonte

1 resposta

6

Eu não acho que seja uma boa ideia. Você está tentando fazer "como se" tudo estivesse em um único banco de dados, enquanto não é o caso. E você faz "como se" houvesse uma associação real de toOne entre um exame e um paciente, embora não seja uma associação real.

Embora você tenha consciência desse fato, outros desenvolvedores ou desenvolvedores futuros não serão necessariamente, e se perguntará por que não é possível fazer uma consulta como

select e from Exam e left join fetch e.patient

ou

select e from Exam e where e.patient.name like 'Smith%'
Em suma, sua pseudo-associação só cumpre uma parte muito pequena do contrato que uma associação regular oferece, e isso, IMO, causa mais confusão do que conforto.

Nada impede que você tenha um método de utilitário como

Patient getExamPatient(Exam e)

que faz a mesma coisa, mas deixa claro que não há uma associação real entre as duas entidades.

    
por JB Nizet 09.02.2012 / 00:08
fonte