2

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?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Simon Plangger
  • 247
  • 1
  • 3
  • 9

3 Answers3

2

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

Firo
  • 30,626
  • 4
  • 55
  • 94
  • OP is using Hibernate 4.x which is a JPA implementation, not the "Good Ol" Hibernate 3.x from back in the dark J2EE ages. – BalusC Feb 22 '12 at 12:54
  • @BalusC if i have to implement SaveOrUpdate myself in H 4.x then i'm more happy with the "Good Ol" Hibernate 3.x and NHibernate 3.x – Firo Feb 22 '12 at 15:26
  • That's fine :) I didn't argue this at all. I was just warning that the OP is not using Hibernate 3.x and thus this answer don't apply to the concrete question at all. – BalusC Feb 22 '12 at 15:32
  • @BalusC just commenting on "from back in the dark J2EE ages" which implies "not as good as today" to me :) – Firo Feb 22 '12 at 15:49
  • Oh :) Admitted, Java EE 5 with JPA was introduced half 2006 which is 6 years ago. Six years! J2EE is also obsoleted since Java EE 6 was out end of 2009. That's almost 2.5 years ago. – BalusC Feb 22 '12 at 15:55
  • damn, it's been a long time since i used java. but after reading about JPA i saw that H 3 already implemented JPA and that it is possible to call Hibernate methods like the one i posted. Is this not possible with H 4 anymore? source: http://stackoverflow.com/a/4639967/671619 – Firo Feb 22 '12 at 16:06
  • Sorry, I confused the Hibernate versions. 3.2 was first to offer JPA 1.x implementation and 3.5 was first to offer JPA 2.x implementation. It's called "Hibernate EntityManager" which is provided on top of "Hibernate Core" (which is the "Good Ol" Hibernate). – BalusC Feb 22 '12 at 16:11
  • so it should be possible to `HibernateEntityManager.getSession().SaveOrUpdate(obj);` which is what the OP wants. – Firo Feb 22 '12 at 17:13
  • The SaveOrUpdate functionality is really what I wanted, but since I use Hibernate 4.0.1.Final (which I should have clarified in the first place ;-) ) I want to use the EntityManager how it's supposed to be. I got it working just with `entityManager.merge(Car)` for existing and new objects alike and I like that approach. – Simon Plangger Feb 23 '12 at 10:40
2

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.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • You were right. Saving should have worked before but there was a problem with the definition of the primary key. In the _real life_ project I use a composite identifier and that was the problem all along. Thanks! – Simon Plangger Feb 22 '12 at 14:55
1

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.

Alex Barnes
  • 7,174
  • 1
  • 30
  • 50
  • Thanks for the info. That seems to be the right approach. I never knew exactly what **merge()** did and why entities were saved when I used it. – Simon Plangger Feb 22 '12 at 14:53