Java 删除不使用 JpaRepository

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22688402/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 17:18:08  来源:igfitidea点击:

Delete Not Working with JpaRepository

javahibernatejpaspring-dataspring-data-jpa

提问by Twisty McGee

I have a spring 4 app where I'm trying to delete an instance of an entity from my database. I have the following entity:

我有一个 spring 4 应用程序,我试图从我的数据库中删除一个实体的实例。我有以下实体:

@Entity
public class Token implements Serializable {

    @Id
    @SequenceGenerator(name = "seqToken", sequenceName = "SEQ_TOKEN", initialValue = 500, allocationSize = 1)
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seqToken")
    @Column(name = "TOKEN_ID", nullable = false, precision = 19, scale = 0)
    private Long id;

    @NotNull
    @Column(name = "VALUE", unique = true)
    private String value;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "USER_ACCOUNT_ID", nullable = false)
    private UserAccount userAccount;

    @Temporal(TemporalType.TIMESTAMP)
    @Column(name = "EXPIRES", length = 11)
    private Date expires;

    ...
    // getters and setters omitted to keep it simple
}

I have a JpaRepository interface defined:

我定义了一个 JpaRepository 接口:

public interface TokenRepository extends JpaRepository<Token, Long> {

    Token findByValue(@Param("value") String value);

}

I have a unit test setup that works with an in memory database (H2) and I am pre-filling the database with two tokens:

我有一个与内存数据库 (H2) 一起使用的单元测试设置,我正在用两个令牌预填充数据库:

@Test
public void testDeleteToken() {
    assertThat(tokenRepository.findAll().size(), is(2));
    Token deleted = tokenRepository.findOne(1L);
    tokenRepository.delete(deleted);
    tokenRepository.flush();
    assertThat(tokenRepository.findAll().size(), is(1));
}

The first assertion passes, the second fails. I tried another test that changes the token value and saves that to the database and it does indeed work, so I'm not sure why delete isn't working. It doesn't throw any exceptions either, just doesn't persist it to the database. It doesn't work against my oracle database either.

第一个断言通过,第二个断言失败。我尝试了另一个更改令牌值并将其保存到数据库的测试,它确实有效,所以我不确定为什么 delete 不起作用。它也不会抛出任何异常,只是不会将其持久化到数据库中。它也不适用于我的 oracle 数据库。



Edit

编辑

Still having this issue. I was able to get the delete to persist to the database by adding this to my TokenRepository interface:

还是有这个问题。通过将其添加到我的 TokenRepository 接口,我能够将删除保留到数据库中:

@Modifying
@Query("delete from Token t where t.id = ?1")
void delete(Long entityId);

However this is not an ideal solution. Any ideas as to what I need to do to get it working without this extra method?

然而,这不是一个理想的解决方案。关于在没有这种额外方法的情况下我需要做什么才能让它工作的任何想法?

回答by Bitman

Your initial value for id is 500. That means your id starts with 500

您的 id 初始值为 500。这意味着您的 id 以 500 开头

@SequenceGenerator(name = "seqToken", sequenceName = "SEQ_TOKEN",
initialValue = 500, allocationSize = 1)

And you select one item with id 1 here

然后您在此处选择 id 为 1 的一项

 Token deleted = tokenRepository.findOne(1L);

So check your database to clarify that

因此,请检查您的数据库以澄清

回答by Davi Arimateia

I had the same problem

我有同样的问题

Perhaps your UserAccount entity has an @OneToMany with Cascade on some attribute.

也许您的 UserAccount 实体在某些属性上有一个带有级联的 @OneToMany。

I've just remove the cascade, than it could persist when deleting...

我刚刚删除了级联,删除时它可能会持续存在......

回答by Lucas Holt

I just went through this too. In my case, I had to make the child table have a nullable foreign key field and then remove the parent from the relationship by setting null, then calling save and delete and flush.

我也刚刚经历了这个。在我的例子中,我必须让子表有一个可为空的外键字段,然后通过设置空值从关系中删除父表,然后调用保存、删除和刷新。

I didn't see a delete in the log or any exception prior to doing this.

在执行此操作之前,我没有在日志中看到删除或任何异常。

回答by Eruvanos

If you use an newer version of Spring Data, you could use deleteBy syntax...so you are able to remove one of your annotations :P

如果您使用较新版本的 Spring Data,则可以使用 deleteBy 语法……这样您就可以删除其中一个注释:P

the next thing is, that the behaviour is already tract by a Jira ticket: https://jira.spring.io/browse/DATAJPA-727

接下来的事情是,行为已经被 Jira 票所吸引:https: //jira.spring.io/browse/DATAJPA-727

回答by Taimur

You need to add PreRemove function ,in the class where you have many object as attribute e.g in Education Class which have relation with UserProfile Education.java

您需要在具有许多对象作为属性的类中添加 PreRemove 函数,例如在与 UserProfile Education.java 相关的 Education Class 中

private Set<UserProfile> userProfiles = new HashSet<UserProfile>(0);

@ManyToMany(fetch = FetchType.EAGER, mappedBy = "educations")
public Set<UserProfile> getUserProfiles() {
    return this.userProfiles;
}

@PreRemove
private void removeEducationFromUsersProfile() {
    for (UsersProfile u : usersProfiles) {
        u.getEducationses().remove(this);
    }
}

回答by pzeszko

Most probably such behaviour occurs when you have bidirectional relationship and you're not synchronizing both sides WHILE having both parent and child persisted (attached to the current session).

当您具有双向关系并且您没有同步双方而同时保留父子和子子(附加到当前会话)时,很可能会发生这种行为。

This is tricky and I'm gonna explain this with the following example.

这很棘手,我将用下面的例子来解释这一点。

@Entity
public class Parent {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Long id;

    @OneToMany(cascade = CascadeType.PERSIST, mappedBy = "parent")
    private Set<Child> children = new HashSet<>(0);

    public void setChildren(Set<Child> children) {
        this.children = children;
        this.children.forEach(child -> child.setParent(this));
    }
}
@Entity
public class Child {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "parent_id")
    private Parent parent;

    public void setParent(Parent parent) {
        this.parent = parent;
    }
}

Let's write a test (a transactional one btw)

让我们写一个测试(顺便说一句,一个事务性的)

public class ParentTest extends IntegrationTestSpec {

    @Autowired
    private ParentRepository parentRepository;

    @Autowired
    private ChildRepository childRepository;

    @Autowired
    private ParentFixture parentFixture;

    @Test
    public void test() {
        Parent parent = new Parent();
        Child child = new Child();

        parent.setChildren(Set.of(child));
        parentRepository.save(parent);

        Child fetchedChild = childRepository.findAll().get(0);
        childRepository.delete(fetchedChild);

        assertEquals(1, parentRepository.count());
        assertEquals(0, childRepository.count()); // FAILS!!! childRepostitory.counts() returns 1
    }
}

Pretty simple test right? We're creating parent and child, save it to database, then fetching a child from database, removing it and at last making sure everything works just as expected. And it's not.

很简单的测试吧?我们正在创建父级和子级,将其保存到数据库中,然后从数据库中获取一个子级,将其删除,最后确保一切都按预期工作。事实并非如此。

The delete here didn't work because we didn't synchronized the other part of relationship which is PERSISTED IN CURRENT SESSION. If Parent wasn't associated with current session our test would pass, i.e.

这里的删除不起作用,因为我们没有同步在当前会话中持久化的关系的另一部分。如果 Parent 与当前会话无关,我们的测试将通过,即

@Component
public class ParentFixture {
    ...
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void thereIsParentWithChildren() {
        Parent parent = new Parent();
        Child child = new Child();
        parent.setChildren(Set.of(child));

        parentRepository.save(parent);
    }
} 

and

@Test
public void test() {
    parentFixture.thereIsParentWithChildren(); // we're saving Child and Parent in seperate transaction

    Child fetchedChild = childRepository.findAll().get(0);
    childRepository.delete(fetchedChild);

    assertEquals(1, parentRepository.count());
    assertEquals(0, childRepository.count()); // WORKS!
}

Of course it only proves my point and explains the behaviour OP faced. The proper way to go is obviously keeping in sync both parts of relationship which means:

当然,它只证明了我的观点并解释了 OP 面临的行为。正确的方法显然是保持关系的两个部分同步,这意味着:

class Parent {
    ...
     public void dismissChild(Child child) {
         this.children.remove(child);
     }

     public void dismissChildren() {
        this.children.forEach(child -> child.dismissParent()); // SYNCHRONIZING THE OTHER SIDE OF RELATIONSHIP 
        this.children.clear();
     }

}

class Child {
    ...
    public void dismissParent() {
        this.parent.dismissChild(this); //SYNCHRONIZING THE OTHER SIDE OF RELATIONSHIP
        this.parent = null;
    }
}

Obviously @PreRemovecould be used here.

显然@PreRemove可以在这里使用。

回答by venge

One way is to use cascade = CascadeType.ALLlike this in your userAccount service:

一种方法是cascade = CascadeType.ALL在您的 userAccount 服务中像这样使用:

@OneToMany(cascade = CascadeType.ALL)
private List<Token> tokens;

Then do something like the following (or similar logic)

然后执行以下操作(或类似的逻辑)

@Transactional
public void deleteUserToken(Token token){
    userAccount.getTokens().remove(token);
}

Notice the @Transactionalannotation. This will allow Spring (Hibernate) to know if you want to either persist, merge, or whatever it is you are doing in the method. AFAIK the example above shouldwork as if you had no CascadeType set, and call JPARepository.delete(token).

注意@Transactional注释。这将允许 Spring (Hibernate) 知道您是要持久化、合并还是要在方法中执行任何操作。AFAIK 上面的例子应该像你没有设置 CascadeType 一样工作,并调用JPARepository.delete(token).

回答by Federico Quaglia

I've the same problem, test is ok but on db row isn't deleted.

我有同样的问题,测试没问题,但没有删除 db 行。

have you added the @Transactional annotation to method? for me this change makes it work

您是否在方法中添加了 @Transactional 注释?对我来说,这个改变使它起作用

回答by Sham Fiorin

In my case was the CASCADE.PERSIST, i changed for CASCADE.ALL, and made the change through the cascade (changing the father object).

在我的例子中是 CASCADE.PERSIST,我更改为 CASCADE.ALL,并通过级联进行更改(更改父对象)。

回答by Dejan

CascadeType.PERSIST and orphanRemoval=true doesn't work together.

CascadeType.PERSIST 和 orphanRemoval=true 不能一起工作。