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.