【问题标题】:CRUDRepository::delete executes silently while the execution should violate FK and throw an exceptionCRUDRepository::delete 静默执行,而执行应该违反 FK 并抛出异常
【发布时间】:2019-01-29 16:08:45
【问题描述】:

下午好,

使用 Spring Boot + JPA + Hibernate,我期待在尝试删除仍被另一个实体引用的实体时出现异常(违反我的 FK 约束)。我正在使用 CRUDRepository,我希望能够从 delete 或 deleteById 调用中捕获 DataIntegrityViolationException。

但是,什么都没有发生,日志只显示一个选择查询:

Hibernate: select jpauserpas0_.id as id1_4_0_, jpauserpas0_.cdate as cdate2_4_0_, jpauserpas0_.password as password3_4_0_, jpauser1_.id as id1_3_1_, jpauser1_.cdate as cdate2_3_1_, jpauser1_.pid as pid4_3_1_, jpauser1_.username as username3_3_1_ from user_password jpauserpas0_ left outer join "user" jpauser1_ on jpauserpas0_.id=jpauser1_.pid where jpauserpas0_.id=?

有人知道为什么会这样吗?这是正常行为吗?

型号:

public class JPAUser
{
    @Id
    @GeneratedValue(generator = "genuser", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "genuser", sequenceName = "user_id_seq", allocationSize=1)
    public Integer id;

    @Column(unique = true)
    public String username;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "pid")
    public JPAUserPassword password;

    public JPAUser(String username, JPAUserPassword password)
    {
        this.username = username;
        this.password = password;
    }

    ...
}

public class JPAUserPassword
{
    @Id
    @GeneratedValue(generator = "genupwd", strategy = GenerationType.SEQUENCE)
    @SequenceGenerator(name = "genupwd", sequenceName = "upwd_id_seq", allocationSize=1)
    public Integer id;

    @Column(nullable=false, updatable=false)
    public String value;

    @OneToOne(mappedBy = "password")
    public JPAUser user;

    public JPAUserPassword(String value)
    {
        this.password = value;
    }
    ...
}

代码:

@Configuration
@EnableAutoConfiguration
@ComponentScan
@EntityScan("model.package.name")
@EnableJpaRepositories("repository.package.name")
public class JPATestConfiguration
{
    @Autowired
    Environment env;

    @Bean
    public DataSource dataSource()
    {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("spring.datasource.driver-class-name"));
        dataSource.setUrl(env.getProperty("spring.datasource.url"));
        dataSource.setUsername(env.getProperty("spring.datasource.username"));
        dataSource.setPassword(env.getProperty("spring.datasource.password"));

        return dataSource;
    }
}

@Repository
public interface JPAUserRepository extends CrudRepository<JPAUser, Integer>
{
}

@Repository
public interface JPAUserPasswordRepository extends CrudRepository<JPAUserPassword, Integer>
{
}

@RunWith(SpringJUnit4ClassRunner.class)
@TestPropertySource("classpath:/application-test.properties")
@SpringBootTest(classes={JPATestConfiguration.class})
public class JPAUserPasswordTest
{
    @Autowired
    private JPAUserPasswordRepository userPasswordRepository;

    @Autowired
    private JPAUserRepository userRepository;

    private final String USERNAME = "username";
    private final String PASSWORD = "password";

    @Test
    public void deleteAttachedEntity()
    {
        JPAUserPassword jpaUserPassword = new JPAUserPassword(PASSWORD);
        JPAUser jpaUser = new JPAUser(USERNAME, jpaUserPassword);

        userRepository.save(jpaUser);

        int id = jpaUserPassword.getId();

        // any of the two should throw the exception
        userPasswordRepository.delete(jpaUserPassword);
        userPasswordRepository.deleteById(id);
    }
}

应用程序测试属性:

spring.datasource.url=jdbc:postgresql://localhost/MYDB?useSSL=false
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

依赖关系:

  • Spring Boot 2.1.2.RELEASE
  • Postgresql 42.2.5
  • Hibernate Core 5.3.7.Final

【问题讨论】:

  • 删除 cascade.ALL 就可以了...
  • 同意,但我仍然希望能够级联删除用户,所以这对我不起作用。而且它仍然没有告诉我为什么尝试直接删除外国实体不会触发完整性违规。

标签: java hibernate spring-boot jpa


【解决方案1】:
// any of the two should throw the exception
userPasswordRepository.delete(jpaUserPassword);
userPasswordRepository.deleteById(id);

Above will delete record from child entity JPA`enter code here`UserPassword, so constraint violation error won't come.

If constraint violation is required on JPAUser, then remove CascadeType.ALL or make it CascadeType.PERSIST

【讨论】:

  • 这对我来说仍然没有意义。如果直接与孩子互动,我可能仍想享受级联政策对父子关系的好处,同时注意违反约束。
猜你喜欢
  • 2012-03-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-29
  • 2011-10-17
相关资源
最近更新 更多