spring-data-jpa 在我的服务中未调用我的存储库- Spring Data JPA

plicqrtu  于 2022-11-10  发布在  Spring
关注(0)|答案(2)|浏览(249)

我不明白为什么我的体重记录存储库没有被调用,尽管我的代码。我写了一些日志之前和之后的体重记录存储库调用。在我的控制台,我可以看到日志之前和之后被调用,但不是我的存储库。我有一个代码200 OK,但在我的数据库,我的数据总是在这里。我不明白为什么。我的Tomcat端口被设置为端口7777。我可以创建和读取数据,但不能删除它。
就在我的代码下面:实体、控制器、存储库和服务。
https://gitlab.com/genetquentin/openweighttracker_backend/-/tree/develop

/*ENTITY 'PERSON'*/
@Entity
@Table(name = "person")
public class Person implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_person")
    private Long idPerson;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "id_initial_data")
    private InitialData userInitData;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "id_user")
    private AppUser appUserPerson;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "person")
    private List<WeightRecord> weightsList = new ArrayList<WeightRecord>();

    /* Getters and setters */

      /*ENTITY 'WEIGHTRECORD'*/

        @Entity
        @Table(name = "weight_record")
        public class WeightRecord implements Serializable{

            private static final long serialVersionUID = 1L;

            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            @Column(name = "id_weight_record")
            private Long idWeightRecord;

            @Column(name = "weight_record_date", nullable = true)
            private LocalDate weightRecordDate;

            //JsonIgnore annotation because circular problem with Jackson and entity.
            @JsonIgnore
            @ManyToOne
            private Person person;

            @Min(1)
            @Max(635)
            @Column(name = "weight_kg_record", nullable = true, precision = 1)
            private Double weightKgRecord;

            @Min(1)
            @Max(99)
            @Column(name = "percent_fat_mass", nullable = true, precision = 1)
            private Double percentFatMass;

            @Min(1)
            @Max(99)
            @Column(name = "percent_muscular_mass", nullable = true, precision = 1)
            private Double percentMuscularMass;

            @Min(1)
            @Max(99)
            @Column(name = "percent_body_water", nullable = true, precision = 1)
            private Double percentBodyWater;

            @Min(1)
            @Max(99)
            @Column(name = "percent_bone_mass", nullable = true, precision = 1)
            private Double percentBoneMass

            /* Getters and setters */

            /*CONTROLLER*/    
                @CrossOrigin(origins = "*")
                @RestController
                @RequestMapping("/weights")
                public class WeightRecordController {

                    @Autowired
                    WeightRecordServiceImpl weightRecordServiceImpl;

                    @Autowired
                    WeightRecordRepository weightRecordRepository;

                    @Autowired
                    AppUserRepository appUserRepository;

                    @Autowired
                    PersonRepository personRepository;

                    private final Logger logger = LoggerFactory.getLogger(WeightRecordController.class);

                    //TODO : Weight is not deleted  and repository not called ?
                    // USE deleteinbatch method repository from jpa ?
                    @DeleteMapping("/{weightId}")
                    public ResponseEntity<WeightRecord> deleteWeigthById(@PathVariable("weightId") Long weightId, Principal principal) {
                        logger.info("DELETE /weights/{}", weightId);
                        try {

                            Long appUserConnectedId = this.getAppUserConnectedId(principal);
                            Person personConnected = personRepository.findById(appUserConnectedId).orElseThrow();
                            WeightRecord weightRecordToDelete = weightRecordRepository.findById(weightId).orElseThrow();

                            if(personConnected != null && personConnected.getWeightsList().contains(weightRecordToDelete)) {
                                logger.info("SERVICE FOR DELETING");
                                logger.info(weightRecordToDelete.getWeightKgRecord().toString());
                                return ResponseEntity.ok(weightRecordServiceImpl.deleteWeightById(weightId));
                            } else {
                                logger.info("BAD USER FOR BAD WEIGHT");
                                return ResponseEntity.notFound().build();
                            }
                        } catch (NoSuchElementException nse) {
                            return ResponseEntity.notFound().build();
                        }   
                    }
                }

    /*REPOSITORY*/

                @Repository
                public interface WeightRecordRepository extends JpaRepository<WeightRecord, Long> {
                }

    /*SERVICE*/

            @Service
            public class WeightRecordServiceImpl implements WeightRecordService {

                @Autowired
                WeightRecordRepository weightRecordRepository;

                @Autowired
                AppUserRepository appUserRepository;

                @Autowired
                PersonRepository personRepository;

                private final Logger logger = LoggerFactory.getLogger(WeightRecordServiceImpl.class);

                public WeightRecord deleteWeightById(Long weightRecordToDeleteId) {

                    logger.info("THIS IS THE WEIGHT ID TO DELETE : {}", weightRecordToDeleteId);
                    WeightRecord weightRecordToDelete = weightRecordRepository.findById(weightRecordToDeleteId).orElseThrow();
                    weightRecordRepository.deleteById(weightRecordToDeleteId);
                    logger.info("Weight with id n°{} is deleted now", weightRecordToDelete.getIdWeightRecord());
                    return weightRecordToDelete;
                }

我在《失眠》中的请求:

控制台中的请求结果:

uhry853o

uhry853o1#

"原因"
问题的原因是这部分代码

personConnected.getWeightsList().contains(weightRecordToDelete)

"为什么会这样“
有一个spring数据属性spring.jpa.open-in-view,默认为true,表示在整个HTTP请求过程中JPA Persistent Context(Hibernate Session)是打开的。What is this spring.jpa.open-in-view=true property in Spring Boot?
如果打开了Persistent Context,则下面代码中的fetch = FetchType.LAZY属性根本不起作用。

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "person")
private List<WeightRecord> weightsList = new ArrayList<WeightRecord>();

当您执行personConnected.getWeightsList().conatins()时,Hibernate会在后台加载weightsList,这就是为什么会记录最后一个SQL日志条目。

select
        weightslis0_.person_id_person as person_i8_3_0_
    ...
    from
        weight_record weightslis0_ 
    where
        weightslis0_.person_id_person=?

所以当你删除一个WeightRecord时,它仍然保留在Person.weightsList中,因为它是由personConnected.getWeightsList().conatins()加载的。
当HTTP请求完成后,Persistent Context关闭,Hibernate将刷新所有对数据库的更改。在Person端有cascade = CascadeType.ALL,因此Hibernate应该记住weightsList中已删除的WeightRecord。因此它什么也不做,因为您可以删除WeightRecord,然后出于某些原因再次插入它。
你可以通过删除personConnected.getWeightsList().conatins()部分代码来验证上面的语句,删除将开始工作。

如何解决

1.在application.property中设置spring.jpa.open-in-view=false。这样,您将得到LazyInitializationExceptionpersonConnected.getWeightsList().conatins()
1.删除personConnected.getWeightsList().conatins()代码。您可以通过比较WeightRecord.Person.id和当前的Person.id来执行相同的操作。

Optional<Person> personConnected = personRepository.findById(appUserConnectedId);
        if (personConnected.isPresent()  ) {
            WeightRecord weightRecordToDelete = weightRecordRepository.findById(weightId).orElseThrow();
            Long userPersonId = personConnected.get().getIdPerson();
            Long recordPersonId = weightRecordToDelete.getPerson().getIdPerson();
            if (Objects.equals(userPersonId, recordPersonId)) {
                logger.info("SERVICE FOR DELETING");
                return ResponseEntity.ok(weightRecordServiceImpl.deleteWeightById(weightRecordToDelete));
            }
        }

        logger.info("BAD USER FOR BAD WEIGHT");
        return ResponseEntity.notFound().build();

现在,您可以将cascade = CascadeType.ALL保留在Person侧的weightsList上。

备注

永远使用spring.jpa.open-in-view=falseLazyInitializationException是您的朋友。
在关联的@ManyToOne部分始终使用fetch = FetchType.LAZY。默认情况下为FetchType.EAGER

@ManyToOne(fetch = FetchType.LAZY)
    private Person person;

永远不要对实体使用Jackson注解(如@JsonIgnore)和Hibernate Validator注解(如@Min(1))。对实体使用PersonEntity类别,对JSON使用Person。在服务层级上进行Map。

s6fujrry

s6fujrry2#

事实上,我刚刚从我的@OneToMany实体Person中删除了CascadeType.ALL,并且一切正常。我可以从数据库中创建、读取和删除记录。
但我将进一步研究这个问题,以便更好地了解它的起源。

@Entity
    @Table(name = "person")
    public class Person implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_person")
    private Long idPerson;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "id_initial_data")
    private InitialData userInitData;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "id_user")
    private AppUser appUserPerson;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "person")
    private List<WeightRecord> weightsList = new ArrayList<WeightRecord>();

相关问题