[JIRA] (HHH-16682) Changes in @JdbcTypeCode(SqlTypes.JSON) are not written to DB
by Christian Thiel (JIRA)
Christian Thiel ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=712020%... ) *updated* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiNTQ5Mjk3ZmZk... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16682?atlOrigin=eyJpIjoiNTQ5Mj... ) HHH-16682 ( https://hibernate.atlassian.net/browse/HHH-16682?atlOrigin=eyJpIjoiNTQ5Mj... ) Changes in @JdbcTypeCode(SqlTypes.JSON) are not written to DB ( https://hibernate.atlassian.net/browse/HHH-16682?atlOrigin=eyJpIjoiNTQ5Mj... )
Change By: Christian Thiel ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=712020%... )
I have a JSON-Mapping in my entity
{code:java}@Entity
public class MyEntity {
@Id
private Long id;
@JdbcTypeCode(SqlTypes.JSON)
private MyJson jsonProperty;
private String info;
}{code}
MyJson is a simple POJO:
{code:java}@Embeddable
public class MyJson {
private String stringProp;
private Long longProp;
}{code}
If i change only the jsonProperty, it seems the entity is _not marked dirty_ → changes are not written to the DB.
If i change any other field-value to (in my test-case the String-field _info_), the entity is marked dirty and all changes are written to DB, even the JSON-changes.
(see _MyEntityTest.java_ in attached Test-project):
{code:java}@QuarkusTest
@Transactional(Transactional.TxType.REQUIRES_NEW)
class MyEntityTest {
@Inject
EntityManager em;
static final long PK = 123L;
static final String CHANGED = "CHANGED";
@Test
void shouldCreateUpdateAndSelectMyEntity() throws Exception {
insert();
findAndUpdate();
selectFiltered();
}
void insert() {
MyEntity myEntity = new MyEntity();
myEntity.setId(PK);
MyJson myJson = new MyJson();
myJson.setLongProp(100L);
myJson.setStringProp("Hello");
myEntity.setJsonProperty(myJson);
em.persist(myEntity);
}
void findAndUpdate() {
MyEntity found = em.find(MyEntity.class, PK);
found.getJsonProperty().setStringProp(CHANGED);
// found.setInfo(CHANGED); // by changing any other property of the entity, it will be marked as dirty and EVERY change will be written to the DB
}
void selectFiltered() {
List<MyEntity> result = em
.createQuery("SELECT e FROM MyEntity e WHERE e.jsonProperty.longProp = :x", MyEntity.class)
.setParameter("x", 100L)
.getResultList();
assertEquals(1, result.size());
assertEquals(CHANGED, result.get(0).getJsonProperty().getStringProp(), "json property not changed");
assertEquals(CHANGED, result.get(0).getInfo(), "plain property not changed");
}
}{code}
see [https://discourse.hibernate.org/t/changes-in-jsonb-field-do-not-mark-enti...]
( https://hibernate.atlassian.net/browse/HHH-16682#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16682#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100225- sha1:2b972a1 )
1 year, 3 months
[JIRA] (HHH-16682) Changes in @JdbcTypeCode(SqlTypes.JSON) are not written to DB
by Christian Thiel (JIRA)
Christian Thiel ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=712020%... ) *created* an issue
Hibernate ORM ( https://hibernate.atlassian.net/browse/HHH?atlOrigin=eyJpIjoiZDk5ZDdlNTI5... ) / Bug ( https://hibernate.atlassian.net/browse/HHH-16682?atlOrigin=eyJpIjoiZDk5ZD... ) HHH-16682 ( https://hibernate.atlassian.net/browse/HHH-16682?atlOrigin=eyJpIjoiZDk5ZD... ) Changes in @JdbcTypeCode(SqlTypes.JSON) are not written to DB ( https://hibernate.atlassian.net/browse/HHH-16682?atlOrigin=eyJpIjoiZDk5ZD... )
Issue Type: Bug Affects Versions: 6.2.1, 6.2.3 Assignee: Unassigned Attachments: code-with-quarkus.zip Components: hibernate-core Created: 24/May/2023 01:51 AM Environment: Quarkus 3.0.3 (integrates hibernate-orm 6.2.1-final)
Quarkus 3 SNAPHSOT with hibernate-orm 6.2.3 (see https://github.com/Sanne/quarkus/tree/ORM6) Priority: Major Reporter: Christian Thiel ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=712020%... )
I have a JSON-Mapping in my entity
@Entity
public class MyEntity {
@Id
private Long id;
@JdbcTypeCode(SqlTypes.JSON)
private MyJson jsonProperty;
private String info;
}
MyJson is a simple POJO:
@Embeddable
public class MyJson {
private String stringProp;
private Long longProp;
}
If i change only the jsonProperty, it seems the entity is not marked dirty → changes are not written to the DB.
If i change any other field-value to (in my test-case the String-field info ), the entity is marked dirty and all changes are written to DB, even the JSON-changes.
(see MyEntityTest.java in attached Test-project):
@QuarkusTest
@Transactional(Transactional.TxType.REQUIRES_NEW)
class MyEntityTest {
@Inject
EntityManager em;
static final long PK = 123L;
static final String CHANGED = "CHANGED" ;
@Test
void shouldCreateUpdateAndSelectMyEntity() throws Exception {
insert();
findAndUpdate();
selectFiltered();
}
void insert() {
MyEntity myEntity = new MyEntity();
myEntity.setId(PK);
MyJson myJson = new MyJson();
myJson.setLongProp(100L);
myJson.setStringProp( "Hello" );
myEntity.setJsonProperty(myJson);
em.persist(myEntity);
}
void findAndUpdate() {
MyEntity found = em.find(MyEntity.class, PK);
found.getJsonProperty().setStringProp(CHANGED);
// found.setInfo(CHANGED); // by changing any other property of the entity, it will be marked as dirty and EVERY change will be written to the DB
}
void selectFiltered() {
List<MyEntity> result = em
.createQuery( "SELECT e FROM MyEntity e WHERE e.jsonProperty.longProp = :x" , MyEntity.class)
.setParameter( "x" , 100L)
.getResultList();
assertEquals(1, result.size());
assertEquals(CHANGED, result.get(0).getJsonProperty().getStringProp(), "json property not changed" );
assertEquals(CHANGED, result.get(0).getInfo(), "plain property not changed" );
}
}
( https://hibernate.atlassian.net/browse/HHH-16682#add-comment?atlOrigin=ey... ) Add Comment ( https://hibernate.atlassian.net/browse/HHH-16682#add-comment?atlOrigin=ey... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100225- sha1:2b972a1 )
1 year, 3 months
[JIRA] (HV-1949) Nondeterministic behavior of HibernateValidator (in springboot?) when both annotation and xml-based definition are used
by martin mucha (JIRA)
martin mucha ( https://hibernate.atlassian.net/secure/ViewProfile.jspa?accountId=70121%3... ) *commented* on HV-1949 ( https://hibernate.atlassian.net/browse/HV-1949?atlOrigin=eyJpIjoiOGFhZTg3... )
Re: Nondeterministic behavior of HibernateValidator (in springboot?) when both annotation and xml-based definition are used ( https://hibernate.atlassian.net/browse/HV-1949?atlOrigin=eyJpIjoiOGFhZTg3... )
So far I have no luck with this.
* When I attempt quick fix with touching ConstraintValidator during startup to be initialized on working path it works, but ONLY for that specific validator. So in order for this to work I’d need to correctly know all available validators across whole module tree discoverable via ServiceLoader, make fake class to init them all, and hope everyone who will potentially adds new validator in future anywhere will know about this, and will also update my workaround. Maybe for me and my <20 validators and theoretically full control over everything it’s doable, but generally not viable soluion.
2. Then I tried to used programmatic validators definition(validators on fields/properties instead of xml declaration) while still using service loader to declare custom ConstraintValidator. Won’t work (new to programmatic approach) as ServiceLoader declaration does not apply for programmatic ConstraintValidator definition. So this is also dead end.
3. Then I tried both programmatic validators definition(instead of xml) and programmatic validators declaration. And this one does not work either. Since we do have the validators implemented in library which already uses ServiceLoader, which is used by several apps. So again, maybe it’s somehow doable, but generally no, we cannot make the ServiceLoader file go away, it is used already in public library, I cannot easily replace it. So what I did here, that I read ServiceLoader file myself and fed it into programmatic declaration of pairing, to make sure it’s there when needed. Which is this beauty:
@Beanpublic static LocalValidatorFactoryBean defaultValidator(ApplicationContext applicationContext) {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean() {
@Override protected void postProcessConfiguration(javax.validation.Configuration<?> configuration) {
super.postProcessConfiguration(configuration);
if (configuration instanceof HibernateValidatorConfiguration hvc) {
ConstraintMapping constraintMapping = hvc.createConstraintMapping();
ServiceLoader.load(ConstraintValidator.class).forEach(cv->{
Arrays.stream(cv.getClass().getGenericInterfaces())
.filter(e -> e instanceof ParameterizedType)
.map(e -> (ParameterizedType) e)
.filter(e -> e.getRawType().equals(ConstraintValidator.class))
.map(ParameterizedType::getActualTypeArguments)
.forEach(aa -> {
Type annotationType = aa[0];
Class annotationClass = (Class<?>) annotationType;
Class constraintValidatorClass = cv.getClass();
System.out.println(String.format("registering %s", constraintValidatorClass));
registerConstraint(constraintMapping, annotationClass, constraintValidatorClass);
System.out.println(String.format("%s registered", constraintValidatorClass));
});
});
//registering instead of xml
constraintMapping.type( mwe.validatorfail.dto.SampleDTO.class )
.getter( "uuid" )
.constraint( new GenericConstraintDef<>(AnyUuid.class) )
.constraint( new NotNullDef() );
hvc.addMapping(constraintMapping);
} else {
throw new IllegalStateException("Should not happen, we explicitly depend on HibernateValidator ValidationProvider");
}
}
};
factoryBean.setProviderClass(HibernateValidator.class);
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory(applicationContext);
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
public static <A extends Annotation> void registerConstraint(ConstraintMapping constraintMapping, Class<A> annotation,
Class<? extends ConstraintValidator<A, ?>> validator) {
constraintMapping.constraintDefinition(annotation)
.includeExistingValidators(false)
.validatedBy(validator);
}
which will produce this error on startup:
Caused by: javax.validation.ValidationException: HV000193: mwe.validatorfail.validation.AnyUuid is configured more than once via the programmatic constraint definition API.
Which is kinda funny. This code registers AnyUuid ONLY ONCE. So if this code does not run, validator for AnyUuid does not exist, if it does run it creates duplicity. Which means, that HibernateValidator after initialization is in ambivalent state, where it does have and does not have validator declared at the same time.
So unless I’m doing something wrong, this is not a viable workaround either, since ServiceLoader and manual registration of validators cannot coexist. For me it might be doable, since I theoretically have full control over all apps using given library and can rewrite everything to avoid wrong parts of HibernateValidator, but generally it’s not viable workaround.
Is there a another way, how we can keep ServiceLoader in place (we might not be able to rewrite public libraries depending on it) and somehow add non-annotation-based validations, which will work with validators defined in ServiceLoader? What is motivation behind it: we want to use annotation where possible, but some sources, like generated stubs, does not allow us (without nasty tricks which even might not be available everytime) to add annotation.
( https://hibernate.atlassian.net/browse/HV-1949#add-comment?atlOrigin=eyJp... ) Add Comment ( https://hibernate.atlassian.net/browse/HV-1949#add-comment?atlOrigin=eyJp... )
Get Jira notifications on your phone! Download the Jira Cloud app for Android ( https://play.google.com/store/apps/details?id=com.atlassian.android.jira.... ) or iOS ( https://itunes.apple.com/app/apple-store/id1006972087?pt=696495&ct=EmailN... ) This message was sent by Atlassian Jira (v1001.0.0-SNAPSHOT#100225- sha1:2b972a1 )
1 year, 3 months