quarkus and hibernate mapping (field access) - quarkus

I moved an entity which worked in JEE wildfly to quarkus:
#Entity
#Getter
#Setter
public class Item {
#Column(name = "archived")
private OffsetDateTime archived;
public boolean isArchived() {
return archived != null;
}
}
After running in dev-mode, I get this error:
Caused by: org.hibernate.MappingException:
In trying to locate getter for property [archived],
Class [com.Item]
defined both a `get` [public java.time.OffsetDateTime com.Item.getArchived()]
and `is` [public boolean com.Item.isArchived()] variant
at org.hibernate.internal.util.ReflectHelper.checkGetAndIsVariants(ReflectHelper.java:538)
at org.hibernate.internal.util.ReflectHelper.verifyNoGetVariantExists(ReflectHelper.java:562)
at org.hibernate.internal.util.ReflectHelper.getGetterOrNull(ReflectHelper.java:502)
at org.hibernate.internal.util.ReflectHelper.findGetterMethod(ReflectHelper.java:424)
at org.hibernate.internal.util.ReflectHelper.getterMethodOrNull(ReflectHelper.java:571)
at org.hibernate.property.access.internal.PropertyAccessMixedImpl.getAccessType(PropertyAccessMixedImpl.java:97)
at org.hibernate.property.access.internal.PropertyAccessMixedImpl.<init>(PropertyAccessMixedImpl.java:47)
at org.hibernate.property.access.internal.PropertyAccessEnhancedImpl.<init>(PropertyAccessEnhancedImpl.java:28)
at org.hibernate.property.access.internal.PropertyAccessStrategyEnhancedImpl.buildPropertyAccess(PropertyAccessStrategyEnhancedImpl.java:27)
at org.hibernate.mapping.Property.getGetter(Property.java:311)
at org.hibernate.tuple.entity.PojoEntityTuplizer.buildPropertyGetter(PojoEntityTuplizer.java:255)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.<init>(AbstractEntityTuplizer.java:142)
at org.hibernate.tuple.entity.PojoEntityTuplizer.<init>(PojoEntityTuplizer.java:59)
... 35 more
I understand the error but I dont know why does this hibernate mapping work in wildfly and not in quarkus? As I declared jpa field access instead of jpa property access, hibernate should see the isArchived() automatically as transient.
Or does quarkus compiles all these field-injections to property-injections?

Quarkus generates a getter for the archived field.
Here Hibernate complains that you have two accessors for the archived field: isArchived() and getArchived().
This is an Hibernate issue/limitation, nothing specific to Quarkus.
I think the best is to rename your isArchived() method as Hibernate don't know which method to use to retrieve the value of the archivedfield. And if you add #Transient to your isArchived() method it could (depending on wich method it get first) handle your field as transient.

Related

Hibernate Upgrade to v6 (Spring Boot 3) - "Named type [interface java.util.List] did not implement BasicType nor UserType"

After upgrading to Spring Boot 3 / Hibernate 6, I am getting exceptions during Spring application / test startup.
java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration#15ec3c0c testClass = ....
Caused by: jakarta.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is java.lang.IllegalArgumentException: Named type [interface java.util.List] did not implement BasicType nor UserType
Problem can be traced / debugged down to a List<String> entity property, which is using an AttributeConverter for storing it as a comma-separated string in the (MySQL) DB (TEXT field).
The converter:
#Converter
class StringListToStringConverter : AttributeConverter<List<String>, String> {
....
}
The entity:
#MappedSuperclass
abstract class MyInstance<T>(
...
#Column(name = "iface_ids", columnDefinition = "TEXT")
#Convert(converter = StringListToStringConverter::class)
var interfaceIds: List<String> = emptyList()
...
)
This has been working nicely before with Spring Boot 2.7.x / Hibernate 5.x.
Do you think there is another solution than writing a custom type (and thus removing/replacing the AttributeConverter) in this case?
Kind Regards,
dom
Expecting it to actually work OOTB, the AttributeConverter solution looks so clean&simple for this purpose compared to a custom type implementation that I don't really want to change that.

NPE from Jackson trying to serializing a field that does not exists?

Here is my simple bean
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
public class Foo {
private String firstName;
private String lastName;
public String getFullName(){
return firstName + lastName;
}
}
when this object gets serialized in Spring-boot controller with Jackson,
I get the following error
j.l.NullPointerException: null
com.example.foobar.foo.getFullName(Foo.java:28)
s.r.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java)
s.r.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
s.r.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
j.l.reflect.Method.invoke(Method.java:498)
c.f.j.d.s.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:653)
c.f.j.d.s.s.BeanSerializerBase.serializeFields(BeanSerializerBase.java:690)..
28 common frames omitted\nWrapped by: c.f.j.d.JsonMappingException: (was java.lang.NullPointerException)
(through reference chain: com.example.foobar.foo[\"fullName\"])
c.f.j.d.JsonMappingException.wrapWithPath(JsonMappingException.java:379)
c.f.j.d.JsonMappingException.wrapWithPath(JsonMappingException.java:339)
c.f.j.d.s.s.StdSerializer.wrapAndThrow(StdSerializer.java:343)
c.f.j.d.s.s.BeanSerializerBase.serializeFields(BeanSerializerBase.java:698)
c.f.j.d.s.BeanSerializer.serialize(BeanSerializer.java:155)
c.f.j.d.s.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:292)
c.f.j.d.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1419)
c.f.j.d.ObjectWriter.writeValue(ObjectWriter.java:940)
o.s.h.c.j.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:267)...
23 common frames omitted\nWrapped by: o.s.h.c.HttpMessageNotWritableException: Could not write content: (was java.lang.NullPointerException) (through reference chain: com.example.foobar.foo[\"fullName\"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: com.example.foobar.foo[\"fullName\"])
o.s.h.c.j.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:274 ..."
Here is the requestBody that I sent
{"firstName": "foo",
"lastName: null
}
Is Jackson trying to serialize fullName property ? but I have no such field declared. is this expected behavior? I am unable to find documentation that supports this behavior.
Thanks
Your guess is right, Jackson is trying to find the field name by its getter method, which it cannot find. And hence NPE.
Different solutions
use #JsonIgnore on the getFullName method.
you can disable this feature by setting this property in spring boot application.properties file
spring.jackson.mapper.use-getters-as-setters=false
If you wish to do it with the java code (If not using spring boot), you can do it with a bean declaration like this
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.featuresToDisable(MapperFeature.USE_GETTERS_AS_SETTERS);
return builder;
}

Spring data and hibernate - model validations - exception translation

I use spring-data and hibernate. Now I would like to apply some validation to my model. In most cases I would like to apply simple validation like null-checking etc. But in some cases I would like to apply more strict validation, such as email-validation. I found very useful feature in Hibernate validator - the #Email annotation. It works very well but here is the problem:
If i try to save a model with null value, then the following exception is thrown:
org.springframework.dao.DataIntegrityViolationException
But if I try to save a model with non-null but non-email value (let's say asdfgh), then the following exception is thrown:
javax.validation.ConstraintViolationException
I would love to see only one type of exception in both cases, because in both cases the model didn't pass the validation and I would like just to worry about only one exception type in my exception-handling code.
I tried to add PersistenceExceptionTranslationPostProcessor to my bean configuration, but it looks like it does not change anything.
Do you have an idea how to "unify" this exceptions?
Model:
#Entity
public class ValidationModel {
...
#Email
#Column(nullable = false)
private String email;
...
}
Repository:
public interface ValidationModelRepository extends JpaRepository<ValidationModel, Long> {
}
#Column(nullable = false) is not a validation check. It's a JPA constraint.
To validate that a value is not null, use #NotNull.

Using #Configurable domain object properties to assign a specific behaviour based on data

I have a domain object which looks like this.
i need to use the data fetched from the database ("type" in this example) to fetch and inject the correct type of service.
I get this output which means that the DB data are not set during the call :
entity is a bean postconstruct: PocProduct [id=null, type=null, productName=null].. attching behavior!
I get the same resilt when I try with the initializing Bean.
What is the correct way to configure this?
#Entity
#Table(name = "AAA_POC_PROD")
#Configurable(dependencyCheck = true)
#Scope("prototype")
public class PocProduct implements Serializable, InitializingBean {
/**
*
*/
private static final long serialVersionUID = 1136936011238094989L;
#Id
private String id;
private String type;
private String productName;
#Transient
private Behaviour behaviour;
#Transient
#Autowired
private BehaviourFactory behaviourFactory;
//getters and setters
#PostConstruct
public void attachBehavior() {
System.out.println("entity is a bean postconstruct: " + this + ".. attching behavior!");
//Need to call this : depends on type which is fetched from DB
// this.behaviour = behaviourFactory.getTypeBasedBehaviour(type);
}
}
Configurable beans are initialized by Spring after or before construction, depending on the value of the #Configurable.preConstruction attribute. When loading an entity from a database this means the following sequence of events:
The JPA provider creates the entity by invoking it's constructor via reflection
While the constructor executes, spring-aspects' AnnotationBeanConfigurerAspect intercepts the constructor execution and, before (or after) the constructor executes, Spring will configure this newly created object by executing any bean configuration you have in your spring context, including autowiring of properties.
The JPA provider will receive this object already configured by Spring and will start populating its persistent properties with data fetched from the DB.
Optionally, if you set up #PostLoad methods, the JPA provider will invoke these methods so that your entities have a chance to do work after the entity is fully populated by data from the DB.
From what I see you're trying to do, this 4th step is where you should put your custom behavior logic, assuming everything else is working properly.

Jpa + Spring - automatically setting transient field value after read from DB

what's the best solution to set a value for a field marked #Transient after the entity has been read from the data source?
I'm using EclipseLink and I'm trying the DescriptorEventAdapter with his postBuild event solution because I need also to get the default value using a Spring bean (obviuosly using DI), but I would know if there is any simpler solution that I'm missing.
Thanks in advance
Here's the simple approach if you're using a repository or DAO:
#Repository
class YourRepository {
#Autowired
private Bean bean;
#PersistenceContext
private EntityManager entityManager;
#Transactional(readOnly = true)
public YourEntity find(..) {
YourEntity entity = lookupUsingEntityManager();
entity.transientField = bean.getDefaultValue();
return entity;
}
}
Here's another approach if you are using active record -style entities:
#Entity
class YourEntity {
#Transient
public Object field;
#PostLoad
public void populateField() {
field = new BeanHolder().bean.getDefaultValueForField();
}
#Configurable
private static class BeanHolder {
#Autowired private Bean bean;
}
}
Mind the semi-pseudo-code. Note that the latter approach works only if you use compile- or load-time AspectJ weaving with <context:spring-configured />.
You got entity which has transient field and the value is always taken from service using DI?
What is the purpose of the field? It's used for some calculation within any entity method?
Such calculation should probably use service's method to obtain the value.
As value from any service is used, I'm not sure whether such calculation (method) belong into entity.
Note that entity and service has completely different lifecycle. The value is changing in the time so it does not make the sense to supply the value in entity's factory at the beginning of it's life?

Resources