Saltar al contenido

Incluir columnas adicionales en la cláusula Where de Hibernate / JPA Generated UPDATE Query

Es importante interpretar el código bien previamente a aplicarlo a tu proyecto y si tdeseas aportar algo puedes comentarlo.

Solución:

Si entiendo las cosas correctamente, esencialmente lo que está describiendo es que desea que hibernate actúe como si tuviera un primario compuesto key aunque su base de datos tenga un primario de una sola columna key (donde también tiene una columna @Version para realizar un bloqueo optimista).

Estrictamente hablando, no es necesario que su modelo de hibernación coincida exactamente con su esquema de base de datos. Puede definir la entidad para que tenga un primario compuesto key, asegurándose de que todas las actualizaciones se produzcan en función de la combinación de los dos valores. El inconveniente aquí es que sus operaciones de carga son un poco más complicadas.

Considere la siguiente entidad:

@Entity
@Table(name="test_entity", uniqueConstraints =  @UniqueConstraint(columnNames = "id")  )
public class TestEntity implements Serializable 

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", nullable = false, unique = true)
    private Long id;

    @Id
    @Column(name = "col_3", nullable = false)
    private String col_3;

    @Column(name = "value", nullable = true)
    private String value;

    @Version
    @Column(nullable = false)
    private Integer version;

    ... getters & setters


Entonces puedes tener el siguiente método (en mi caso, creé una prueba JUnit simple)

@Test
public void test() 

    TestEntity test = new TestEntity();
    test.setCol_3("col_3_value");
    test.setValue("first-value");

    session.persist(test);

    long id = test.getId();
    session.flush();
    session.clear();

    TestEntity loadedTest = (TestEntity) session
            .createCriteria(TestEntity.class)
            .add(Restrictions.eq("id", id))
            .uniqueResult();

    loadedTest.setValue("new-value");
    session.saveOrUpdate(loadedTest);
    session.flush();


Esto genera las siguientes declaraciones SQL (registro de Hibernate habilitado)

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        test_entity
        (value, version, id, col_3) 
    values
        (?, ?, ?, ?)
Hibernate: 
    select
        this_.id as id1_402_0_,
        this_.col_3 as col_2_402_0_,
        this_.value as value3_402_0_,
        this_.version as version4_402_0_ 
    from
        test_entity this_ 
    where
        this_.id=?
Hibernate: 
    update
        test_entity 
    set
        value=?,
        version=? 
    where
        id=? 
        and col_3=? 
        and version=?

Esto hace que la carga sea un poco más complicada, como puede ver: utilicé un criterio aquí, pero satisface sus criterios, que sus declaraciones de actualización siempre incluyen la columna col_3 en la cláusula ‘where’.

La siguiente solución funciona, sin embargo, te recomiendo que envuelvas tu saveOrUpdate método de una manera que termina utilizando un enfoque más natural. El mío está bien … pero es un poco hacky.

Solución:

Puede crear su propia anotación e inyectar su condición adicional para hibernar el método de guardado utilizando un interceptor de hibernación. Los pasos son los siguientes:

1. Cree una anotación de nivel de clase:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ForcedCondition 
    String columnName() default "";
    String attributeName() default ""; // <-- this one is just in case your DB column differs from your attribute's name

2. Anote su entidad especificando el nombre de la base de datos de la columna y su entidad attribute nombre

@ForcedCondition(columnName = "col_3", attributeName= "col_3")
@Entity
@Table(name="test_entity")
public class TestEntity implements Serializable 

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id", nullable = false, unique = true)
    private Long id;

    @Column(name = "col_3", nullable = false)
    private String col_3;

    public String getCol_3() 
        return col_3;
    
    ... getters & setters


3. Agregue un interceptor de Hibernación e inyecte la condición adicional:

public class ForcedConditionInterceptor extends EmptyInterceptor 
    private boolean forceCondition = false;
    private String columnName;
    private String attributeValue;

    @Override
    public boolean onSave(
            Object entity,
            Serializable id,
            Object[] state,
            String[] propertyNames,
            Type[] types) 
        // If your annotation is present, backup attribute name and value
        if (entity.getClass().isAnnotationPresent(ForcedCondition.class)) 
            // Turn on the flag, so later you'll inject the condition
            forceCondition = true;
            // Extract the values from the annotation
            columnName = entity.getClass().getAnnotation(ForcedCondition.class)).columnName();
            String attributeName = entity.getClass().getAnnotation(ForcedCondition.class)).attributeName();
            // Use Reflection to get the value 
            // org.apache.commons.beanutils.PropertyUtils
            attributeValue = PropertyUtils.getProperty(entity, attributeName);
        
        return super.onSave(entity, id, state, propertyNames, types);
    

    @Override
    public String onPrepareStatement(String sql) 
        if (forceCondition) 
            // inject your extra condition, for better performance try java.util.regex.Pattern
            sql = sql.replace(" where ", " where " + columnName + " = '" + attributeValue.replaceAll("'", "''") + "' AND ");
        
        return super.onPrepareStatement(sql);
     

Después de todo eso, cada vez que llamas entity.save() o session.update(entity) sobre una entidad anotada con @ForcedCondition, el SQL se inyectará con la condición adicional que desee.

POR CIERTO: No probé este código, pero debería ayudarlo en el camino. Si cometí algún error, por favor dímelo para que pueda corregirlo.

valoraciones y reseñas

Si guardas algún atolladero o disposición de ascender nuestro sección puedes escribir una reseña y con placer lo leeremos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *