I have an entity with a byte[] member variable annotated as a #Lob. I perform some calculations and then I do a merge() call. This is done in a stateless bean. For some reason it does not update the entity. No exceptions are thrown either. Help me www dot stackoverflow dot com, you're my only hope.
Here is basically what I have.
My entity:
#Entity
#Table(name="my_entity")
public class MyEntity implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private long id;
#Lob
#Column(name="data")
private byte[] data;
// Getters and Setters for both variables...
}
Then I have my stateless bean. I will make the skeleton of exactly what I have. I have a timer that is called every minute which acts on the data.
public #Stateless class MyBean implements IMyBeanRemote {
#PersistenceContext
private EntityManager em;
#Resource
javax.ejb.TimerService timerService;
#Timeout
public void doLogic(javax.ejb.Timer time) {
MyEntity e = getMyEntity(1L);
doMoreLogic(e);
}
private MyEntity getMyEntity(long id) {
return em.find(MyEntity.class, id);
}
private void doMoreLogic(MyEntity entity) {
entity.getData()[0] = 123;
em.merge(entity);
}
}
I think this basically mirrors what my code is doing. When I initially create MyEntity and persist() it, that works.
After typing all this, I decided to do a check to see if I modify that data anywhere else. Turns out I did and I was resetting the data as a debugging test. =( All solved!
Lack of sleep caused me to not see previous debugging code resetting the data.
Related
I'm using Spring boot 2.7.0
And have the next entities in simple:
#Getter
#Setter
#Entity
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Version
private Long version;
private String name;
}
#Getter
#Setter
#Entity
public class Event {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
Account account;
private String message;
}
and jpa repositories:
#Repository
public interface AccountRepository extends JpaRepository<Account, Long> {
}
#Repository
public interface EventRepository extends JpaRepository<Event, Long> {
Page<Event> findAllByAccount(Account account, Pageable pageable);
}
In short I call
eventRepository.findAllByAccount(accountRepository.findById(1), PageRequest.of(1,10));
Problem is every call of last code increases the version field of Account by 1. So question is why? I don't call any update or save method.
And additionally the result of this behaviour is calling of method needs
#Transactional(readonly=false)
Otherwise if I write readonly=true that throws cannot execute UPDATE in a read-only transaction
ADDED:
full code of usage:
#Transactional
public Page<Event> events(Long accountId, int page) {
return eventRepository.findByAccount(findById(accountId), PageRequest.of(page, PAGE_SIZE));
}
#GetMapping("/events")
public List<EventResponse> listEvents(#RequestParam(value = "max", defaultValue = "0") int page) {
return eventService.events(1L, page).stream().map(EventResponse::of).toList();
}
It looks like hibernate is deriving lockMode type as either of WRITE or OPTIMISTIC_FORCE_INCREMENT or PESSIMISTIC_FORCE_INCREMENT based on isolation level of your database. As per reference hibernate decides this pessimistic locking by its own based on database you use.
As per doc, if lockmode type is either of what I mentioned above, Version will get automatically incremented even if you haven't changed anything i.e. even if you haven't do any update or save.
Please check database isolation level & based on that you might get an idea about this.
Edit: as you explicitly setting lockmode as write so my answer validates that because of WRITE mode, your version got incremented automatically.
The problem should be related in the code which is using the result of the find.
If you're modifying entities under a transaction they're going to be modified at the end of the method, when Spring in this case is going to close the transaction. In this part when transaction ends, the JPA provider (for example hibernate) aligns the relative entity record into the database with the 'java entity object' by an update.
I'm sorry. After trim all my code to the posted and debug I found my mistake:
In the begin I was retrieving Account in another method by .lock(Long) method instead of .findById(Long)
lock method is below:
#Lock(LockModeType.WRITE)
#Query("from Account where id = :id")
public Optional<Account> lock(Long id);
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 am using Wicket, JPA+Hibernate in a project
I have the persistence class like this
#Entity
#Table(name = "document", uniqueConstraints = { #UniqueConstraint(columnNames = {
"name"}) })
public class Document
implements Serializable
{
private static final long serialVersionUID = 8496087166198616020L;
#Id
#GeneratedValue
private long id;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String state;
.....
Geter && setters
Then
I have the dao implementation for crud operations
One method is
#Override
#Transactional
public void createDocument(Document aDocument)
{
entityManager.persist(aDocument);
}
My Problem is, in the wicketPage, if I use a Wicket Link and it's onclick method doing the following
document.setState("someState");
it updates the value with out calling the createDocument(document) specifically and the database is also up-to-date
But if I use AjaxLink and its onclick method
then, I have to manually update the object otherwise the value will not be updated in the database. My modification should be as follows:
#Override
#Transactional
public void createDocument(Document aDocument)
{
if(aDocument.getId()==0{
entityManager.persist(aDocument);
}
else{
entityManager.merge(aDocument);
}
Then to save the changes in the Database, I have to first update the state and call createDocument() method.
document.setState("someState");
dao.createDocument(document);
Can anyone point out why this happens?
Many thanks
Your database session must be in AutoCommit mode. When you use the Link a "classic" RequestCycle is fired which certainly triggers your OpenSessionInView filter. This way, the document object that you update is still managed by the Persistence Context, thus, the underlying JPA implementation auto commits the change to the database.
When you use the AjaxLink, you're playing with a detached entity, JPA does not manage it and hence does not autocommit it to the database. When you call your createDocument method, you merge your entity with your Persistence Context and save it back.
If you do not want the autocommit to happen, you can set the FlushMode of your entityManager to FlushMode.COMMIT
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.
I'm using Hibernate with jpa and I'm getting LazyInizializationException trying to do a very simple thing. I know what does LazyInizializationException means but I can't understand why it comes while i'm doing everything in the most common and simple way. This is the "one" side of the relationship:
#Entity
public class User implements Serializable{
#Id #GeneratedValue
private int idUser;
private String name;
private String surname;
private String username;
#OneToMany(mappedBy="user")
private List<Device> dev;
...getters and setters...
and this is the "Many" side:
#Entity
public class Device implements Serializable {
#Id #GeneratedValue
private int idDevice;
private String brand;
private String model;
#ManyToOne
#JoinColumn(name="user_fk")
private User user;
...getters and setters...
the jUnit test that throws the exception is:
#Test
public void testLazyUserSnd() {
User u = uDao.getUser(2);
List<Device> devList = u.getDev();
Device aDevice = devList.get(0); // <--- Here the exception is thrown
aDevice.getModel();
I made the relationship as explained in the Hibernate Documentation. Any hint? Am I making some big and stupid mistakes?
While #Xavi's answer is perfectly reasonable, you may not always want to load the devices for a user. If you don't, there are 2 ways of fixing this.
Create an additional method uDao.getUserWithDevices(id) and call that when you know you need devices, otherwise call the uDao.getUser(id).
Encapsulate the test method, and therefore any production code that uses the method, in a transaction. In other words keep the session open as long as you need to.
Personally I'd use the transaction method since as it allows more flexibility and allows JPA to lazy load whenever it needs to. See also http://community.jboss.org/wiki/OpenSessionInView for more interesting information around session lifecycle.
The exception is telling you that you're trying to get some of the lazy-loaded association's elements when the session is closed. Probably you should call u.getDev() or Hibernate.initialize(u.getDev()) inside the dao's method, when the hibernate session is still open.
Or, if you're using Criteria, you could also use setFetchMode to force eager fetching.
public User getUser(String id) {
Session session = getSession();
Criteria criteriaQuery = session.createCriteria(User.class);
criteriaQuery.add(Expression.eq("id", id));
criteriaQuery.setFetchMode("dev", FetchMode.JOIN);
return criteriaQuery.uniqueResult();
}
The entity is probably detached from the session (transaction context) when you access the relation. Try to enclose your test method in a transaction.