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

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() );
}
}

Related

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.

Captalizing Strings Automatically before Persist

I have the following situation: my application needs to save all the strings typed by the user capitalized on the database, no matter how the user types it, the application needs to capitalize everything before saving it.
I know I could just call the toUpperCase() method on every string before saving it, or call it on every setter method, but I really don't want to do that, I'm looking for a more automatic way to do it without having to change too much code on the application.
I'm using JSF, JPA2, Eclipselink and EJB3.
Does anyone have any suggestion?
You can use lifecycle event listeners for this. There are several ways to implement it:
1: default listeners:
public class StringCapListener {
#PrePersist
#PreUpdate
public void capitalize(Object o) {
// capitalize string attributes
}
...
For the capitalizing you will either need to use reflection (extracting all string fields and changing their value) or let your entities implement an interface.
If you are using the listener only on several entities, prefer using the #EntityListeners annotation on the entity classes. In order to use the listeners on all entities, use default listeners. Unfortunately, you can only define them in XML:
<entity-mappings ...>
<persistence-unit-metadata>
<persistence-unit-defaults>
<entity-listeners>
<entity-listener class="com.example.StringCapListener">
2: inherited listener method
Let your entities derive from a BaseEntity of sorts. This base class can implement a listener method that is triggered on persist & update.
#PrePersist
#PreUpdate
public void capitalize(BaseEntity o) {
// capitalize string attributes
}
You will need to employ the same reflection magic to get and change all string attributes.
I'm thinking of an interface
public interface Processor<TResult, TInput> {
public TResult process(TInput input);
}
public class StringProcessor implements Processor<String, String> {
public String process(String input) {
return input.toUpperCase();
}
}
Then you'd call the interface on every string before persisting it
//...
public void persistString(String input) {
input = processor.process(input);
// Persistence logic
}

Add Java class to existing inheritance hierarchy in JPA with Hibernate

I have an abstract base class with existing subclasses that is mostly used for defining a number of common fields and associated methods. I have a separate concrete class that "organically evolved" (i.e., bad design due to unforeseen feature requests) to end up with all the same fields defined in that abstract subclass.
Is there any way of having that separate class extend the abstract class and carry over the data of existing stored instances of that separate class? I would like to use InheritanceType.SINGLE_TABLE, but if another strategy makes it easier or possible, I guess that's fine too.
Also, those are entities referenced in other entities (OneToMany). Is that a problem? Hibernate uses only one global sequence for assigning entity ids - so it should in theory be possible to not break those references even if the data is moved to another table, right?
Already tried a few things, but no luck so far (e.g., add the "extend" to the separate class, hard-code it to use the same table as the base class, manually add a field for the discriminator...).
I am also happy about any pointers to examples/docs on how to carry out class hierarchy changes and other data model changes with JPA/Hibernate without losing data!
So, here's a simplified example of the situation. Base is the abstract base class that already has sub-classes.
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "Base")
public abstract class Base {
private long persistenceId;
private String privateField;
#Id
#GeneratedValue
public long getPersistenceId() {
return persistenceId;
}
public void setPersistenceId(long persistenceId) {
this.persistenceId = persistenceId;
}
[...]
}
#Entity
public class SubclassToBe {
private long persistenceId;
private String privateField;
private String someFieldNotInBaseClass;
#Override
#Id
#GeneratedValue
public long getPersistenceId() {
return persistenceId;
}
#Override
public void setPersistenceId(long persistenceId) {
this.persistenceId = persistenceId;
}
[...]
}
The goal would be to have SubclassToBe inherit from Base, removing the definitions of shared fields but keeping the information stored there. And at the same time, not break references to the persistence ids of SubclassToBe objects that are used in other objects as part of OneToMany relations.

Automatic filling entity properties

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

#Id #GeneratedValue but set own ID value

I have a table with a generated id, but in some cases I would like to set it on my own. Can I, somehow, force Hibernate to ignore the #GeneratedValue?
It may be an overkill but have you thought about writing your own CustomIDGenerator which probably subclasses say the AutoGenerator of hibernate and exposes a couple of methods where you can set the id of the next class object to be generated so for example
class MyGenerator extends .... {
public void setIdForObject(Class clazz, Long id) {
//once you use this API, the next time an object of
//type clazz is saved the id is used
}
public void setIdForObject(Class clazz, Long id, Matcher matcher) {
//once you use this API, the next time an object of
//type clazz is saved and the matcher matches yes the id will be
//assigned. Your matcher can match properties like name, age etc
//to say the matched object
}
}
This could get complicated but at the least is possible as per hibernate doco
create your own identifiergenerator/sequencegenerator
public class FilterIdentifierGenerator extends IdentityGenerator implements IdentifierGenerator{
#Override
public Serializable generate(SessionImplementor session, Object object)
throws HibernateException {
// TODO Auto-generated method stub
Serializable id = session.getEntityPersister(null, object)
.getClassMetadata().getIdentifier(object, session);
return id != null ? id : super.generate(session, object);
}
}
modify your entity as:
#Id
#GeneratedValue(generator="myGenerator")
#GenericGenerator(name="myGenerator", strategy="package.FilterIdentifierGenerator")
#Column(unique=true, nullable=false)
private int id;
...
and while saving instead of using persist() use merge() or update()
Although this question was asked quite a while ago, I found the perfect answer for it in this post by #lOranger, and wanted to share it.
This proposal checks if the object's current id is set to something other than null, and if so, it uses it, otherwise, it generates it using the default (or configured) generation strategy.
It's simple, straight forward, and addresses the issue brought up by #Jens, of one not being able to retrieve the object's current id.
I just implemented it (by extending the UUIDGenerator), and it works like a charm :-D
For you use case, you can manually add this no user.
One way to do it is to put the insert operation on a file named "./import.sql" (in your classpath).
Hibernate will go execute these statements when the SessionFactory is started.

Categories

Resources