Load Spring configuration in Hibernate entity - spring

I want to configure Hibernate to set maximum length for a VARCHAR field. This maximum length is defined in a configuration file, and this configuration file is loaded by a class ValidationConfiguration.
This is my entity:
#Entity
public class MyEntity{
#Autowired /*I know this is bad practice,
I just want to let readers know that this object is instantiated.*/
private ValidationConfiguration config;
#Column(length = config.getMaxLength()) /*This gives a "java: element value
must be a constant expression"*/
String description;
//Get and set
}
Is this possible? If not, are there any workarounds?

From your code, it is clearly visible, that You are just defining the ValidationConfiguration by private ValidationConfiguration config;
But You are not Instantiating the Object.
So, Instantiate the object like new ValidationConfiguration() and since you haven't shared the code of ValidationConfiguration , i am predicting that your method is getMaxLength() not static. If the problem persists, do share the ValidationConfiguration code.
If that is a bean, then you can autowire it simply and don't create new instantiation.

Related

validate ConfigurationProperties mapping

Is there a way to validate application.properties (or yml) if the properties match Java bean that it is mapped to via #ConfigurationProperties - so that if there is a typo in an attribute, exception will be thrown?
I tried using #Validated but it works only if every property has #NotNull annotation - but this is not exactly what I want to achieve... there may be some nullable properties in the config and I still want to "validate" them
I just spent 2 hours debugging an issue and I found out, the problem is that I misspelled an attribute name
e.g. application.yml
property1: 1
properrrrrty2: 2
#Configuration
#ConfigurationProperties
public class AppConfig {
private String property1;
private String property2; // <--- this property does not match due to typo in application.yml
}
A)
If you want to be sure that always a property exists then use #Validated with #NotNull for that property. #NotNull will complain if it does not find that property. You can still have the property there with an empty value if that is what you mean with nullable properties and NotNull will not complain.
You can't say I want it to be able to be nullable but also the validator should complain when that property is null.
So to sum things up.
#NotEmpty property must exist and also not have an empty value
#NotNull property must just exist. It does not care if it exists with an empty value.
That's why I insist you go with NotNull for your requirements.
B)
Also I can think of another way to handle that.
#Component
public class AppConfig {
#Value("${property1}")
private String property1;
#Value("${property2}")
private String property2;
}
Using injection with #Value, spring will fail to initialize the singleton AppConfig during application startup if some property with exactly the same name does not exist on properties file, therefore you will be informed that no property with that name exists and the application will not start up.
You can specify ignoreUnknownFields = false to ensure that no unknown properties are defined under the corresponding prefix. (docs):
Flag to indicate that when binding to this object unknown fields should be ignored. An unknown field could be a sign of a mistake in the Properties.
Borrowing from your example:
#Configuration
#ConfigurationProperties(prefix = "myapp", ignoreUnknownFields = false)
public class AppConfig {
private String property1;
private String property2;
}
This means myapp.property1 and myapp.property2 are allowed but not required to be set, so they remain nullable.
Any other set property with the myapp prefix (such as myapp.properrrrrty2=2) will cause a startup failure and the offending property name will be logged in the exception.

Service cannot auto-wire in Entity class

I needed a RoleMappingService class(which is annotated by #Service) object into a Employee class (which is annotated by #Entity)
below are my classes
********************* RoleMappingsService class **********************
#Service
public class RoleMappingsService {
#Autowired
RolesMappingDao rolesMappingDao;
public List<RolesMappings> getRolesMappingByauthSystemRole(String authSystemRole) {
return rolesMappingDao.getRolesMappingByauthSystemRole(authSystemRole);
}
}
############### Employee class
#Configurable
#Component
#Entity
#NamedQuery(name = "Employee.findAll", query = "SELECT e FROM Employee e")
public class Employee implements Serializable, UserDetails {
#Autowired
#Transient
RoleMappingsService roleMappingsService;
public static final String STATUS_ACTIVE = "ACTIVE";
public static final String STATUS_INACTIVE = "INACTIVE";
public static final String STATUS_LOCKED = "LOCKED";
public static final String STATUS_ONLEAVE = "ONLEAVE";
public static final String STATUS_EXPIRED = "EXPIRED";
private static final long serialVersionUID = 1L;
#Id
#Column(name = "emp_id")
private String empId;
#Column(name = "emp_password")
private String empPassword;
#Column(name = "emp_email")
private String empEmail;
#Column(name = "emp_address")
private String empAddress;
#Column(name = "emp_age")
private int empAge;
#Column(name = "emp_firstname")
private String empFirstname;
}
Here Autowire is not working for roleMappingsService and the object is always found null. However I tried to autowire same object in some other service and there Autowire is perfectly working.
( I know Entity class is only used for representing database table but in my case I need to set some field values which depend on another table so need to fetch data using service)
JB Nizet is totally right
I'll try to provide more explanations here.
Spring can Autowire only beans, objects that it manages, and not arbitrary objects.
Entities are usually created from within a JPA (Hibernate) and are not something that you want to manage by Spring.
There is a related discussion available here but bottom line you should never do something like this.
Why not?
Here are a couple of questions/reasons:
Maybe these entities will go outside spring context at all (serialization), what should that reference contain? Should we also serialize the service? How?
What will happen if the method that turns to the service will be called "outside" the spring driven application (maybe even in different JVM)?
If there are, say 1000 objects returned by that query, do you really want all of them to reside in application context? Or maybe should they be of "prototype" scope?
As you see, it doesn't play nice with spring concepts. I think the reason for it is that Hibernate and JPA do not "support" an idea of methods inside entities, it's just a different framework. I know there are other frameworks that do allow such a concept, but Hibernate/JPA just doesn't, period
So instead of trying to inject the service into the entity bean, probably you should redesign the application so that the service method will be called from outside, maybe via some facade, and entities will be just populated by query, and then "enriched" with additional information if we're talking about SELECT queries, or, alternatively, some information should be pre-set on entity objects, generated by the Business Logic Layer and only then the entity object should be stored in DB

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.

How do I add an interceptor to save/fetch methods of Spring's JPARepository to update transient values?

First let me say I'm a complete novice with Spring AOP, and I apologize if this is a duplicate question.
Here's my scenario:
Let's say I have the following domain class:
#Entity(name="MyTable")
#Table(name="MY_TABLE")
public class MyTable {
private static final long serialVersionUID = 1234567890123456L;
#Id
#Column(name = "USER_ID")
private Long userID;
#Transient
private String key;
#Column(name = "KEY")
private String secureKey;
/* Other columns */
/* Getters and Setters */
}
and I have the following JPARepository class to manage it:
#Repository
public interface MyTableRepository extends JpaRepository<MyTable, Long> {
/* findBy methods */
}
As you can see, I have a secureKey field and a transient key field. In this case secureKey is an encrypted version of key.
What I need is for the secureKey value to be populated before a domain object is saved, and for the key value to be populated after a domain object is fetched. (This is a trivial example but in the real case I have multiple transient and encrypted values.) The idea is for the secure values to be persisted to the DB, but users of the domain class will only need to work with the "insecure" values.
Currently I'm handling this in my service layer. After I call a fetch method I'm populating the transient values, and before calling a save method I'm populating the "secure" values. This is working as expected but ideally I'd like this to be managed transparently, because now the burden is on each developer to remember to update those values after fetching or before saving.
I'm assuming the best way to handle this would be through some AOP class, but I confess I have little to no idea where to begin there. Is this a common scenario, and if so, would someone be willing to point me in the right direction? Also, if you have a suggestion for a better way to implement this decrypted/encrypted field pair scenario, please let me know.
Ideally I'd like to be able to add an annotation to both the secure and insecure fields, maybe pointing to each other, maybe something like:
#Insecure(secureValue = "secureKey")
#Transient
private String key;
#Secure(insecureValue = "key")
#Column(name = "KEY")
private String secureKey;
Any assistance you could provide is most appreciated.
Thanks,
B.J.
I think Spring AOP isn't the correct technology in your use case, i would recommend to use EntityListeners.
Hibernate: https://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/listeners.html
Eclipselink: https://wiki.eclipse.org/EclipseLink/Release/2.5/JPA21#CDI_Entity_Listeners

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