Удаление дочерних объектов в режиме гибернации (Spring JPA)

#java #spring #entity-framework #hibernate #jpa

#java #весна #entity-framework #гибернация #jpa

Вопрос:

Я изучаю Hibernate (Spring) и сталкиваюсь со странной проблемой при удалении дочерних объектов из родительского.

Вот что у меня есть:

Родительский объект:

 @Entity  
public class Company {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;

    @OneToMany(cascade = CascadeType.ALL)
    @JoinColumn(name = "company_id", referencedColumnName = "id")
    List<CompanyObject> companyObjects;
}  
  

Дочерняя сущность:

 @Entity
public class CompanyObject {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    Long id;

    @Enumerated(EnumType.STRING)
    ObjectType type;

    @ManyToOne
    @JoinColumn(name = "company_id")
    Company company;
}  
  

Вот мои определения таблиц:

 CREATE TABLE `company` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`),
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8

CREATE TABLE `company_object` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `company_id` bigint(20) NOT NULL,
  `type` varchar(50) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `FK__company` (`company_id`),
  CONSTRAINT `FK__company` FOREIGN KEY (`company_id`) REFERENCES `company` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8  
  

И, кроме того, у меня есть следующий update метод:

 // some code here
public void update(CompanyDto dto) {
    Company company = repository.getCompanyById(companyId);  
    repository.save(dto.merge(company));
}
// some code here  


public class CompanyDto {
    private List<CompanyObjectDto> companyObjects = new ArrayList<>();

    public Company merge(Company company) {        
        company.getCompanyObjects().clear();
        for (CompanyObjectDto dto : companyObjects) {
            company.getCompanyObjects().add(dto.to(company));
        }
        return company;
    }
}

public class CompanyObjectDto {
    ObjectType type;

    public CompanyObject to(Company company) {
        CompanyObject object = new CompanyObject();
        object.setType(this.getType());
        object.setCompany(company);
        return object;
    }
}
  

И как только я запускаю update метод, я получаю следующую ошибку: java.sql.SQLWarning: Column 'company_id' cannot be null . Я немного исследовал это и обнаружил, что если я закомментирую company.getCompanyObjects().clear(); строку, она работает нормально, поэтому, похоже, есть какая-то проблема с каскадным действием удаления объектов компании.

Может, пожалуйста, кто-нибудь указать мне на мои ошибки? Спасибо.

Комментарии:

1. При двунаправленном отображении должен быть только один @Joincolumn, и вам не хватает ‘mappedby’.

2. Проблема не в этом, но если вы не добавите , orphanRemoval = true к этой OneToMany аннотации (где находится список), вы обнаружите, что она никогда не удаляет строки, которые вы удалили из списка. Отсутствие этого — это то, что вызовет у вас следующую проблему.

3. @Kirinya, спасибо, это сработало. Не могли бы вы, пожалуйста, отправить свой ответ, чтобы я мог пометить его соответствующим образом?

Ответ №1:

Вы сопоставили свои объекты Company и CompanyObject двунаправленно, т. Е. Оба объекта имеют отношение к другому объекту.

В этом случае должен быть только один @Joincolumn, и один объект должен быть выбран в качестве объекта-владельца, а другой объект помечает отношение с помощью ‘mappedby’ (см. http://docs.oracle.com/javaee/6/api/javax/persistence/ManyToOne.html ).

Комментарии:

1. Еще раз спасибо!

Ответ №2:

Вы получаете сообщение об ошибке, потому что вы удаляете объекты из List , а затем используете их List в качестве ссылки на свой Company объект. Смотрите приведенный ниже код :

 private List<CompanyObjectDto> companyObjects = new ArrayList<>(); //Stmt 1
  

Приведенный выше код используется для определения списка, на который будет ссылаться приведенный ниже код :

 company.getCompanyObjects().clear(); //It will clear out all objects
    for (CompanyObjectDto dto : companyObjects) { //Iterating over empty list defined in stmt 1.
        company.getCompanyObjects().add(dto.to(company));
    }
  

Таким образом, ваш внешний ключ всегда будет иметь значение null, что недопустимо и вызывает исключение.

И ваш код работает, когда вы закомментируете List#clear строку, потому что в этом сценарии в списке уже есть некоторые объекты, на которые ссылаются ссылки, которые не были изменены.

Комментарии:

1. Спасибо за ответ, но, к сожалению, не уверен, что понимаю. Вы хотите сказать, что мне нужно назначить new ArrayList() companyObjects , чтобы заставить hibernate делать то, что я хочу?

2. Ах, я думаю, я понял. На самом деле companyObjects не пусто. Некоторые данные поступают из веб-интерфейса.