3

I have table Account that contains FK to another table.

Option 1:

@Override
@Transactional(readOnly = false)
public Account changePassword(Account existingAccount, String newPassword){
    existingAccount.setPassword(newPassword);
    return accountDAO.save(existingAccount);
}

This way, whenever I call save(), a join is made to retrieve another tables ID, THEN the update itself.
Note that all fields are updated here even tho only password changes.

EDIT : I figured out that the join is being made because I'm passing entity as a parameter (existingAccount). If I find it in changePassword method instead, it works fine(no joins). Problem is, I need this entity in a controller for validation so making the same database call 2 times is pointless.

Option 2:

@Modifying
@Query("UPDATE Account SET password=(:pass) WHERE username=(:username)")
public void changePassword(@Param("username")String username, @Param("pass")String pass);

This way, only the custom update within @Query is executed.

I think option 2 is probably better if it comes to performance but option 1 feels more jpa-like. Any suggestions appreciated.

Sikor
  • 11,628
  • 5
  • 28
  • 43
  • You should add the code of `Account` and `AccountRepository` interface –  Nov 05 '14 at 16:18
  • There is really nothing to add. Option 1 is service layer code, theres nothing in repository and Option 2 is repository interface code. Service layer just calls it and thats it. – Sikor Nov 05 '14 at 16:26

1 Answers1

3

Both of these should be ok. If you want to use the first way but avoid updating fields besides password you can configure dynamic-update with the annotation @DynamicUpdate(value=true):

dynamic-update (optional - defaults to false): specifies that UPDATE SQL should be generated at runtime and can contain only those columns whose values have changed.

You'd need to use select-before-update or optimistic locking with this. select-before-update might be worse-performing than updating all the fields.

Apparently this is implementation-specific and not part of JPA.

Community
  • 1
  • 1
Nathan Hughes
  • 94,330
  • 19
  • 181
  • 276
  • Looking it up atm and @org.hibernate.annotations.Entity is deprecated, but @DynamicUpdate(value = true) works instead. It works as you said but the only problem is the first option still executes the join before updating (even tho it updates only 1 field now - password). – Sikor Nov 05 '14 at 16:36
  • @Sikor: interesting. maybe make the association to the joined thing lazy? – Nathan Hughes Nov 05 '14 at 16:39
  • @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) @JoinColumn(name = "pilot_id"), It is lazy :D – Sikor Nov 05 '14 at 16:40
  • I guess I'll stick to the option 2. It feels like I have more control over what I'm doing that way + I don't have to do any select before update things to update just one field. I still don't know why hibernate executes join statement in option 1 tho. It's weird. – Sikor Nov 05 '14 at 17:07
  • @Sikor: yes, the lazy issue didn't get described fully here, i think it would make a good question on its own. – Nathan Hughes Nov 05 '14 at 17:18
  • I found out why hibernate was making the join. Take a look at my parameters in option 1. I'm passing the entity (existingAccount) from controller because I needed it there for validation purposes. Now, if I pass only username instead and find the account here, in changePassword method, it works fine aka no joins. Doesn't really solve my problem because as I said, I need existingAccount in the controller and creating another (exactly the same) sql in service layer is pointless so yeah, I will definitely stick to option 2. – Sikor Nov 05 '14 at 18:04