What I have:
I've implemnted a `State Pattern. Now I have a dozen of States in my app, all of them are Spring's beans, like:
#Service(value = "FinishedState")
#Scope("singleton")
public class FinishedState extends AbstractState {
private final String stateName = "Finished";
private final String badgeColor = "#459852";
//implementation of state API
}
What I want:
Be able to change stateName and badgeColor without recompiling my application. Or in other words I want to store my states in DB.
What a question:
Is it good idea to make all States as Singleton entities?
What diffculties may I face?
Means that I will manually set an id to each State , like id = this.getClass().getSimpleName(), and map all state-siblings into one table.
Edit (for #Adam Arold):
And also its worth to say that I need that other entities can reference that Singleton's states.
#Service(value = "FinishedState ")
#Scope("singleton")
public class FinishedState extends AbstractState {
private final String stateName = "Finished";
private final String badgeColor = "#459852";
#Override
public void someMethod1(Session session, String newState, MyEntity entity) {
entity.setStatus(newState);
writeHistory(entity);
}
#Override
protected boolean someMethod2(Session session, AbstractState newState, MyEntity entity)
{
return true;
}
}
The only time I make my states Singletons is when I am changing states frequently. This is useful when performance is essential -- in the case that I do not wish to call new every time I switch back and forth from states.
Suggestion
My suggestion to you is that you analyze:
How does your system operate in regards to the states?
Do you change states frequently?
Can you afford the space in memory of having a bunch of instances of
states alive during the execution of the system?
The only issue I see that you can come across is that while your system is running the Singleton states will be alive until the system finishes.
Personally
I would make them Singletons though
I ended with:
#MappedSuperclass
#Inheritance(strategy= InheritanceType.SINGLE_TABLE)
#Table(name = "state")
public class StateBase {
/**
* Entity API
*/
#Id
#Column(name = "id")
private String id = this.getClass().getSimpleName(); //prevent from creating two instances of state
#Column(name = "name")
private String name;
#Column(name = "badgeColor")
private String badgeColor;
//only Getters! also hashCode and Equals
//.....................
/**
* ======== State API ========
* Use TEMPLATE METHOD and hooks
*/
final public void changeState(Session session, State newState, StateEntity entity) { //StateEntity is interface that is implemented by all entities that have a state
if(newState.equals(this)) return;
if (canMove(session, newState, entity)) {
hook(session, newState, entity);
beforeTransition(session, newState, entity);
makeTransition(session, newState, entity);
afterTransition(session, newState, entity);
} else {
throw new TransitionDeniedException("Transition from " + getName() + " to " + newState.getName() + " is denied.");
}
}
//Defauls implementation for all (or almost all hooks)
protected void beforeTransition(Session session, State newState, StateEntity entity) {
entity.setState(newState);
}
protected void afterTransition(Session session, State newState, StateEntity entity) {
writeHistory(entity);
session.merge(entity);
}
//..........................
}
And than:
#Entity
public class SpecificState extends StateBase {
//Override hook
#Override
public void makeTransition(Session session, State newState, StateEntity entity) {
MyEntity myEntity = (MyEntity ) entity;
String commentText = "some comment";
String author = "[autogenerated]";
addProfileComment(session, myEntity, commentText, author);
}
}
Due to my implementation, all states are entites, but they have only private String field without Setters. So they have no muttable state. So it isn't realy matter if there can exist one or more entities of specific state (in memory). In DB I can store only one entity of specific state class at once.
Advantages
It's allow me to forget about troubles of state synchronization and another possible difficulties linked with replacement of Spring singleton beans with Hibernate entities.
It's also allows user to change name and badgeColor through admin tool without recompiling the whole application.
Disadvantages:
Not observed.
Related
I know this is a common question, but I haven't found another that solves my doubts.
Usually, if the project is small, I've persistence annotations in the same object that represents the domain object. This allows to load the entity from database and keep all the setters private, ensuring any instance is always in a valid state. Something like:
#Entity
class SomeEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
// ... other attributes
protected SomeEntity() {}
/* Public getters */
public Long getId() { ... }
public String getAttribute1() { ... }
public String getAttribute2() { ... }
/* Expose some behaviour */
public void updateAttributes(String attribute1, String attribute2) {
/* do some validations before updating */
}
}
My problem appears if I want to hava a different persistent model. Then I would have something like:
/* SomeEntity without persistent info */
class SomeEntity {
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
// ... other attributes
protected SomeEntity() {}
/* Public getters */
public Long getId() { ... }
public String getAttribute1() { ... }
public String getAttribute2() { ... }
/* Expose some behaviour */
public void updateAttributes(String attribute1, String attribute2) {
/* do some validations before updating */
}
}
and DAO:
#Entity
class SomeEntityDAO {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
public SomeEntityDAO() {}
/* All getters and setters */
}
My question is, how can I map SomeEntityDAO to SomeEntity without exposing SomeEntity's attributes?
If I create a constructor like: public SomeEntity(String attribute1, String attribute2, ...) {}, then anyone can create an invalid instance of SomeEntity. The same occurs if I make all setters public in SomeEntity.
I also don't think is a valid solution build the object using updateAttributes() since this will execute some validations I don't whant to execute at this point (we trust the data that's persistet in database).
I'm thinking in having all the setters protected, so the DAO can extend the Entity and have access to setters... but I'm not sure if this is a good option.
Which is the best or common approach to solve this problem?
I've had the same kind of problem. And looking around I've found no solution. Believe me, if it exists is well hidden somewhere. None that suggests what to do when you have to deal with an old project where ORM entities are everywhere and there's a big step between Domain and ORM model.
Given this, I've deducted that if you really want to keep your Domain entities pure (so non get and set - the latter I would NEVER accept!) you have to do some deals. Because there's no way to share the internals without giving the entities some extra knowledge. Beware, this doesn't mean that you have to make the Domain entities aware of the ORM layer, nor that you have to use getters. Just, what I've concluded, the Domain entities should have ways to expose them as a different model.
So, in conclusion, what I would do in your situation is to build up a Visitor pattern. The Domain entity EntityA would implement the EntityAVisitable interface to accept a EntityAVisitor or something like this.
interface EntityAVisitable {
accepts(EntityAVisitor visitor);
}
The builder implements the interface required by the Visitor, EntityAVisitor.
interface EntityAVisitor<T>{
setCombinedValue1_2(String attribute1_attribute2_combinedInEntity);
<T> build();
}
The build() function of the interface EntityAVisitor uses a generic type T. In this way the Domain entity is agnostic about the return type of the concrete implementation of the EntityAVisitor.
Is it perfect? No.
Perfect solution would be to get rid of the ORM (actually I would say that I hate them, because the way are used is most of the times wrong - but this is my personal thought).
Is it nice? No.
A nice solution is not allowed due to language restrictions (I suppose you use Java).
Does it a good work in encapsulating the real content of your Domain entity? Yes.
Not only, in this way you can decide exactly what could be exposed and how. So, in my opinion, is a good deal between keeping the entity pure and having to work with an ORM under the seat.
Domain entity should be self-validating meaning it should only validate itself based on it's internal values. If update requires validation that depends on external dependencies, then I would create an updater class that is responsible for the update. From the updater class, you can use specification pattern (as an injectable dependency) to implement the validation.
Use domain entities when modifying, and DTOs for read-only projections. There are performance and simplification gains when you use straight DTOs in read-only. This is used in CQRS patterns.
class SomeEntity {
private Long id;
private String attribute1;
private String attribute2;
private String attribute3;
// ... other attributes
public SomeEntity() {}
/* Public getters/setter */
public Long getId() { ... }
public String getAttribute1() { ... }
public String getAttribute2() { ... }
public Long setId() { ... }
public String setAttribute1() { ... }
public String setAttribute2() { ... }
}
//classes/interfaces named for clarity
class EntityUpdater implements IEntityUpdater {
public EntityUpdater (ISpecification spec){
}
public updateEntity(SomeEntity entity){
//assert/execute validation
}
}
Some ORMs allow setting entity values through field access (as opposed to setter methods).
JPA uses the #Access annotation. See What is the purpose of AccessType.FIELD, AccessType.PROPERTY and #Access
I created an ORM, sormula, that can use field access. See #Row fieldAccess and test case org.sormula.tests.fieldaccess.
I have a REST service that use a stateless bean to perform an algorithm by using other beans.
All those beans made changes to the fields of my data, including a ManyToOne field.
At the end of the algorithm, just before the “return” of my rest service, my data is in the correct state, and the ManyToOne field in question has been changed.
But just after the return of the rest service, an update seems to happen on that field, and change it back to its previous state. But the other fields are still in the correct state.
What could possibly change my data after the “return” of my rest service ?
The project work with hibernate and JTA transactions in a wildfly environment.
The data looks like below :
public class Data extends JpaDecorator {
#Column(length=1024)
#Override
public String getCommentaire() {
return getImpl().getCommentaire();
}
#Column(length=1024)
#Override
public void setCommentaire(String value) {
getImpl().setCommentaire(value);
}
}
#MappedSuperclass
public class JpaDecorator<T> {
private WorkspaceJpa workspaceJpa;
private T impl;
#Transient
public T getImpl() {
return impl;
}
#ManyToOne
#JoinColumn(name = "wid", referencedColumnName = "oid")
public WorkspaceJpa getJpaWid() {
return this.workspaceJpa;
}
public void setJpaWid(WorkspaceJpa value) {
this.workspaceJpa = value;
Workspace wks = ((this.workspaceJpa != null) ? this.workspaceJpa.getImpl() : null);
getImpl().setWid(wks);
}
}
EDIT :
I finally solved the problem. It came from an unclosed transaction used to collect the data at the beginning of the algorithm. That transaction stayed alive until the return of the rest service, and then was forced to end and flush the state of its data, which was obsolete.
I'm trying to get the old entity in a #HandleBeforeSave event.
#Component
#RepositoryEventHandler(Customer.class)
public class CustomerEventHandler {
private CustomerRepository customerRepository;
#Autowired
public CustomerEventHandler(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
#HandleBeforeSave
public void handleBeforeSave(Customer customer) {
System.out.println("handleBeforeSave :: customer.id = " + customer.getId());
System.out.println("handleBeforeSave :: new customer.name = " + customer.getName());
Customer old = customerRepository.findOne(customer.getId());
System.out.println("handleBeforeSave :: new customer.name = " + customer.getName());
System.out.println("handleBeforeSave :: old customer.name = " + old.getName());
}
}
In the event I try to get the old entity using the findOne method but this return the new event. Probably because of Hibernate/Repository caching in the current session.
Is there a way to get the old entity?
I need this to determine if a given property is changed or not. In case the property is changes I need to perform some action.
If using Hibernate, you could simply detach the new version from the session and load the old version:
#RepositoryEventHandler
#Component
public class PersonEventHandler {
#PersistenceContext
private EntityManager entityManager;
#HandleBeforeSave
public void handlePersonSave(Person newPerson) {
entityManager.detach(newPerson);
Person currentPerson = personRepository.findOne(newPerson.getId());
if (!newPerson.getName().equals(currentPerson.getName)) {
//react on name change
}
}
}
Thanks Marcel Overdijk, for creating the ticket -> https://jira.spring.io/browse/DATAREST-373
I saw the other workarounds for this issue and want to contribute my workaround as well, cause I think it´s quite simple to implement.
First, set a transient flag in your domain model (e.g. Account):
#JsonIgnore
#Transient
private boolean passwordReset;
#JsonIgnore
public boolean isPasswordReset() {
return passwordReset;
}
#JsonProperty
public void setPasswordReset(boolean passwordReset) {
this.passwordReset = passwordReset;
}
Second, check the flag in your EventHandler:
#Component
#RepositoryEventHandler
public class AccountRepositoryEventHandler {
#Resource
private PasswordEncoder passwordEncoder;
#HandleBeforeSave
public void onResetPassword(Account account) {
if (account.isPasswordReset()) {
account.setPassword(encodePassword(account.getPassword()));
}
}
private String encodePassword(String plainPassword) {
return passwordEncoder.encode(plainPassword);
}
}
Note: For this solution you need to send an additionally resetPassword = true parameter!
For me, I´m sending a HTTP PATCH to my resource endpoint with the following request payload:
{
"passwordReset": true,
"password": "someNewSecurePassword"
}
You're currently using a spring-data abstraction over hibernate.
If the find returns the new values, spring-data has apparently already attached the object to the hibernate session.
I think you have three options:
Fetch the object in a separate session/transaction before the current season is flushed. This is awkward and requires very subtle configuration.
Fetch the previous version before spring attached the new object. This is quite doable. You could do it in the service layer before handing the object to the repository. You can, however not save an object too an hibernate session when another infect with the same type and id it's known to our. Use merge or evict in that case.
Use a lower level hibernate interceptor as described here. As you see the onFlushDirty has both values as parameters. Take note though, that hibernate normally does not query for previous state of you simply save an already persisted entity. In stead a simple update is issued in the db (no select). You can force the select by configuring select-before-update on your entity.
Create following and extend your entities with it:
#MappedSuperclass
public class OEntity<T> {
#Transient
T originalObj;
#Transient
public T getOriginalObj(){
return this.originalObj;
}
#PostLoad
public void onLoad(){
ObjectMapper mapper = new ObjectMapper();
try {
String serialized = mapper.writeValueAsString(this);
this.originalObj = (T) mapper.readValue(serialized, this.getClass());
} catch (Exception e) {
e.printStackTrace();
}
}
}
I had exactly this need and resolved adding a transient field to the entity to keep the old value, and modifying the setter method to store the previous value in the transient field.
Since json deserializing uses setter methods to map rest data to the entity, in the RepositoryEventHandler I will check the transient field to track changes.
#Column(name="STATUS")
private FundStatus status;
#JsonIgnore
private transient FundStatus oldStatus;
public FundStatus getStatus() {
return status;
}
public FundStatus getOldStatus() {
return this.oldStatus;
}
public void setStatus(FundStatus status) {
this.oldStatus = this.status;
this.status = status;
}
from application logs:
2017-11-23 10:17:56,715 CompartmentRepositoryEventHandler - beforeSave begin
CompartmentEntity [status=ACTIVE, oldStatus=CREATED]
Spring Data Rest can't and likely won't ever be able to do this due to where the events are fired from. If you're using Hibernate you can use Hibernate spi events and event listeners to do this, you can implement PreUpdateEventListener and then register your class with the EventListenerRegistry in the sessionFactory. I created a small spring library to handle all of the setup for you.
https://github.com/teastman/spring-data-hibernate-event
If you're using Spring Boot, the gist of it works like this, add the dependency:
<dependency>
<groupId>io.github.teastman</groupId>
<artifactId>spring-data-hibernate-event</artifactId>
<version>1.0.0</version>
</dependency>
Then add the annotation #HibernateEventListener to any method where the first parameter is the entity you want to listen to, and the second parameter is the Hibernate event that you want to listen for. I've also added the static util function getPropertyIndex to more easily get access to the specific property you want to check, but you can also just look at the raw Hibernate event.
#HibernateEventListener
public void onUpdate(MyEntity entity, PreUpdateEvent event) {
int index = getPropertyIndex(event, "name");
if (event.getOldState()[index] != event.getState()[index]) {
// The name changed.
}
}
Just another solution using model:
public class Customer {
#JsonIgnore
private String name;
#JsonIgnore
#Transient
private String newName;
public void setName(String name){
this.name = name;
}
#JsonProperty("name")
public void setNewName(String newName){
this.newName = newName;
}
#JsonProperty
public void getName(String name){
return name;
}
public void getNewName(String newName){
return newName;
}
}
Alternative to consider. Might be reasonable if you need some special handling for this use-case then treat it separately. Do not allow direct property writing on the object. Create a separate endpoint with a custom controller to rename customer.
Example request:
POST /customers/{id}/identity
{
"name": "New name"
}
I had the same problem, but I wanted the old entity available in the save(S entity) method of a REST repository implementation (Spring Data REST).
What I did was to load the old entity using a 'clean' entity manager from which I create my QueryDSL query:
#Override
#Transactional
public <S extends Entity> S save(S entity) {
EntityManager cleanEM = entityManager.getEntityManagerFactory().createEntityManager();
JPAQuery<AccessControl> query = new JPAQuery<AccessControl>(cleanEM);
//here do what I need with the query which can retrieve all old values
cleanEM.close();
return super.save(entity);
}
The following worked for me. Without starting a new thread the hibernate session will provide the already updated version. Starting another thread is a way to have a separate JPA session.
#PreUpdate
Thread.start {
if (entity instanceof MyEntity) {
entity.previous = myEntityCrudRepository.findById(entity?.id).get()
}
}.join()
Just let me know if anybody would like more context.
Don't know if you're still after an answer, and this is probably a bit 'hacky', but you could form a query with an EntityManager and fetch the object that way ...
#Autowired
EntityManager em;
#HandleBeforeSave
public void handleBeforeSave(Customer obj) {
Query q = em.createQuery("SELECT a FROM CustomerRepository a WHERE a.id=" + obj.getId());
Customer ret = q.getSingleResult();
// ret should contain the 'before' object...
}
I am not sure what the best practice is for dealing with collection/lookup tables/in RequestFactory.
For example if I have following two Domain objects:
#Entity
public class Experiment {
private Long id;
private String name;
#ManyToOne(cascade={CascadeType.PERSIST,CascadeType.MERGE})
private UnitOfMeasure unitOfMeasure;
public Experiment() { }
public String getName() {
return name;
}
public Long getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public UnitOfMeasure getUnitOfMeasure() {
return unitOfMeasure;
}
public void setUnitOfMeasure(UnitOfMeasure unitOfMeasure) {
this.unitOfMeasure = unitOfMeasure;
}
}
#Entity
public class UnitOfMeasure {
private Long id;
private String unit_type;
public UnitOfMeasure() { }
public String getUnitType() {
return unit_type;
}
public Long getId() {
return id;
}
public void setUnitType(String unitType) {
this.unit_type = unitType;
}
}
This is a normal unidirectional 1:n realtionship between Experiment and UnitOfMeasure using a ForeignKey in the Experiment table.
I have a limited amount of different UnitOfMeasure instances which usually don't change.
The web-app provides a view where the user can change some properties of the Experiment instance. The view uses the Editor framework. For changing the UnitOfMeasure of a specific Experiment I use a ValueListBox and render the unit_type property.
Because the list of available UnitOfMeasure instances is static I use AutoBeanFactory to create a json string which I put into the HTML host page and during application start I parse it (same thing for all other collection like table values) and store them in a Singleton class instance (AppData) which I pass to `setAcceptableValues``.
Currently I derive UnitOfMeasureProxy from EntityProxy but in order to decode/encode it with AutoBeanFactory I have to annotate the Factory with EntityProxyCategory. I somehow suspect that a ValueProxy would be a better fit.
However with a ValueProxy when I change the UnitOfMeasure of a specific Experiment the entire ValueProxy instance is transmitted over the wire.
From a database point of view however only changing the value for the foreignkey in the Experiment table is required.
So what is the best practice (ValueProxy vs EntityProxy) for collection like tables and child values respectively?
In many cases, references to other entities are best modelled using their IDs rather than the EntityProxys themselves (it's debatable, but I think it's also true for server-side code, or actually any code that crosses unit-of-work boundaries –JPA EntityManager lifetime, Hibernate session, etc.–)
BTW, the proper way to serialize RequestFactory proxies is to use a ProxySerializer.
Make sure you use GWT 2.5.0-rc1 though if you have lists of ValueProxys (see issue 6961)
I hava a basic Hibernate/JPA question. I want to find a best practice solution for saving entities. I have a List of Entities and many of them might be altered so I want to save them all at once.
I believe everything is pretty much standard. (Just example code for readability reasons)
Entity: Car
#Entity
public class Car implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private id;
private String model;
// .... Setter
// .... Getter
}
Service Class: CarService
#Named
#Transactional
public class CarServiceImpl implements CarService {
#PersistenceContext
private EntityManager entityManager;
#Override
public List<Car> findAll() {
TypedQuery<Car> q = entityManager.createQuery(
"FROM Car", Car.class);
return q.getResultList();
}
#Override
public void saveEntity (Car car) {
/* What exactly am I doing here? */
}
}
Controller: CarEditController
#Named
public class CarEditController implements Serializable {
private static final long serialVersionUID = 1L;
#Inject
private CarService carService;
private List<Car> cars;
public List<Car> getCars () {
return carService.findAll();
}
public void setCars (List<Car> cars) {
this.cars = cars;
}
public void btn_newClick() {
Car newCar = new Car();
car setModel("BMW");
cars.add(newCar);
}
public void btn_saveClick() {
for (Car car : cars) {
carService.saveEntity(car);
}
}
}
I found quite a few ways of saving the entity. The obvious are entityManager.merge(car) for existing entities and entityManager.persist(car) for new ones. In theory thats easy but how do I know which entity is new?
The documentation suggests entityManager.flush(), which in theory updates all existing and inserts all new entities. It works but only if I fetch the entity with find().
The Question:
I want to fetch all entities in one step, add new ones and then save them all in one methode (btn_saveClick()). How is this task best accomplished?
Just check if the #Id is been set.
#Override
public void saveEntity (Car car) {
if (car.getId() == null) {
entityManager.persist(car);
} else {
entityManager.merge(car);
}
}
More common approach, however, is to offer separate create() and update() service methods.
I'm not familiar with JPA but in hibernate there is session.saveOrUpdate()
for(Car car : existingAndNewCars)
{
session.saveOrUpdate(car);
}
Update:
As i understand JPA, its merge is like session.merge which is totally different as it doesn't track changes to object supplied to it while persist/save/update/saveOrUpdate would track subsequent changes to car, leading to subtle differences
Update:
since you use the same entitymanager it should suffice to
#Override
public void saveEntity (Car car) {
if (car.getId() == null) {
entityManager.persist(car);
}
without the subtle difference of persist and merge
The flush operation will operate on all entities in the current Hibernate session - new entities are inserted and existing entities are updated if they have changed.
You need to ensure that all entities are attached to the session. You do this by using merge as you correctly say. After you have loaded all of the entities the session is closed when the transaction ends. Your objects are then in a detached state i.e. have been persisted but are no longer attached to a session.
I would amend your logic so that your carService#save takes a List. It can then call merge on each one (attaching them to the session). Then when your transaction ends Hibernate will flush all changes to the database at once.