ฉันควรใส่คำอธิบายประกอบ @Transactional ไว้ที่ใดที่นิยามอินเตอร์เฟสหรือในคลาสการนำไปใช้งาน


92

คำถามจากชื่อในรหัส:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

เทียบกับ

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}

คำตอบ:


123

จากhttp://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

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

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

(การเน้นที่เพิ่มเข้าไปในประโยคแรกการเน้นอื่น ๆ จากต้นฉบับ)


บิ๊ก +1 BTW ฉันใช้เสรีภาพในการเสนอราคาเพื่อให้คนไม่สับสนและเพิ่มตัวเอียงกลับจากต้นฉบับ
TJ Crowder

ฉันได้อ่านคำสั่งนี้มาก่อนในฤดูใบไม้ผลิ docu แต่ยังคงฉันไม่สามารถเข้าใจว่าทำไมการตั้งค่าการทำธุรกรรมจะไม่ได้รับการยอมรับจากโครงสร้างพื้นฐาน proxying ระดับตาม ? โดยส่วนตัวแล้วฉันคิดว่านี่เป็นเพียงข้อ จำกัด ในการใช้งานในฤดูใบไม้ผลิเท่านั้นไม่ใช่ปัญหาของโครงสร้างพื้นฐานพร็อกซี
dma_k

มองไปที่แหล่งที่มาของฤดูใบไม้ผลิหนึ่งสามารถพบว่าJdkDynamicAopProxyผ่านไปทุกที่ปรึกษาถั่วในแต่ละวิธีการภาวนา (ดูDefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()) BeanFactoryTransactionAttributeSourceAdvisorซึ่งในกรณีของการติดตั้งการทำธุรกรรมที่เปิดเผยรวมถึง ในทางกลับกันTransactionAttributeSourcePointcut#matches()จะถูกเรียกซึ่งควรรวบรวมข้อมูลที่เกี่ยวข้องกับธุรกรรม มันถูกส่งผ่านคลาสเป้าหมายและสามารถข้ามอินเตอร์เฟสทั้งหมดที่คลาสนี้ใช้ ตอนนี้: ทำไมสิ่งนี้ไม่สามารถทำงานได้อย่างน่าเชื่อถือ?
dma_k

2
@dma_k - รันไทม์ Java ให้การเข้าถึงโดยตรงไปยังคำอธิบายประกอบคลาสที่สืบทอดมาจากซูเปอร์คลาสหรือคำอธิบายประกอบเมธอดโดยไม่มีการสืบทอดเลย ดังนั้น Pointcut.matches () จะต้องมีการสำรวจจริงซ้ำการเชื่อมต่อทั้งหมดค้นหา "ของจริง" วิธีการแทนที่ด้วยการสะท้อนวิธีการจัดการสะพานบรรทุกเกินพิกัด, varargs, generics ผู้รับมอบฉันทะและแน่นอนจะทำมันได้อย่างรวดเร็ว ถ้าอย่างนั้นคุณต้องจัดการกับมรดกเพชร - ข้อเขียนใดที่เป็นไปได้มากที่@Transactionalจะนำไปใช้? แม้ว่าจะเป็นไปได้ทั้งหมดแต่ฉันก็ไม่โทษสปริง jira.springsource.org/browse/SPR-975
Peter Davis

9

คุณสามารถวางไว้บนอินเทอร์เฟซได้ แต่ขอเตือนว่าธุรกรรมอาจไม่เกิดขึ้นในบางกรณี ดูเคล็ดลับที่สองในSecion 10.5.6ของ Spring docs:

Spring ขอแนะนำให้คุณใส่คำอธิบายประกอบเฉพาะคลาสคอนกรีต (และวิธีการของคลาสคอนกรีต) ด้วยคำอธิบายประกอบ @Transactional ซึ่งต่างจากการใส่คำอธิบายประกอบอินเทอร์เฟซ คุณสามารถวางคำอธิบายประกอบ @Transactional ไว้บนอินเทอร์เฟซ (หรือวิธีการเชื่อมต่อ) ได้อย่างแน่นอน แต่จะใช้งานได้ตามที่คุณคาดไว้เท่านั้นหากคุณใช้พร็อกซีที่ใช้อินเทอร์เฟซ ความจริงที่ว่าคำอธิบายประกอบ Java ไม่ได้รับการสืบทอดมาจากอินเทอร์เฟซหมายความว่าหากคุณใช้พร็อกซีตามคลาส (proxy-target-class = "true") หรือด้านที่ใช้การทอผ้า (โหมด = "มุมมอง") การตั้งค่าธุรกรรมจะ ไม่ได้รับการยอมรับจากโครงสร้างพื้นฐานการพร็อกซีและการทอและวัตถุจะไม่ถูกรวมไว้ในพร็อกซีธุรกรรมซึ่งจะไม่ดีอย่างแน่นอน

ฉันอยากจะแนะนำให้นำไปใช้งานด้วยเหตุผลนี้

สำหรับฉันแล้วการทำธุรกรรมดูเหมือนเป็นรายละเอียดการใช้งานดังนั้นจึงควรอยู่ในคลาสการนำไปใช้งาน ลองนึกภาพว่ามีการใช้งาน wrapper สำหรับการบันทึกหรือทดสอบการใช้งาน (mocks) ที่ไม่จำเป็นต้องทำธุรกรรม


9

คำแนะนำของ Spring คือให้คุณใส่คำอธิบายประกอบการใช้งานที่เป็นรูปธรรมแทนที่จะเป็นอินเทอร์เฟซ การใช้คำอธิบายประกอบบนอินเทอร์เฟซนั้นไม่ถูกต้องเป็นไปได้ที่จะใช้คุณสมบัตินั้นในทางที่ผิดและเลี่ยงการประกาศ @Transaction ของคุณโดยไม่ได้ตั้งใจ

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

ในทางปฏิบัติจะมีลักษณะดังนี้:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

1

สนับสนุน @Transactional ในชั้นเรียนคอนกรีต:

ฉันชอบออกแบบโซลูชันใน 3 ส่วนโดยทั่วไป ได้แก่ API การใช้งานและเว็บ (หากจำเป็น) ฉันพยายามอย่างเต็มที่เพื่อให้ API มีน้ำหนักเบา / เรียบง่าย / POJO ให้มากที่สุดโดยการลดการอ้างอิง เป็นสิ่งสำคัญอย่างยิ่งหากคุณเล่นในสภาพแวดล้อมแบบกระจาย / รวมที่คุณต้องแชร์ API เป็นจำนวนมาก

การใส่ @Transactional ต้องใช้ไลบรารี Spring ในส่วน API ซึ่ง IMHO ใช้ไม่ได้ผล ดังนั้นฉันชอบที่จะเพิ่มในการดำเนินการที่ธุรกรรมกำลังทำงานอยู่


0

การวางไว้บนอินเทอร์เฟซนั้นใช้ได้ดีตราบใดที่ผู้ใช้งาน IFC ที่คาดการณ์ไว้ทั้งหมดของคุณจะดูแลเกี่ยวกับข้อมูล TX (ธุรกรรมไม่ใช่ปัญหาที่ฐานข้อมูลเท่านั้นที่จัดการ) หากวิธีนี้ไม่สนใจ TX (แต่คุณต้องวางไว้ที่นั่นสำหรับ Hibernate หรืออะไรก็ตาม) ให้วางไว้ที่ impl

นอกจากนี้อาจเป็นการดีกว่าเล็กน้อยที่จะวาง@Transactionalเมธอดในอินเทอร์เฟซ:

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.