1
votes

I got two Entities in my Spring Boot Application:

User:

public class User implements java.io.Serializable, UserDetails  {
@GenericGenerator(name = "generator", strategy = "foreign", parameters = @Parameter(name = "property", value = "person") )
@Id
@GeneratedValue(generator = "generator")

@Column(name = "person_id", unique = true, nullable = false)
public int getPersonId() {
    return this.personId;
}

public void setPersonId(int personId) {
    this.personId = personId;
}

@OneToOne(fetch = FetchType.LAZY)
@PrimaryKeyJoinColumn
public Person getPerson() {
    return this.person;
}

public void setPerson(Person person) {
    this.person = person;
}

    @Column(name = "email", unique = true, nullable = false, length = 50)
public String getEmail() {
    return this.email;
}

public void setLoginVersuche(int loginVersuche) {
    this.loginVersuche = loginVersuche;
}

@Column(name ="loginVersuche")
public int getLoginVersuche() {
    return loginVersuche;
}
...omitted code...

}

and Person:

public class Person implements java.io.Serializable {

@Id
@GeneratedValue(strategy = IDENTITY)

@Column(name = "idPerson", unique = true, nullable = false)
public Integer getIdPerson() {
    return this.idPerson;
}

public void setIdPerson(Integer idPerson) {
    this.idPerson = idPerson;
}

@OneToOne(fetch = FetchType.LAZY, mappedBy = "person")
public User getUser() {
    return this.user;
}

public void setUser(User user) {
    this.user = user;
}
...omitted code...
}

and I have my DAO:

@Repository
public interface UserDAO extends CrudRepository<User, Integer>{
    User findByEmail(String user);
}

Now I want to update a field in authentication. For this I ask for the User object (email is unique) and set the field. After this I want to update the object in db:

User userByDB = userDAO.findByEmail(username);    
userByDB.setLoginVersuche(userByDB.getLoginVersuche()+1);
userDAO.save(userByDB);

It just doesn't update the datafield in db, but I also get no error. Does it have to do with my primary key in user that is a foreign key?

UPDATE-1

@Transactional
    @Modifying
    @Query("update User u set u.loginVersuche = u.loginVersuche+1, u.gesperrt = ?1 where u.email = ?2")
    public int incrementLoginVersuche(boolean gesperrt, String email);

UPDATE-2

23:51:43.181 TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Initializing transaction synchronization 23:51:43.182 TRACE org.springframework.transaction.interceptor.TransactionInterceptor - Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.incrementLoginVersuche] 23:51:43.183 DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Opening JPA EntityManager 23:51:43.184 TRACE org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl - Opening Hibernate Session. tenant=null, owner=org.hibernate.jpa.internal.EntityManagerImpl@71dfe394 23:51:43.184 TRACE org.hibernate.internal.SessionImpl - Opened session at timestamp: 14569591031 23:51:43.185 TRACE org.hibernate.internal.SessionImpl - Setting flush mode to: AUTO 23:51:43.186 TRACE org.hibernate.internal.SessionImpl - Setting cache mode to: NORMAL 23:51:43.189 DEBUG org.springframework.orm.jpa.EntityManagerFactoryUtils - Registering transaction synchronization for JPA EntityManager 23:51:43.207 TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Bound value [org.springframework.orm.jpa.EntityManagerHolder@1028e769] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@3d018d7d] to thread [http-nio-8443-exec-2] 23:51:43.208 TRACE org.hibernate.engine.query.spi.QueryPlanCache - Located HQL query plan in cache (update User u set u.loginVersuche = u.loginVersuche+1, u.gesperrt = ?1 where u.email = ?2) 23:51:43.210 TRACE org.springframework.transaction.support.TransactionSynchronizationManager - Retrieved value [org.springframework.orm.jpa.EntityManagerHolder@1028e769] for key [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@3d018d7d] bound to thread [http-nio-8443-exec-2] 23:51:43.214 DEBUG org.hibernate.jpa.spi.AbstractEntityManagerImpl - Mark transaction for rollback 23:51:43.215 TRACE org.springframework.transaction.interceptor.TransactionInterceptor - Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.incrementLoginVersuche] after exception: javax.persistence.TransactionRequiredException: Executing an update/delete query 23:51:43.217 TRACE org.springframework.transaction.interceptor.RuleBasedTransactionAttribute - Applying rules to determine whether transaction should rollback on javax.persistence.TransactionRequiredException: Executing an update/delete query

UPDATE-3

TransactionManager configuration:

@Bean(name = "transactionManager")
    public PlatformTransactionManager txManager() {
        return new DataSourceTransactionManager(dataSource());
    }
3

3 Answers

1
votes

According to docs (4.6. Transactionality)

CRUD methods on repository instances are transactional by default

so you don't need @Transactional(readOnly = true) annotation above your UserDAO interface. Instead, you should make your saving method transactional (assuming that your class is a valid Spring Component):

@Transactional
public void incrementVersuche() {
    ...    
    User userByDB = userDAO.findByEmail(username);    
    userByDB.setLoginVersuche(userByDB.getLoginVersuche()+1);
    userDAO.save(userByDB);
}

UPDATE

public interface IUserSaver { 
        void incrementVersucheAndSave(String username)
}

@Service
public class UserSaver implements IUserSaver {

@Transactional
public void incrementVersucheAndSave(String username) {
        User userByDB = userDAO.findByEmail(username);    
        userByDB.setLoginVersuche(userByDB.getLoginVersuche()+1);
        userDAO.save(userByDB);
      }
}

And then in your AuthProvider:

....
@Autowired
private IUserSaver saver;

 public void incrementLoginVersuche() {
   ....
   saver.incrementVersucheAndSave(username);
}
0
votes

Remove

@Transactional(readOnly = true)

from your repository. The transaction will not throw an exception: @Transactional readOnly

0
votes

The method called without a transaction. Check your tr demercations or the caller env. is not transactional.the original problem : if you set the tr RO, it won't execute update SQL commands.