1

Let's assume I have Entity that have nested Entity inside it. For example (please, ignore missing annotations, getters/setters, etc):

@Entity
class User {
  private String userId;
  private Set<UserOperation> userOperations;
}

@Entity
class UserOperation {
   private String someString;
   // This is nested referenced entity
   private User user;
}

Let's assume that I want to insert new UserOperation and all that I have is userId.

Can I do something like:

// We just create new user. There is no interaction to get existing from DB. Totally only 1 insert
User user = new User();
user.setId("someId")

UserOperation uOp = new UserOperation();
uOp.setUser(user);
uOp.setSomeString("just op");
em.persist(uOp);

Or I should go that way only:

// We retrieve existing user. There is interaction to get it from DB. Totally 1 select and 1 insert
User user = em.find("someId")

UserOperation uOp = new UserOperation();
uOp.setUser(user);
uOp.setSomeString("just op");
em.persist(uOp);

What is the right way of doing it?

Because from DB perspective userOperation table just have String user reference, so ID should be enough. Java requires an object. When call "new User" I would like to avoid, properties of existing user be flushed (as they are all not set) or JPA trying to insert new user and operation failing due to primary key violation.

Some examples are welcomed.

Tigran
  • 984
  • 2
  • 12
  • 27

2 Answers2

1

For your use case, there is particularly method getReference() in EntityManager. It gives you an entity object for id, but does not access DB to create it. Therefore the best solution is a slightly modified 2nd solution of yours:

// We retrieve a stub user for given id. There is no interaction with DB
User user = em.getReference("someId", User.class);

UserOperation uOp = new UserOperation();
uOp.setUser(user);
uOp.setSomeString("just op");
em.persist(uOp);

Explanation:

getReference() has the same logical meaning as find(), with the exception that it does call DB. The consequence is that it does not check if there is a row in DB table with the given id, and that the object you get does not yet contain the data. However, the object is fully capable to load additinal data when get method is called. Therefore the object is fully usable even if retrieved by getReference() - in fact it works the same way as lazy loading.

A side note to your first solution:

The first solution would not work, as it would create a new entity user and then it would fail either when storing the entity to DB if it is cascaded (persist always calls insert and it would try to insert user with the same ID as exists in DB), or it would fail that UserOperation is to be persisted while user is not. In order to fix this solution, you would need to call em.merge(user) before you call em.persist(userOperation). But again, this would call a select to DB in the same way as em.find().

OndroMih
  • 6,329
  • 1
  • 21
  • 38
0

The best way to do this is using the second example. We should always try to use the actual object direct from db. Working with only the db reference will be way worse to mantain.

Now speaking specifically about Hibernate, it makes even more sense to work with whole objects, especially because of Hibernate's cascade, that can and will (if cascade is set) update the child entities of the one you are persisting to database.

Well, I have to admit that always fetching objects from database may cause some performance issues especially after the database gets a huge amount of data, so it's always important to implement nice and coherent model entities, and also keep in track of database hits from your application, and try to keep it the less possible queries being generated.

As for example, your own example (the second) is clean and easy to understand, I would stick with this approach, since it's really simple.

Hope it can solve your questons :)

Bonifacio
  • 1,362
  • 12
  • 18
  • I found another method - getreference. May be it should be used intead of new?? http://stackoverflow.com/questions/1607532/when-to-use-entitymanager-find-vs-entitymanager-getreference – Tigran Sep 12 '15 at 19:24
  • I've never faced an issue using find(), however the answer of the question you linked is pretty detailed and I think you could use it as an aditional advice for your implementations. – Bonifacio Sep 12 '15 at 19:50
  • EntityManage.getReference() is probably what you want. It gives you roughly the same as em.find() but does not access DB – OndroMih Sep 14 '15 at 07:59