【问题标题】:Updating Column of OneToOne with Spring Repo使用 Spring Repo 更新 OneToOne 的列
【发布时间】:2016-06-15 19:20:52
【问题描述】:

我的 Spring Boot 应用程序中有两个实体:

用户:

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...

}

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...
}

我有我的 DAO:

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

现在我想更新身份验证中的字段。为此,我要求用户对象(电子邮件是唯一的)并设置字段。在此之后我想更新 db 中的对象:

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

它只是不更新​​数据库中的数据字段,但我也没有收到错误。是否与我在用户中作为外键的主键有关?

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 - 初始化事务同步 23:51:43.182 TRACE org.springframework.transaction.interceptor.TransactionInterceptor - 获取 [org.springframework.data.jpa.repository.support.SimpleJpaRepository.incrementLoginVersuche] 的交易 23:51:43.183 调试 org.springframework.orm.jpa.EntityManagerFactoryUtils - 打开 JPA EntityManager 23:51:43.184 TRACE org.hibernate.internal.SessionFactoryImpl$SessionBuilderImpl - 打开休眠会话。租户=null,所有者=org.hibernate.jpa.internal.EntityManagerImpl@71dfe394 23:51:43.184 TRACE org.hibernate.internal.SessionImpl - 在时间戳打开会话:14569591031 23:51:43.185 TRACE org.hibernate.internal.SessionImpl - 将刷新模式设置为:AUTO 23:51:43.186 TRACE org.hibernate.internal.SessionImpl - 将缓存模式设置为:NORMAL 23:51:43.189 调试 org.springframework.orm.jpa.EntityManagerFactoryUtils - 为 JPA EntityManager 注册事务同步 23:51:43.207 跟踪 org.springframework.transaction.support.TransactionSynchronizationManager - 键 [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@3d018d7d] 的绑定值 [org.springframework.orm.jpa.EntityManagerHolder@1028e769] 到线程 [http -nio-8443-exec-2] 23:51:43.208 TRACE org.hibernate.engine.query.spi.QueryPlanCache - 位于缓存中的 HQL 查询计划(更新用户 u 设置 u.loginVersuche = u.loginVersuche+1,u.gesperrt = ?1 where u.email = ?2) 23:51:43.210 TRACE org.springframework.transaction.support.TransactionSynchronizationManager - 键 [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@3d018d7d] 的检索值 [org.springframework.orm.jpa.EntityManagerHolder@1028e769] 绑定到线程 [ http-nio-8443-exec-2] 23:51:43.214 调试 org.hibernate.jpa.spi.AbstractEntityManagerImpl - 将事务标记为回滚 23:51:43.215 TRACE org.springframework.transaction.interceptor.TransactionInterceptor - 在异常发生后完成 [org.springframework.data.jpa.repository.support.SimpleJpaRepository.incrementLoginVersuche] 的事务:javax.persistence.TransactionRequiredException:执行更新/删除询问 23:51:43.217 TRACE org.springframework.transaction.interceptor.RuleBasedTransactionAttribute - 应用规则来确定事务是否应在 javax.persistence.TransactionRequiredException 上回滚:执行更新/删除查询

UPDATE-3

TransactionManager 配置:

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

【问题讨论】:

    标签: java spring spring-mvc spring-data


    【解决方案1】:

    根据docs (4.6. Transactionality)

    存储库实例上的 CRUD 方法默认是事务性的

    所以你不需要在UserDAO 接口上方添加@Transactional(readOnly = true) 注释。 相反,您应该使您的保存方法具有事务性(假设您的类是有效的 Spring 组件):

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

    更新

    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);
          }
    }
    

    然后在您的 AuthProvider 中:

    ....
    @Autowired
    private IUserSaver saver;
    
     public void incrementLoginVersuche() {
       ....
       saver.incrementVersucheAndSave(username);
    }
    

    【讨论】:

    • 好的,我实现了自己的查询(请参阅更新的答案),但这也行不通。所有注释都来自 spring 包。出现错误:Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query 但更重要的是: 为什么我的第一次尝试不起作用?想明白这一点。
    • @Andy,请在您调用incrementLoginVersuche 方法的地方分享您的配置和服务。在您的配置中,您应该有类似&lt;tx:annotation-driven transaction-manager="transactionManager"/&gt; 的内容。你有吗?另请注意,您的存储库不需要@Repository 注释,因为它扩展了CrudRepository。最后,由于您没有使用本机查询,我认为您应该编写查询,例如 @Query("update User u set u.loginVersuche = u.loginVersuche+1, u.gesperrt = :gesperrt where u.email = :email")
    • 参见 UPDATE-3,我使用的是 Java 配置而不是命名空间。最重要的是,我更新了我的查询,如你所说:@Query("update User u set u.loginVersuche = u.loginVersuche+1, u.gesperrt = :gesperrt where u.email = :email") public int incrementLoginVersuche(@Param("gesperrt") boolean gesperrt, @Param("email")String email); 也许看看@TheBitman。
    • 您的配置上方有@EnableTransactionManagement 注释吗?另外,(只是为了再次确定)检查您的所有注释是否来自 spring。
    • 是的,我在 AppConfig 中做过,所有注释都来自 spring。
    【解决方案2】:

    在没有事务的情况下调用的方法。检查您的 tr demercations 或调用者环境。不是事务性的。原始问题:如果您设置了 tr RO,它将不会执行更新 SQL 命令。

    【讨论】:

    • 好的,请看看我对@Enigo 所做的评论。我现在正在调用“incrementLoginVersuche”(请参阅​​ UPDATE-1),但现在得到 Caused by: javax.persistence.TransactionRequiredException: Executing an update/delete query @Transactional 已设置为您可以看到的。我通过 Authentication-Provider 的 authenticate-method 调用该方法。
    • 这并没有改变事实:你调用了没有 tr 的方法。也许堆栈跟踪可能是一个 goog 起点
    • 我是不是误会了什么?我的 customAuthenticationProvider 有一个覆盖方法:@Override public Authentication authenticate(Authentication authentication) throws AuthenticationException {...} 在这个函数中,我调用了标记为事务的incrementLoginVersuche。所以它应该开始一个新的交易,不是吗?
    • tr-less 的原因可能在环境中。错误的设置,缺少连接等。堆栈跟踪可能会显示它:)
    • 但是,为什么我不能读取我的用户对象,设置一个新值并保存它呢?一切都与交易有关吗?我想用@Transactional 注释authenticate,但我不能因为它的覆盖。 authenticatemethod 是通过 Spring Security 调用的,方法是使用 ajax 将 post 发送到 /login。
    【解决方案3】:

    删除

    @Transactional(readOnly = true)

    来自您的存储库。事务不会抛出异常: @Transactional readOnly

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-11
      • 2020-03-06
      • 2021-08-16
      • 2016-03-29
      • 2020-07-08
      • 2023-04-02
      相关资源
      最近更新 更多