ตอนที่ 1 . ชื่อตอน คำถามเรื่องนี้เป็นเรื่องสำคัญที่อาจจะทำให้โปรแกรมมของเราทำงานออกมาไม่ถูกต้องได้ blog นี้จะเขียนโดยการใช้โค๊ดนำทาง เนื่องจากว่าอยากจะเน้นให้เห็นชัดเจน
@Statelesspublic class AuditServiceBean implements AuditService { @PersistenceContext(unitName="EmployeeService") EntityManager em;public void logTransaction(int empId, String action) { // verify employee number is valid if (em.find(Employee.class, empId) == null) { throw new IllegalArgumentException("Unknown employee id"); } LogRecord lr = new LogRecord(empId, action); em.persist(lr);}}@Statelesspublic class EmployeeServiceBean implements EmployeeService { @PersistenceContext(unitName="EmployeeService") EntityManager em; @EJB AuditService audit;public void createEmployee(Employee emp) { em.persist(emp); audit.logTransaction(emp.getId(), "created employee");}// ...}คำอธิบายEmployeeService เรียกใช้ AuditService อีกที และ createEmployee มี Transaction attr=REQUIRE by defaultโค๊ดนี้จะมีปัญหาก็ต่อเมื่อเราไปแก้ไข Transaction attribute ของ เมธอด logTransactionคำถาม 1. logTransaction() ใช้ transaction attr เป็นอะไรถึงจะเกิดปัญหา ( แน่นอนครับว่าในเมธอดนี้จะต้องมี transaction active อยู่ ดังนั้น REQUIRED,REQUIRES_NEW จึงเป็นสองข้อที่เหลือให้เลือก ไม่นับ MANDATORY เพราะในกรณีนี้มีค่าเท่ากับ REQUIREDคำถาม 2. ปัญหามาจากอะไรเชิญ post คำตอบตามสบายเลยนะครับ ถ้ามีคนอธิบายได้ดีแล้ว ผมจะคิดคำถามมาถามอีก
1 Comments On This Entry
Page 1 of 1
comx
09 November 2006 - 10:53 AM
ตอนที่ 2 คำตอบของตอนที่1
ปัญหามันอยู่ที่
ครับ เนื่องจากว่าเราใช้ Transaction scope persistence context วิธีการจัดการของมันคือ มันจะตรวจสอบ JTA Transaction ว่า transaction นั้นมี persistence context ผูกมาด้วยหรือไม่ ถ้าไม่มีก็สร้างให้ใหม่
ดังนั้นใน method createEmployee() ตรงบรรทัด em.persist(emp); จะสร้าง persistence context ขึ้นใหม่
ปัญหาอยู่ที่ ใน logTransaction ของ AuditServiceBean ครับ
logTransaction มีการเรียกใช้ em.find() ถ้าสังเกตให้ดีๆจะพบว่า ในขณะที่เรียกใช้ em.find() นั้นจะ persistance context จะยังไม่ได้ sync กับ database(จะ sync ตอนที่ commit คือ ตอนที่ createEmployee() ทำงานเสร็จ)
ดังนั้น ถ้าหากในเมธอด logTransaction() ไม่ใช้ persistence context เดียวกับ createEmployee() จะทำให้ find() ไม่เจอ แต่โชคดีครับที่ JEE เขาทำ persistance propagation ไปตาม JTA propagation ดังนั้นในตัวอย่างโค๊ดข้างบนจึงไม่เกิดปัญหาเนื่องจาก logTransaction มี transaction attribute = REQUIRED ทำให้ transaction จาก createEmployee() propagate ไปถึงได้(พร้อมกับ persistence context)
โปแรกรมนี้จะมีปัญหาเมื่อ logTransaction ใช้ transaction ของตัวเอง คือ กำหนด transaction attribute=REQUIRES_NEW ครับ
การทำงานใน logTransaction () จะเป็นแบบนี้ครับ
1.container สร้าง Transaction ใหม่ และ suspend Transaction ของ createEmployee() ไว้
2. em.find(Employee.class, empId) ถูกเรียก แต่เนื่องจาก transaction ถูกสร้างขึ้นใหม่จึงยังไม่มี persistence context คอนเทนเนอร์เลยสร้างใหม่
3. em.find() หาข้อมูลใน DB ไม่เจอเพราะ ยังไม่ได้ถูกบันทึกลง DB จริงๆ
แค่ transaction attribute ตัวเดียวก็ทำให้โปรแกรมเราผิดพลาดได้ ถ้าหากเราไม่รู้การทำงานของมัน
ปัญหามันอยู่ที่
public void createEmployee(Employee emp) {
em.persist(emp);
audit.logTransaction(emp.getId(), "created employee");
}ครับ เนื่องจากว่าเราใช้ Transaction scope persistence context วิธีการจัดการของมันคือ มันจะตรวจสอบ JTA Transaction ว่า transaction นั้นมี persistence context ผูกมาด้วยหรือไม่ ถ้าไม่มีก็สร้างให้ใหม่
ดังนั้นใน method createEmployee() ตรงบรรทัด em.persist(emp); จะสร้าง persistence context ขึ้นใหม่
ปัญหาอยู่ที่ ใน logTransaction ของ AuditServiceBean ครับ
logTransaction มีการเรียกใช้ em.find() ถ้าสังเกตให้ดีๆจะพบว่า ในขณะที่เรียกใช้ em.find() นั้นจะ persistance context จะยังไม่ได้ sync กับ database(จะ sync ตอนที่ commit คือ ตอนที่ createEmployee() ทำงานเสร็จ)
ดังนั้น ถ้าหากในเมธอด logTransaction() ไม่ใช้ persistence context เดียวกับ createEmployee() จะทำให้ find() ไม่เจอ แต่โชคดีครับที่ JEE เขาทำ persistance propagation ไปตาม JTA propagation ดังนั้นในตัวอย่างโค๊ดข้างบนจึงไม่เกิดปัญหาเนื่องจาก logTransaction มี transaction attribute = REQUIRED ทำให้ transaction จาก createEmployee() propagate ไปถึงได้(พร้อมกับ persistence context)
โปแรกรมนี้จะมีปัญหาเมื่อ logTransaction ใช้ transaction ของตัวเอง คือ กำหนด transaction attribute=REQUIRES_NEW ครับ
การทำงานใน logTransaction () จะเป็นแบบนี้ครับ
1.container สร้าง Transaction ใหม่ และ suspend Transaction ของ createEmployee() ไว้
2. em.find(Employee.class, empId) ถูกเรียก แต่เนื่องจาก transaction ถูกสร้างขึ้นใหม่จึงยังไม่มี persistence context คอนเทนเนอร์เลยสร้างใหม่
3. em.find() หาข้อมูลใน DB ไม่เจอเพราะ ยังไม่ได้ถูกบันทึกลง DB จริงๆ
แค่ transaction attribute ตัวเดียวก็ทำให้โปรแกรมเราผิดพลาดได้ ถ้าหากเราไม่รู้การทำงานของมัน
Page 1 of 1
← September 2010 →
| S | M | T | W | T | F | S |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |
Help
1 Comments








