Почему строка из БД не удаляется, когда в Spring Boot существует связь «многие ко многим»? - PullRequest
0 голосов
/ 21 июня 2020

В моем приложении Spring Boot есть отношение "многие ко многим". Мне нужно создать функцию «избранного» с пользователями и рекламой. Пользователь может добавить в избранное множество объявлений, а также реклама может быть одобрена многими пользователями. Это соотношение между ними:

Класс пользователя:

@ManyToMany
    @JoinTable(name = "user_ads",
        joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name = "ads_id", referencedColumnName = "id"))
    private Set<Ads> adsFavourites = new HashSet<Ads>();

Это класс Ads:

@ManyToMany(mappedBy = "adsFavourites")
    private Set<JwtUser> userFavourites = new HashSet<JwtUser>();

Это класс контроллера:

@DeleteMapping("users/favourites/delete")
    public ResponseEntity<?> deleteUserFavourite(@RequestHeader("Authorization")String token, @RequestParam(required = true) Long userId, @RequestParam(required=true) Long adsId) throws NotFoundException, ForbiddenException {
        jwtUserServiceImplement.deleteFavourite(userId, token, adsId);
        if (jwtUserServiceImplement.isDeleted) {
            return new ResponseEntity<>("OK", HttpStatus.OK);
        } else {
            return new ResponseEntity<>("Not Found", HttpStatus.NOT_FOUND);
        }
    }

А это UserServiceImplementation:

  @Override
    public void deleteFavourite(Long userId, String token, Long adsId) throws ForbiddenException {
        JwtUser jwtUser = jwtRepository.findOneById(userId);
        if (jwtUser != null) {
            for (Ads ads : jwtUser.getAdsFavourites()) {
                if (ads.getId().equals(adsId)) {
                    jwtUser.getAdsFavourites().remove(ads);
                    isDeleted = true;
                    break;
                }
            }
        } else {
            throw new ForbiddenException();
        }
        
    }

Для вставки избранного пользователь должен нажимать на объявления, и эта функция работает нормально. Также я могу отправить список всех объявлений, которые нравятся пользователю. Но когда я хочу удалить какое-то объявление из избранного пользователя (пользователь хочет удалить рекламу из своего избранного), я не могу этого сделать. Методы выполняются нормально, Почтальон возвращает ОК, но строка из БД не удаляется. Просто хочу сказать, что я хочу удалить рекламу только из списка пользователей, а не наоборот (все логические c должны выполняться с точки зрения пользователя, а не из рекламы). Может кто-нибудь мне поможет?

1 Ответ

0 голосов
/ 21 июня 2020

Пару вещей. Используйте отладчик. Извините за снисходительность, но вы всегда должны знать, что делает ваше приложение. Вдоль этих строк включите вывод SQL, например, spring.jpa.show-sql=true.

1 Не создавайте экземпляр Collection в своем классе сущности. Это свойство предназначено для результатов запроса. Если вы хотите записать отношение, создайте набор или запросите существующий набор. Удалите new HashSet<Ads>() в своем коде, и вы получите ужасный LazyInitializationExcetion, который является вашей подсказкой для части 2.

   private Set<Ads> adsFavourites;

2 Отношения не создаются в запросах по умолчанию Repository или в по крайней мере, для отношений ManyToMany. Вам нужно сделать свой собственный запрос, чтобы специально их прочитать.

@Query("from JwtUser u left join fetch u.adsFavourites where u.id=:id")
JwtUser findByIdJoinAds(@Param("id") Long id);

3 В spring-data-jpa объект отключается после запроса, хотя, по общему признанию, я немного не уверен в этом. Мне нужно было сделать save после изменения набора adsFavourites. Вероятно, потому что ManyToMany отношениям нужна таблица соединений.

@Override
public void run(String... args) throws Exception {
    write();
    read();
    delete();
    read();
}

@Autowired
JwtUserRepository jwtRepository;
@Autowired
AdsRepository adsRepository;

private void delete() {
    System.out.println("delete");
    JwtUser jwtUser = jwtRepository.findByIdJoinAds(1L);
    for (Ads ads : jwtUser.getAdsFavourites()) {
        if (ads.getId().equals(1L)) {
            jwtUser.getAdsFavourites().remove(ads);
            break;
        }
    }
    jwtRepository.save(jwtUser);
}

private void read() {
    System.out.println("read");
    System.out.println( jwtRepository.findByIdJoinAds(1L) );
}

private void write() {
    System.out.println("write");
    JwtUser user = new JwtUser();
    
    Ads ads1 = new Ads();
    adsRepository.save(ads1);

    Ads ads2 = new Ads();
    adsRepository.save(ads2);
    
    Set<Ads> ads = new HashSet<>();
    ads.add(ads1);
    ads.add(ads2);
    user.setAdsFavourites(ads);
    
    jwtRepository.save(user);
    
}

Что дает мне:

Hibernate: drop table if exists ads CASCADE 
Hibernate: drop table if exists jwt_user CASCADE 
Hibernate: drop table if exists user_ads CASCADE 
Hibernate: create table ads (id bigint generated by default as identity, primary key (id))
Hibernate: create table jwt_user (id bigint generated by default as identity, primary key (id))
Hibernate: create table user_ads (user_id bigint not null, ads_id bigint not null, primary key (user_id, ads_id))
Hibernate: alter table user_ads add constraint FKjo0u9wqmq7jkp7ovwxg5pebec foreign key (ads_id) references ads
Hibernate: alter table user_ads add constraint FK2tmldht7773rpej1o6nfyfmr foreign key (user_id) references jwt_user
2020-06-20 22:45:37.451  INFO 3048 --- [         task-1] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-06-20 22:45:37.457  INFO 3048 --- [         task-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-06-20 22:45:37.629  INFO 3048 --- [           main] DeferredRepositoryInitializationListener : Spring Data repositories initialized!
2020-06-20 22:45:37.634  INFO 3048 --- [           main] c.m.ManytomanytestApplication            : Started ManytomanytestApplication in 1.704 seconds (JVM running for 2.136)
write
Hibernate: insert into ads (id) values (null)
Hibernate: insert into ads (id) values (null)
Hibernate: insert into jwt_user (id) values (null)
Hibernate: insert into user_ads (user_id, ads_id) values (?, ?)
Hibernate: insert into user_ads (user_id, ads_id) values (?, ?)
read
Hibernate: select jwtuser0_.id as id1_1_0_, ads2_.id as id1_0_1_, adsfavouri1_.user_id as user_id1_2_0__, adsfavouri1_.ads_id as ads_id2_2_0__ from jwt_user jwtuser0_ left outer join user_ads adsfavouri1_ on jwtuser0_.id=adsfavouri1_.user_id left outer join ads ads2_ on adsfavouri1_.ads_id=ads2_.id where jwtuser0_.id=?
JwtUser(id=1, adsFavourites=[Ads(id=1), Ads(id=2)])
delete
Hibernate: select jwtuser0_.id as id1_1_0_, ads2_.id as id1_0_1_, adsfavouri1_.user_id as user_id1_2_0__, adsfavouri1_.ads_id as ads_id2_2_0__ from jwt_user jwtuser0_ left outer join user_ads adsfavouri1_ on jwtuser0_.id=adsfavouri1_.user_id left outer join ads ads2_ on adsfavouri1_.ads_id=ads2_.id where jwtuser0_.id=?
Hibernate: select jwtuser0_.id as id1_1_0_ from jwt_user jwtuser0_ where jwtuser0_.id=?
Hibernate: select adsfavouri0_.user_id as user_id1_2_0_, adsfavouri0_.ads_id as ads_id2_2_0_, ads1_.id as id1_0_1_ from user_ads adsfavouri0_ inner join ads ads1_ on adsfavouri0_.ads_id=ads1_.id where adsfavouri0_.user_id=?
Hibernate: delete from user_ads where user_id=? and ads_id=?
read
Hibernate: select jwtuser0_.id as id1_1_0_, ads2_.id as id1_0_1_, adsfavouri1_.user_id as user_id1_2_0__, adsfavouri1_.ads_id as ads_id2_2_0__ from jwt_user jwtuser0_ left outer join user_ads adsfavouri1_ on jwtuser0_.id=adsfavouri1_.user_id left outer join ads ads2_ on adsfavouri1_.ads_id=ads2_.id where jwtuser0_.id=?
JwtUser(id=1, adsFavourites=[Ads(id=2)])

И не забывайте:

@Entity
@Data
@EqualsAndHashCode(of = "id")
@ToString(of = "id")
public class Ads {
    ...
}
...