Keling, oddiy Spring boot dasturini ko'rib chiqaylik:

pom.xml Bahorga bog'liqliklar

...
<!-- spring -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
...

Ma'lumotlar bazasiga ulanish uchun zarur bo'lganlardan tashqari, bizning xususiyatlar fayllarimizda alohida sozlamalar yo'q.

Qat'iylikni boshqarish uchun konteyner tomonidan boshqariladigan tranzaksiya ko'lamli JPA EntityManager dan foydalanish.

Men o'zimga yaxshi tanish bo'lgan shubhali tushunchalarni qo'yish uchun bir necha qator kodlar etarli ekanligini siz bilan baham ko'rmoqchiman.

Keling, quyidagi parchalarni ko'rib chiqaylik, bu juda oddiy, bizda ikkita ob'ekt, kontroller va xizmat mavjud.

Savol beruvchi ob'ekt

@Entity
@Table(name = "Question")
public class Question {
    @Id
    @GeneratedValue
    @Column(name = "id")
    private Long id;
    @Column(name = "description")
    private String description;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "survey_id")
    private Survey survey;
 
    ...
}

So'rov ob'ekti

@Entity
@Table(name = "Survey")
public class Survey implements Serializable {
    @Id
    @GeneratedValue
    @Column(name = "id")
    private Long id;
    @Column(name = "name")    
    private String name;
   @OneToMany(fetch = FetchType.LAZY, mappedBy = "survey")
   private List<Question> questions;
   ...
}

Dam olish boshqaruvchisi

@RestController
@RequestMapping("/default")
public class DefaultEndpoint {

  
  @Autowired
  private MyService myService;

  
  @PostMapping(value = "/foo")
  public void foo() {
        myService.foo();
    }
 ...
}

Xizmat

@Service
public class MyService {

    @PersistenceContext
    private EntityManager entityManager;

    public void foo() {
        Survey survey = entityManager.find(Survey.class, 1L);
        System.out.println(survey.getQuestions().size());
    }
}

MyService sinfini batafsil ko'rib chiqing.

Konsolimda chiroyli tarzda chop etilgan so'rovnoma savollarining haqiqiy sonini ko'rganimda nima uchun hayron bo'lganimni tushuna olasizmi?

Ikkita sabab:

  • Soʻrov endi qatʼiylik kontekstiga biriktirilmaydi, chunki faol qatʼiylik konteksti yoʻq, chunki usulda @Transactional izohi yoʻq.
  • Savollarni olish siyosatiLAZYshuning uchun natijalar ma'lumotlar bazasidan olinishi uchun qat'iylik konteksti faol bo'lishi kerak.

Shunday qilib, men OSIV (Open Session In View) naqshini bilgan paytim edi.

Va bu, asosan, quyidagilardan qochish haqida:org.hibernate.LazyInitializationExceptionbu yuqoridagi kodning OSIV o‘rnatilmagani bilan bajarilishi natijasida yuzaga keladi. Istisno ob'ekt (bizning holatlarimizda so'rov ob'ekti) alohida holatda ekanligini bildiradi, chunki uni olish uchun foydalanilgan qat'iylik konteksti allaqachon yopilgan va biz so'ragan ma'lumotlar olinmagan, chunki LAZYolish siyosati qabul qilindi.

OSIV bu vaziyatni engib o'tish usuli ahamiyatsiz bo'lib, u @Transactional izohidan foydalanib, biz buni aniq so'ramasak ham, qat'iylik kontekstini faol ushlab turadi.

Gap shundaki, men har doim bu istisno va xatti-harakatni juda foydali deb bilaman, chunki u ko'plab eng yaxshi amaliyotlarni qo'llashga yordam beradi:

  • Qatlamlar orasidagi tashvishlarni ajratish: ob'ektlar taqdimot darajasiga etib bormasligi kerak, faqat DTO'lar kerak.
  • Sub'ektlar va DTOlar o'rtasidagi ajratish: Bu ajratilgan holda taqdimot mantig'i va biznes/doimiylik mantig'i ajratiladi va ular bir-biridan yaxshi darajada erkinlik bilan rivojlanishi mumkin.
  • Ma'lumotlar bazasidagi stressni cheklash: O'qish paytida ma'lumotlar bazasiga ulanish faqat DTO'larni to'ldirish uchun ma'lumotlarni olish uchun zarur bo'lgan vaqt davomida saqlanadi.

Bundan ham yomoni shundaki, bahor yuklashning avtomatik konfiguratsiyasi mexanizmi sukut bo'yicha OSIVni faol qiladi.

Xususiyatlar faylingizga quyidagi qatorni qo'shish orqali ushbu mexanizmdan voz kechish uchun uni aniq o'chirib qo'yishingiz kerak.

spring.jpa.open-in-view=false

Shunday qilib, asosan bizda ishlab chiquvchilarga tartibsiz va samarasiz kod yozishga yordam beradigan mexanizm mavjud va sukut bo'yicha yoqilgan, bundan ham yomonlashishi mumkinmi? Bo'lishi mumkin.

Keling, Xizmatning ushbu muqobil versiyasini ko'rib chiqaylik:

Esda tutingki, men OSIV qandaydir ehtiyotsiz kod bilan zaxiralanganda qandaydir ekstravagant va zararli xatti-harakatlarni keltirib chiqarishi mumkinligini koʻrsatish uchun foo usulidan @Transactional ni ataylab oʻtkazib yubordim.

@Service
public class MyService {

    @PersistenceContext
    private EntityManager entityManager;
    @Autowired
    private QuestionRepo questionRepo;
   public void foo() {
        Survey survey = entityManager.find(Survey.class, 1L);
        survey.setName("example");
        Question question = new Question();
        question.setDescription("description");
        question.setSurvey(survey);
        questionRepo.save(question);
    }
}

QuestionRepo

public interface QuestionRepo extends CrudRepository<Question, Long> {
}

Saqlash usulini bahorgi ma'lumotlarni amalga oshirish

@Transactional
public <S extends T> S save(S entity) {
    if (this.entityInformation.isNew(entity)) {
        this.em.persist(entity);
        return entity;
    } else {
        return this.em.merge(entity);
    }
}

Keling, OSIV o'chirib qo'yilgan oddiy stsenariyda nima bo'lishini tushunishga harakat qilaylik.

Savol to'g'ri saqlanadi va bizningqayta olingan so'rov ob'ektidagi nom maydonini o'zgartirishga urinishimiz e'tiborga olinmaydi, chunki ob'ekt hech qanday qat'iylik kontekstiga biriktirilmagan va biz kaskadli mantiqdan foydalanmayapmiz (qarang. yana So'rovnoma va Savol sinflari).

OSIV yoqilganda nima sodir bo'lishi juda g'alati.

So'rovnomadagi nomning o'zgarishi uning jadval qatoridagi tegishli maydonni yangilaydi.

Bu erda nima sodir bo'ladi, soddalashtirilgan so'zlar bilan aytganda, doimiylik konteksti topish operatsiyasidan so'ng faol bo'lib qoladi, shuning uchun so'rov ob'ekti ham unga bog'langan bo'lib qoladi, saqlash operatsiyasi uni yana o'z tranzaksiyasiga bog'laydigan bir xil faol qat'iylik kontekstidan foydalanadi (e'tibor bering@Transactionalsaqlash usulida).

Saqlash usuli yozish operatsiyasini amalga oshiradi, shuning uchun u tranzaktsiya oxirida doimiylik kontekstini tozalaydi va so'rov ob'ekti uchun iflos tekshirish mexanizmini ishga tushiradi.

Endi, ayniqsa, agar siz qo'ng'iroqlar zanjirini bundan biroz uzunroq deb hisoblasangiz, bu mexanizm qanday qilib muammolarni bartaraf etish juda qiyin bo'lgan kutilmagan xatti-harakatlarga olib kelishi aniq ko'rinadi.

Taxmin qilish osonki, ko'plab ilovalar allaqachon ishlab chiquvchilari bu haqda bilmasdan turib, bunday effektlarga tayanishi mumkin.

Agar siz o'qigan narsalaringiz bilan qiziqsangiz, men sizga ushbu maqolalarni yaxshi o'qib chiqishingizni maslahat beraman, u erda siz OSIV ishlari haqida batafsil ma'lumot olishingiz mumkin.