Automatic filling entity properties - java

I have some type an architect(?) question
I develop an application, based on Spring and Hibernate (annotated configuration)
For each table in my database I added 4 fields: createdBy and modifiedBy(String), created and modified (Datetime). Respectively each entity class also has this fields and getter/setter pairs. So I want to find best practice solution for filling this fields instead adding for each DAO extra code. Is it possible?
I'll be glad to any proposal

Certainly. Just add this code to a base class for all your persistent instances and enable annotation processing:
#PrePersist
public void prePersist()
{
if (created == null)
{
created = updated = createCurrentTimestamp();
createdBy = updatedBy = CurrentUser.get();
}
}
#PreUpdate
public void preUpdate()
{
updated = createCurrentTimestamp();
updatedBy = CurrentUser.get();
}
public static java.sql.Timestamp createCurrentTimestamp ()
{
final long now = System.currentTimeMillis();
final java.sql.Timestamp ts = new java.sql.Timestamp (now);
ts.setNanos(((int)(now % 1000)) * 1000000);
return ts;
}
CurrentUser is a ThreadLocal<String> which allows me to specify at the start of an operation which user started it. This way, any object that gets touched will contain the correct information.
Without annotation processing, activate the respective options in your HBM file.

Look at Spring AOP.
You can assign an "interceptor" for your DAO methods, so that the objects are first handled by the interceptor, and then the execution proceeds to the DAO methods.
In the interceptor you can fill the objects with the data you need.

One possibility would be to define a Hibernate EventListener which can fill in these fields just before each entity is flushed to the database

Related

Intercept repository method calls in Spring Data, refining the query on the fly

Say I've got a few interfaces extending CRUDRepositor. There are methods in there like findByField. Some of these methods should only return entities that belong to a group of entities to which the user has access (the group is a column in the database, so it's a field that's defined for most entities). I want to achieve this by allowing the use of annotations (like #Protected) on the repository methods, and then when these methods are called instead of calling findByField a method findByFieldAndGroup is called behind the scenes. With the use of AOP (which intercepts methods annotated with my #Protected tag) the group can be assigned before the method is effectively executed.
public interface MyRepository extends CRUDRepository<MyEntity,long> {
#Protected
Optional<MyEntity> findById(Long id); // Should become findByIdAndGroup(Long id, String group) behind the scenes
#Protected
Collection<MyEntity> findAll();
}
Is there a way to achieve this? In the worst case I either add all the methods manually, or completely switch to a query by example approach (where you can more easily add the group dynamically) or generate methods with a Java agent using ASM (manipulating the bytecode) ... but these are much less practical approaches which demand a good deal of refactoring.
Edit : found these relevant questions Spring data jpa - modifying query before execution
Spring Data JPA and spring-security: filter on database level (especially for paging)
Other relevant references include this ticket on GitHub (no progress, only a sort-of-solution with QueryDSL which precludes the use of queries based on method names) and this thread.
You can use filters, a specific Hibernate feature, for this problem.
The idea is the following.
First, you need to annotate your entity with the different filters you want to apply, in your case, something like:
#Entity
//...
#Filters({
#Filter(name="filterByGroup", condition="group_id = :group_id")
})
public class MyEntity implements Serializable {
// ...
}
Then, you need access to the underlying EntityManager because you need to interact with the associated Hibernate Session. You have several ways to do this. For example, you can define a custom transaction manager for the task, something like:
public class FilterAwareJpaTransactionManager extends JpaTransactionManager {
#Override
protected EntityManager createEntityManagerForTransaction() {
final EntityManager entityManager = super.createEntityManagerForTransaction();
// Get access to the underlying Session object
final Session session = entityManager.unwrap(Session.class);
// Enable filter
try{
this.enableFilterByGroup(session);
}catch (Throwable t){
// Handle exception as you consider appropriate
t.printStackTrace();
}
return entityManager;
}
private void enableFilterByGroup(final Session session){
final String group = this.getGroup();
if (group == null) {
// Consider logging the problem
return;
}
session
.enableFilter("filterByGroup")
.setParameter("group_id", group)
;
}
private String getGroup() {
// You need access to the user information. For instance, in the case of Spring Security you can try:
final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return null;
}
// Your user type
MyUser user = (MyUser)authentication.getPrincipal();
String group = user.getGroup();
return group;
}
}
Then, register this TransationManager in your database configuration instead of the default JpaTransactionManager:
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new FilterAwareJpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory());
return transactionManager;
}
You can also have access to the EntityManager and associated Session by creating a custom JpaRepository or by injecting #PersistenceContext in your beans, but I think the above-mentioned approach is the simpler one although it has the drawback of being always applied.

How is the better solution to replace my code, when I think i can access service from entity in Spring?

I have a problem to access service in my entity. I know, besides my code doesn't work, it's also not recomended. So, i want to know what is the best practice if i have the problem like this? Here are my class.
The Controller Class:
#Controller
#RequestMapping("step")
public class TenderController {
#Autowired
StepService stepService;
#GetMapping("")
public ModelAndView index(ModelAndView mView,
#ModelAttribute(name = "result_code") String result_code,
#ModelAttribute(name = "result_message") String result_message) {
mView.addObject("stepList", stepService.getAllSteps());
mView.setViewName("pages/step/index");
return mView;
}
}
On my view html, I iterate the stepList
<tr th:each="s:${stepList}"
th:classappend="${s?.isStepNow()?'bg-success':''}">
<!-- some td -->
</tr>
The problem is, for some reason, i have to use if else condition to get the current date to use in isStepNow() method. One from operating system. The other one from the database. So, i come up with an idea to create a service class
Here are the Service Class :
public interface TimeServices {
Date getNow();
}
and The Implementation Class:
#Service
public class TimeServicesImpl implements TimeServices {
#Value("${app.mode}")
String appMode;
#Autowired
DateDBRepository dateDBRepository;
#Override
public Date getNow() {
if(appMode.equalsIgnoreCase("GET_FROM_DB")){
Optional<DateDB> dateDBOptional = dateDBRepository.findById(1L);
if(dateDBOptional.isPresent()){
return dateDBOptional.get().getDate();
}else{
throw new IdNotExistsException();
}
}else{
return new Date();
}
}
}
The Problem is in my Entity:
#Entity
#Table(name = "step")
public class Step{
#Autowired
#Transient
TimeServices timeServices; //BAD PRACTICE AND DOESN'T WORK
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Date start;
private Date end;
public Boolean isStepNow(){
Date now = timeServices.getNow(); //THE PROBLEM
if(now.compareTo(start)>0 && end.compareTo(now)>0) {
return true;
}else{
return false;
}
}
}
Of course it doesn't work, because the timeService is always null. Anyone have some recommendation for me to solve this problem?
I know i can edit my isStepNow() to isStepNow(Date date). Then, i can access the service via controller. So i can call the isStepNow(date) on my view. But, i think it's not efficient in writing source code because i have to access the service from some controllers rather than only write it one time in the entity.
I've seen this discussion a lot.
People using DDD tend to solve it as follows:
Rename your #Entity-annotated class to StepEntity or ORMStep something similar, and only keep the fields needed to do ORM in that class.
Create a different (domain) class Step that you create using a ORMStep and dependent services, and put your domain logic methods in that class.
Let the StepService interface (better call it StepRepository) return Step class, not ORMStep.
Implement the StepRepository by injecting both the DAO (which Spring Data confusingly also calls Repository) and the dependent services, and combine them to read ORMSteps and convert to Step classes.
This seems like a lot of effort, and you probably need to convert the Step instances back to ORMStep classes too to do updates, but in the long run it's a very clean solution. You can evolve the Step classes independently of the ORM classes or switch the ORM without having to change the controller etc.
It's also TDD-friendly, since all business logic is in the domain objects, not in the ORM objects, so you can unit test them much easier.
If the classes you use have a lot of fields, MapStruct and/or Lombok Builders can keep your code cleaner.

Spring Data domain events go missing (?)

I am having trouble publishing events from an aggregate-root in a Spring Boot application. What I basically want is to publish an "Update" event every time some information about a person is changed.
The code for this is pretty straightforward:
#Entity
public class Person {
#Transient
private final Collection<AbstractPersonRelatedEvent> events = new ArrayList<>();
Person(Person other) {
// copy other fields
other.events.foreach(events::add);
}
// other stuff
public Person updateInformation(...) {
Person updated = new Person(this);
// setting new data on the updated person
if (!hasUpdateEventRegistered()) {
updated.registerEvent(PersonDataUpdatedEvent.forPerson(updated));
}
return updated;
}
void registerEvent(AbstractPersonRelatedEvent event) {
events.add(event);
}
#DomainEvents
Collection<AbstractPersonRelatedEvent> getModificationEvents() {
return Collections.unmodifiableCollection(events);
}
#AfterDomainEventPublication
void clearEvents() {
events.clear();
}
}
I am managing Person instances through a manager:
#Service
#Transactional
class PersistentPersonManager implements PersonManager {
// other methods are omitted
#Override
public Person save(Person person) {
return personRepository.save(person);
}
}
However when I call the manager (manager.save(person.updateInformation(...)) the events seem to go "missing":
upon calling the save() method all events are still present but when Spring invokes getModificationEvents() the collection is empty. The events seem to have vanished somewhere in between (with only Spring-code being executed).
As this is pretty basic, I must be missing something essential but got stuck in a rut.
So how do I get back on track here?
I assume you are using JPA here.
For JPA the save operation actually does a merge on the JPA EnityManager.
For a detached entity merge loads/finds the entity with the same id from the database or the current session and copies all the (changed) fields over. This does ignore transient fields like the events.
You are dealing with detached entities because you are creating a new entity every time you call updateInformation.
So here is what is happening:
You load an entity (e1) from the database. It does not have any events registered.
By calling updateInformation you create a new detached entity (e2). You also register events with e2.
When calling save JPA finds the matching e1 and copies all changes from e2 into it, except the events. So e1 still has no events registered.
Events get triggered, but there aren't any because only e1 is used.
In order to fix this: Do not create new instances of the entity in updateInformation.

How does Java implements the methods declared in the Annotations (Metadata)?

An image taken from a book which I am going through,
The caption says it all. Please suggest or give me something as to what happens behind the scenes.
For example, how does #NotNull in Hibernate Bean Validation API works?
I know that through Reflection API, we can do something like this,
class Meta {
// Annotate a method.
#MyAnno(str = "Annotation Example", val = 100)
public static void myMeth() {
Meta ob = new Meta();
// Obtain the annotation for this method
// and display the values of the members.
try {
// First, get a Class object that represents
// this class.
Class c = ob.getClass();
// Now, get a Method object that represents
// this method.
Method m = c.getMethod("myMeth");
// Next, get the annotation for this class.
MyAnno anno = m.getAnnotation(MyAnno.class);
// Finally, display the values.
System.out.println(anno.str() + " " + anno.val());
} catch (NoSuchMethodException exc) {
System.out.println("Method Not Found.");
}
}
public static void main(String args[]) {
myMeth();
}
}
Annotations don't have any implemented code and actually don't do anything themself.
To make them "work", there should be some kind of annotation processor (initializer, loader or any class that works with annotated objects). This annotation processor checks annotation objects annotations and changes the way it is handled.
For example Spring annotation processor, when initializing an object, looks for #Autowired fields, to fill autowired fields.
Same goes for Hibernates #NotNull. it doesn't do anything actually. However, Hibernate, when persisting your object, checks if there should be something there.

Setting a bean value (column) automatically before persisting in Hibernate?

Is there a nice and elegant way to set a bean value (column) before Hibernate persists an entity? Basically I have a field called "modification_date". It's on a bunch of entities. Whenever one of these entities is updated/modified, I'd basically like that field set automatically.
I could write the code in the service layer to set the date every time the object is saved/updated manually...
I also have a Dao Layer. Every Dao extends from a support class that contains a save() method. I could just use reflection and set the value inside of this method. I could check to see if that class has a field with the name "modicationDate", and if it does, set it to new Date().
Is there a better way than this? Or is using my generic save() method the best approach? This is something I'd like to be robust and not have to worry about it ever again. I will be happy knowing that by simply making a "modificationDate" property that this will be taken care of for me automatically from this point on. Using the save() method seems like the best place, but if there's a better way, I'd like to become aware of it.
Checkout event listeners:
#Entity
#EntityListeners(class=LastUpdateListener.class)
public class Cat {
#Id private Integer id;
private String name;
private Calendar dateOfBirth;
#Transient private int age;
private Date lastUpdate;
#PostLoad
public void calculateAge() {
...
}
}
public class LastUpdateListener {
/**
* automatic property set before any database persistence
*/
#PreUpdate
#PrePersist
public void setLastUpdate(Cat o) {
o.setLastUpdate( new Date() );
}
}

Categories

Resources