คำอธิบายประกอบ @Transactional อยู่ที่ไหน


528

คุณควรวาง@TransactionalในDAOชั้นเรียนและ / หรือวิธีการของพวกเขาหรือมันจะดีกว่าที่จะใส่คำอธิบายประกอบชั้นเรียนบริการที่กำลังเรียกใช้วัตถุ DAO? หรือเหมาะสมที่จะอธิบายคำว่า "เลเยอร์" ทั้งคู่?

คำตอบ:


537

ฉันคิดว่าการทำธุรกรรมเป็นของเลเยอร์บริการ มันเป็นสิ่งที่รู้เกี่ยวกับหน่วยงานและการใช้เคส เป็นคำตอบที่ถูกต้องหากคุณมี DAO หลายตัวที่ถูกฉีดเข้าไปในบริการที่ต้องทำงานร่วมกันในการทำธุรกรรมครั้งเดียว


ฉันเห็นด้วยกับที่ บางครั้งมันไม่สำคัญ แต่บางครั้งคุณสามารถได้รับประโยชน์จากสิ่งนั้นเช่นช่วง Hibernate ถูกทำธุรกรรมในขณะที่ดังนั้นวัตถุที่โหลดทั้งหมดอยู่ในแคชระดับที่ 1 และคุณไม่จำเป็นต้องใส่วัตถุอีกครั้งเพื่อเซสชั่นอีกครั้งบวกโหลดอย่างเกียจคร้าน ฟังก์ชั่นคุณสมบัติโดยไม่ต้องฝอย
dma_k

5
ธุรกรรมทั่วโลกสามารถประกอบด้วยมากกว่าหนึ่งธุรกรรม @Transactional เหล่านี้ (propagation = Propagation.REQUIRED) หรือ @Transactional เป็นเขตแดนสำหรับการทำธุรกรรมเสมอ ผมไม่แน่ใจว่าผมได้รับมันจากเอกสาร แต่มันดูเหมือนว่าบิตที่คุณสามารถสร้างการทำธุรกรรมแม้ที่ประกอบด้วยวิธีการ @Transactional และทุกอย่างที่วิ่งภายใน
Lisak

1
ใช่ฉันคิดว่า @Transactional ด้านนอกสุดจะกลายเป็นขอบเขตของธุรกรรมหากการเผยแพร่เผยแพร่เปิดใช้งานอยู่
duffymo


309

โดยทั่วไปฉันเห็นด้วยกับคนอื่น ๆ ที่ระบุว่าการทำธุรกรรมมักจะเริ่มในระดับการให้บริการ (ขึ้นอยู่กับความละเอียดที่คุณต้องการแน่นอน)

อย่างไรก็ตามในเวลาเดียวกันฉันก็เริ่มเพิ่ม@Transactional(propagation = Propagation.MANDATORY)ไปที่เลเยอร์ DAO ของฉัน (และเลเยอร์อื่น ๆ ที่ไม่ได้รับอนุญาตให้เริ่มต้นการทำธุรกรรม แต่ต้องมีคนที่มีอยู่) เพราะมันง่ายกว่ามากในการตรวจสอบข้อผิดพลาดที่คุณลืมเริ่มต้นธุรกรรมในผู้โทร เช่นบริการ) หาก DAO ของคุณมีคำอธิบายประกอบด้วยการแพร่กระจายภาคบังคับคุณจะได้รับข้อยกเว้นระบุว่าไม่มีธุรกรรมที่ใช้งานอยู่เมื่อมีการเรียกใช้เมธอด

ฉันยังมีการทดสอบการรวมที่ฉันจะตรวจสอบถั่วทั้งหมด (bean post processor) สำหรับคำอธิบายประกอบนี้และล้มเหลวหากมีการเพิ่มความคิดเห็นที่@Transactionalมีการแพร่กระจายอื่น ๆ นอกเหนือจากการบังคับใช้ใน bean ที่ไม่ได้อยู่ในเลเยอร์บริการ ด้วยวิธีนี้ฉันแน่ใจว่าเราจะไม่เริ่มต้นการทำธุรกรรมในชั้นที่ผิด


3
ควรทำสิ่งนี้ในชั้น dao (อินเตอร์เฟส) ใน dao impl เลเยอร์หรือทั้งสองอย่าง?
Johan

3
ฉันไม่ทราบว่าเหมาะกับการสนทนานี้หรือไม่ แต่อาจมีเคล็ดลับอีกอย่างหนึ่งที่สามารถเพิ่ม @Transactional (readOnly = true) ใน dao ซึ่งหมายถึงการดำเนินการที่ไม่ได้เขียน
maxivis

10
@Johan Spring แนะนำให้ใส่หมายเหตุประกอบธุรกรรมบนคลาสการนำไปใช้แทนที่จะเป็นอินเตอร์เฟส
งัดกรัม

ฉันชอบความคิดนี้จริง ๆ ลองทำเพื่อโปรเจ็กต์ของฉันด้วย
โทมัสไอน์วอลเลอร์

1
Lemme ดูว่าฉันเข้าใจหรือไม่ ... คุณกำลังพูดว่าฉันควรใส่@Transactionalคลาสการนำไปใช้งานของบริการและฉันควรใส่@Transactional(propagation = MANDATORY)ในการใช้งานคลาส DAO (พื้นที่เก็บข้อมูล) หรือไม่
John Henckel

93

คำอธิบายประกอบการทำธุรกรรมควรวางไว้รอบการดำเนินงานทั้งหมดที่แยกออกไม่ได้

ตัวอย่างเช่นการโทรของคุณคือ "เปลี่ยนรหัสผ่าน" ที่ประกอบด้วยสองการดำเนินงาน

  1. เปลี่ยนรหัสผ่าน
  2. ตรวจสอบการเปลี่ยนแปลง
  3. ส่งอีเมลถึงลูกค้าว่ารหัสผ่านมีการเปลี่ยนแปลง

ดังนั้นในข้างต้นหากการตรวจสอบล้มเหลวควรเปลี่ยนรหัสผ่านด้วยหรือไม่ ถ้าเป็นเช่นนั้นการทำธุรกรรมควรจะประมาณ 1 และ 2 (ดังนั้นที่ชั้นบริการ) หากอีเมลล้มเหลว (น่าจะมีความล้มเหลวที่ปลอดภัยในเรื่องนี้ดังนั้นมันจะไม่ล้มเหลว) ดังนั้นมันควรย้อนกลับรหัสผ่านการเปลี่ยนแปลงและการตรวจสอบหรือไม่?

@Transactionalเหล่านี้เป็นชนิดของคำถามที่คุณจะต้องมีการถามว่าการตัดสินใจที่จะใส่


41

คำตอบที่ถูกต้องสำหรับสถาปัตยกรรมสปริงดั้งเดิมคือการวางความหมายของธุรกรรมไว้ในคลาสบริการเนื่องจากเหตุผลที่คนอื่น ๆ ได้อธิบายไว้แล้ว

แนวโน้มที่เกิดขึ้นใหม่ในฤดูใบไม้ผลิคือไปสู่การออกแบบที่ขับเคลื่อนด้วยโดเมน (DDD) Spring Rooเป็นตัวอย่างของแนวโน้มที่ดี แนวคิดคือการทำให้โดเมนวัตถุ POJOs สมบูรณ์ยิ่งขึ้นกว่าในสถาปัตยกรรม Spring ทั่วไป (โดยปกติจะเป็นโลหิตจาง ) และโดยเฉพาะอย่างยิ่งการทำธุรกรรมและความหมายการคงอยู่บนวัตถุโดเมนเอง ในกรณีที่จำเป็นทั้งหมดคือการดำเนินการ CRUD อย่างง่ายผู้ควบคุมเว็บดำเนินการโดยตรงบน POJOs วัตถุโดเมน (พวกเขาทำงานเป็นหน่วยงานในบริบทนี้) และไม่มีระดับบริการ ในกรณีที่จำเป็นต้องมีการประสานงานระหว่างวัตถุโดเมนคุณสามารถมี Service bean ที่จัดการได้ด้วย@Transactionตามประเพณี คุณสามารถตั้งค่าการเผยแพร่ธุรกรรมบนวัตถุโดเมนเป็นสิ่งที่ต้องการREQUIREDเพื่อให้วัตถุโดเมนใช้ธุรกรรมที่มีอยู่เช่นธุรกรรมที่เริ่มต้นที่ service bean

เทคนิคเทคนิคนี้ทำให้การใช้ AspectJ <context:spring-configured />และ Roo ใช้คำจำกัดความประเภท AspectJ เพื่อแยกความหมายเอนทิตี (การทำธุรกรรมและการคงอยู่) จากวัตถุสิ่งของในโดเมน (โดยทั่วไปคือฟิลด์และวิธีการทางธุรกิจ)


38

กรณีปกติคือคำอธิบายประกอบในระดับเลเยอร์บริการ แต่ขึ้นอยู่กับความต้องการของคุณ

คำอธิบายประกอบในเลเยอร์บริการจะส่งผลให้การทำธุรกรรมนานกว่าคำอธิบายประกอบในระดับ DAO ขึ้นอยู่กับระดับการแยกธุรกรรมที่สามารถแยกแยะปัญหาได้เนื่องจากธุรกรรมที่เกิดขึ้นพร้อมกันจะไม่เห็นการเปลี่ยนแปลงของกันและกันเช่น อ่านซ้ำได้

การใส่คำอธิบายลงใน DAOs จะทำให้ธุรกรรมสั้นที่สุดเท่าที่จะเป็นไปได้ด้วยข้อเสียเปรียบว่าการทำงานของชั้นบริการของคุณจะไม่เปิดเผยในการทำธุรกรรมครั้งเดียว (ย้อนกลับได้)

ไม่ควรใส่คำอธิบายประกอบทั้งสองเลเยอร์หากตั้งค่าโหมดการเผยแพร่เป็นค่าเริ่มต้น


31

ฉันวาง@Transactionalบน@Serviceเลเยอร์และตั้งrollbackForข้อยกเว้นใด ๆ และreadOnlyเพื่อเพิ่มประสิทธิภาพการทำธุรกรรมเพิ่มเติม

โดยค่าเริ่มต้น@TransactionalจะมองหาRuntimeException(ข้อยกเว้นที่ไม่ได้ตรวจสอบ) โดยการตั้งค่าการย้อนกลับเป็นException.class(การตรวจสอบข้อยกเว้น) มันจะย้อนกลับสำหรับข้อยกเว้นใด ๆ

@Transactional(readOnly = false, rollbackFor = Exception.class)

ดูยืมกับข้อยกเว้นไม่ได้ตรวจสอบ


17

หรือเหมาะสมที่จะอธิบายคำว่า "เลเยอร์" ทั้งคู่? - ไม่ควรใส่หมายเหตุประกอบทั้งเลเยอร์บริการและเลเยอร์ dao - หากต้องการตรวจสอบให้แน่ใจว่ามีการเรียกใช้เมธอด DAO เสมอ (แพร่กระจาย) จากชั้นบริการที่มีการเผยแพร่ "ข้อบังคับ" ใน DAO สิ่งนี้จะให้ข้อ จำกัด บางอย่างสำหรับวิธีการ DAO จากการถูกเรียกจากเลเยอร์ UI (หรือตัวควบคุม) นอกจากนี้ - เมื่อหน่วยทดสอบเลเยอร์ DAO โดยเฉพาะ - การมีคำอธิบายประกอบ DAO จะช่วยให้มั่นใจได้ว่าจะได้รับการทดสอบสำหรับการทำงานของทรานแซคชัน


จะไม่ส่งผลให้เกิดธุรกรรมซ้อนกันใช่ไหม และทุกปัญหาที่ลึกซึ้งที่ไปพร้อมกับมัน?
HDave

11
ไม่ไม่มีธุรกรรมที่ซ้อนกันใน JPA การวางทั้งสองอย่างนี้จะดีมาก - ถ้าคุณทำธุรกรรมเมื่อคุณกด DAO แล้วธุรกรรมนั้นจะดำเนินต่อไป
fool4jesus

4
propagation=Propagation.REQUIRES_NEWการทำธุรกรรมที่ซ้อนกันสามารถเกิดขึ้นได้ถ้าคุณใช้ มิฉะนั้นสำหรับกรณีส่วนใหญ่รวมถึง propogation = ข้อบังคับ DAO จะเข้าร่วมในธุรกรรมที่มีอยู่ที่เริ่มต้นโดยชั้นบริการ
แดนคาร์เตอร์


15

สำหรับการทำธุรกรรมในระดับฐานข้อมูล

ส่วนใหญ่ฉันใช้@Transactionalในระดับวิธีของ DAO เพียงอย่างเดียวดังนั้นการกำหนดค่าสามารถเป็นวิธีเฉพาะ / ใช้ค่าเริ่มต้น (จำเป็น)

  1. วิธีการ DAO ที่รับการดึงข้อมูล (เลือก .. ) - ไม่จำเป็นต้องทำเช่น @Transactional นี้อาจทำให้เกิดค่าใช้จ่ายเนื่องจากการทำธุรกรรม interceptor / และ AOP proxy ที่ต้องดำเนินการด้วยเช่นกัน

  2. วิธีการของ DAO ที่ทำการแทรก / อัพเดตจะได้รับ @Transactional

บล็อกที่ดีมากในการทำธุรกรรม

สำหรับระดับแอปพลิเคชัน -
ฉันใช้ธุรกรรมเพื่อตรรกะทางธุรกิจฉันต้องการให้สามารถย้อนกลับได้ในกรณีที่เกิดข้อผิดพลาดที่ไม่คาดคิด

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}

6
+1 บทความที่ดีมากเกี่ยวกับTransactionalinJava
oak

11

โดยปกติหนึ่งควรใส่ธุรกรรมที่ชั้นบริการ

แต่ตามที่ระบุไว้ก่อนหน้านี้อะตอมมิกซิตี้ของการดำเนินการเป็นสิ่งที่บอกเราว่าจำเป็นต้องมีคำอธิบายประกอบอย่างไร ดังนั้นหากคุณใช้เฟรมเวิร์กเช่น Hibernate ซึ่งการดำเนินการ "บันทึก / อัพเดต / ลบ / ... /" เดียวในวัตถุนั้นมีโอกาสที่จะแก้ไขแถวหลายแถวในตารางต่างๆ (เนื่องจากการเรียงซ้อนผ่านกราฟวัตถุ) ของ แน่นอนควรมีการจัดการธุรกรรมในวิธี DAO เฉพาะนี้


10

@Transactionalควรใส่คำอธิบายประกอบรอบการปฏิบัติการทั้งหมดที่แยกออกไม่ได้ การใช้@Transactionalการเผยแพร่ธุรกรรมจะได้รับการจัดการโดยอัตโนมัติในกรณีนี้หากมีการเรียกวิธีอื่นโดยวิธีปัจจุบันวิธีการนั้นจะมีตัวเลือกในการเข้าร่วมธุรกรรมที่กำลังดำเนินอยู่

ลองทำตัวอย่าง:

คือเรามี 2 แบบจำลองและCountry Cityการทำแผนที่เชิงสัมพันธ์ของCountryและCityรูปแบบเป็นเหมือนหนึ่งCountryสามารถมีหลายเมืองดังนั้นการทำแผนที่เป็นเช่น

@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;

Lazilyที่นี่ประเทศแมปไปยังหลายเมืองที่มีการเรียกพวกเขา ดังนั้นที่นี่มาบทบาทของ@Transactinalเมื่อเราดึงประเทศวัตถุจากฐานข้อมูลแล้วเราจะได้รับข้อมูลทั้งหมดของวัตถุประเทศ LAZILYแต่จะไม่ได้รับการตั้งค่าของเมืองเพราะเราเป็นเมืองเรียก

//Without @Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //After getting Country Object connection between countryRepository and database is Closed 
}

เมื่อเราต้องการเข้าถึงชุดของเมืองจากวัตถุในประเทศเราจะได้รับค่า Null ในชุดนั้นเพราะวัตถุของชุดที่สร้างขึ้นเท่านั้นชุดนี้ไม่เริ่มต้นด้วยข้อมูลเพื่อรับค่าของชุดที่เราใช้@Transactionalเช่น

//with @Transactional
@Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
   Object object = country.getCities().size();   
}

ดังนั้นโดยทั่วไปแล้ว@Transactionalบริการสามารถทำการโทรหลายครั้งในการทำรายการเดียวโดยไม่ต้องปิดการเชื่อมต่อกับจุดสิ้นสุด


2
ข้อมูลมากขอบคุณ! สิ่งที่ฉันกำลังมองหาการอธิบายถึงสิ่งที่@Transactionalเป็นจริง
CybeX

6

มันจะดีกว่าที่จะมีมันในชั้นบริการ! นี่เป็นคำอธิบายที่ชัดเจนในบทความหนึ่งที่ฉันพบเมื่อวานนี้! นี่คือลิงค์ที่คุณสามารถตรวจสอบได้!


5

ป้อนคำอธิบายรูปภาพที่นี่

@Transactionalควรใช้กับบริการชั้นเป็นมันมีเหตุผลทางธุรกิจ ชั้น DAO มักจะมีการดำเนินการ CRUD ฐานข้อมูลเท่านั้น

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

Spring doc: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html


5

เลเยอร์บริการเป็นสถานที่ที่ดีที่สุดในการเพิ่ม@Transactionalคำอธิบายประกอบเนื่องจากตรรกะทางธุรกิจส่วนใหญ่อยู่ที่นี่ซึ่งมีพฤติกรรมการใช้งานรายละเอียดในระดับรายละเอียด

สมมติว่าเราเพิ่มลงใน DAO และจากบริการที่เรากำลังเรียก 2 ชั้น DAO หนึ่งล้มเหลวและความสำเร็จอื่น ๆ ในกรณีนี้ถ้า@Transactionalไม่อยู่ในบริการหนึ่งฐานข้อมูลจะกระทำและอื่น ๆ จะย้อนกลับ

ดังนั้นคำแนะนำของฉันคือใช้คำอธิบายประกอบนี้อย่างชาญฉลาดและใช้ที่ชั้นบริการเท่านั้น

โครงการ Github- java-algos


ObjectOptimisticLockingFailureException เกิดขึ้นเฉพาะหลังจากการทำธุรกรรมเสร็จสมบูรณ์ หากคุณมีเธรดแยกต่างหากสำหรับการดำเนินการอื่นเช่นบริการอีเมลการออกแบบนี้จะล้มเหลวโดยสิ้นเชิง ตอนนี้เรากำลังทุกข์ทรมาน ทางออกเดียวที่เหลือคือ AOP
Nabin Kumar Khatiwada

4

ก่อนอื่นเรามากำหนดว่าเราต้องใช้ธุรกรรมที่ไหน?

ฉันคิดว่าคำตอบที่ถูกต้องคือ - เมื่อเราต้องตรวจสอบให้แน่ใจว่าลำดับของการกระทำจะเสร็จสิ้นพร้อมกับการดำเนินการปรมาณูเพียงครั้งเดียวหรือไม่มีการเปลี่ยนแปลงใด ๆ แม้ว่าการกระทำอย่างใดอย่างหนึ่งจะล้มเหลว

เป็นวิธีปฏิบัติที่รู้จักกันดีในการนำตรรกะทางธุรกิจมาใช้ในการบริการ ดังนั้นวิธีการบริการอาจมีการกระทำที่แตกต่างกันซึ่งจะต้องดำเนินการเป็นหน่วยทางลอจิคัลเดียวของการทำงาน ถ้าเป็นเช่นนั้น - แล้ววิธีการดังกล่าวจะต้องมีการทำเครื่องหมายว่าการทำธุรกรรม แน่นอนว่าไม่ใช่ทุกวิธีที่จำเป็นต้องมีข้อ จำกัด ดังกล่าวดังนั้นคุณไม่จำเป็นต้องทำเครื่องหมายบริการทั้งหมดว่าเป็นการทำธุรกรรมการทำธุรกรรม

และยิ่งกว่านั้น - อย่าลืมคำนึงว่า@Transactionalชัดอาจลดประสิทธิภาพของวิธีการ ในการดูภาพรวมคุณต้องรู้ระดับการแยกธุรกรรม การรู้ว่าอาจช่วยให้คุณหลีกเลี่ยงการใช้@ ธุรกรรมที่ไม่จำเป็นต้องใช้


3

ดีกว่าที่จะเก็บ@Tractactionalในเลเยอร์กลางแยกระหว่าง DAO และ Service Layer เนื่องจากการย้อนกลับมีความสำคัญมากคุณสามารถวางการจัดการ DB ทั้งหมดของคุณลงในชั้นกลางและเขียนตรรกะทางธุรกิจใน Service Layer เลเยอร์กลางจะโต้ตอบกับเลเยอร์ DAO ของคุณ

สิ่งนี้จะช่วยคุณในหลาย ๆ สถานการณ์เช่นObjectOptimisticLockingFailureException - ข้อยกเว้นนี้เกิดขึ้นหลังจากธุรกรรมของคุณสิ้นสุดลงเท่านั้น ดังนั้นคุณไม่สามารถจับมันในชั้นกลาง แต่คุณสามารถจับในชั้นบริการของคุณตอนนี้ สิ่งนี้จะเป็นไปไม่ได้หากคุณมี @Transactional ในเลเยอร์บริการ แม้ว่าคุณสามารถจับในตัวควบคุม แต่ตัวควบคุมควรจะสะอาดที่สุด

หากคุณกำลังส่งจดหมายหรือ SMS ในเธรดแยกหลังจากตัวเลือกบันทึกลบและอัปเดตทั้งหมดคุณสามารถทำสิ่งนี้ได้ในบริการหลังจากธุรกรรมเสร็จสมบูรณ์ในเลเยอร์กลางของคุณ อีกครั้งหากคุณพูดถึง @Transactional ในเลเยอร์บริการคุณจะได้รับจดหมายแม้ว่าธุรกรรมของคุณจะล้มเหลว

การมีเลเยอร์ @Transaction ตรงกลางจะช่วยทำให้โค้ดของคุณดีขึ้นและง่ายต่อการจัดการ มิฉะนั้นถ้าคุณใช้ในเลเยอร์ DAO คุณอาจไม่สามารถย้อนกลับการดำเนินการทั้งหมดได้ หากคุณใช้ในเลเยอร์บริการคุณอาจต้องใช้AOP (Aspect Oriented Programming) ในบางกรณี


2

ในอุดมคติแล้วเลเยอร์บริการ (ผู้จัดการ) แสดงถึงตรรกะทางธุรกิจของคุณและดังนั้นจึงควรมีคำอธิบายประกอบด้วย@Transactional. บริการชั้นอาจเรียก DAO ที่แตกต่างกันเพื่อดำเนินการฐานข้อมูล สมมติว่าสถานการณ์ที่คุณมีการดำเนินงาน DAO เป็นจำนวน N ในวิธีการบริการ หากการดำเนินการ DAO ครั้งที่ 1 ของคุณล้มเหลวผู้อื่นอาจยังคงผ่านไปและคุณจะสิ้นสุดสถานะฐานข้อมูลที่ไม่สอดคล้องกัน คำอธิบายประกอบเลเยอร์บริการสามารถช่วยให้คุณประหยัดจากสถานการณ์ดังกล่าว



1

@Transactionalใช้ใน service layer ซึ่งถูกเรียกโดยใช้ controller layer ( @Controller) และ service layer call ไปยัง DAO layer ( @Repository) เช่นการดำเนินการที่เกี่ยวข้องกับฐานข้อมูล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.