package org.nutz.trans; import static org.junit.Assert.*; import org.junit.Test; import org.nutz.dao.test.DaoCase; import org.nutz.lang.Lang; import org.nutz.service.IdEntityService; import org.nutz.trans.Atom; import org.nutz.trans.Trans; public class TransactionTest extends DaoCase { private IdEntityService<Cat> catService; private IdEntityService<Company> comService; private IdEntityService<Master> masterService; protected void before() { dao.create(Company.class, true); dao.create(Master.class, true); dao.create(Cat.class, true); catService = new IdEntityService<Cat>(dao) {}; comService = new IdEntityService<Company>(dao) {}; masterService = new IdEntityService<Master>(dao) {}; } protected void after() {} private Cat insert(final Cat cat, final Bomb bomb) { // //System.out.printf("\n>> insert cat: %d\n", // bomb.times); Trans.exec(new Atom() { public void run() { dao.insert(cat); cat.setMaster(insert(cat.getMaster(), bomb)); bomb.bong(); } }); // //System.out.printf("<< insert cat: %d\n", // bomb.times); return cat; } private Company insert(final Company com, final Bomb bomb) { // //System.out.printf("\n>> insert company: %d\n", // bomb.times); Trans.exec(new Atom() { public void run() { dao.insert(com); bomb.bong(); } }); // //System.out.printf("<< insert company: %d\n", // bomb.times); return com; } private Master insert(final Master master, final Bomb bomb) { // //System.out.printf("\n>> insert master: %d\n", // bomb.times); Trans.exec(new Atom() { public void run() { dao.insert(master); master.setCom(insert(master.getCom(), bomb)); bomb.bong(); } }); // //System.out.printf("<< insert master: %d\n", // bomb.times); return master; } private static class Bomb { private int times; Bomb(int times) { this.times = times; } Bomb bong() throws RuntimeException { if (--times == 0) { // //System.out.printf("Bong!!! bomb-- %d => %d\n", // times + 1, // times); throw new RuntimeException("Bong!!!"); } // //System.out.printf("bomb-- %d => %d\n", // times + 1, times); return this; } } @Test public void testNestedCommit() { Cat cat = Cat.create("XiaoBai", Master.create("zzh", Company.create("Dtri"))); insert(cat, new Bomb(-1)); assertEquals(1, dao.count(Cat.class)); assertEquals(1, dao.count(Master.class)); assertEquals(1, dao.count(Company.class)); assertNull(Trans.get()); assertEquals(0, Trans.count.get().intValue()); } @Test public void testNestedRollback() { Cat cat = Cat.create("XiaoBai", Master.create("zzh", Company.create("Dtri"))); try { insert(cat, new Bomb(2)); fail(); } catch (Exception e) { assertEquals(0, dao.count(Company.class)); assertEquals(0, dao.count(Master.class)); assertEquals(0, dao.count(Cat.class)); assertNull(Trans.get()); } } class CheckCheck extends Thread { private Object lock = new Object(); private Object tellor; private boolean standby; public boolean isStandby() { return standby; } public void run() { // System.out.println("\nI am checker"); try { synchronized (lock) { standby = true; // System.out.println("\nchecker: I am standby and wait for another thread..."); lock.wait(); // System.out.println("\nchecker: I will do some check"); } if (0 != dao.count(Company.class, null) || 0 != dao.count(Master.class, null) || 0 != dao.count(Cat.class, null)) { throw new RuntimeException("Find change in another thread"); } } catch (InterruptedException e) { throw Lang.wrapThrow(e); } finally { synchronized (tellor) { // System.out.println("\nchecker: I will notify tellor!"); tellor.notifyAll(); // System.out.println("\nchecker: I done for notify tellor"); } } } } class AnotherThread extends Thread { private Object checker; private Object tellor; public Object waiter; public void run() { Trans.exec(new Atom() { public void run() { // System.out.println("\nI am another"); Company com = Company.create("dtri"); Master m = Master.create("zzh", com); Cat c1 = Cat.create("XiaoBai", m); Cat c2 = Cat.create("Tony", m); comService.dao().insert(com); assertEquals(1, dao.count(Company.class, null)); m.setComId(com.getId()); masterService.dao().insert(m); assertEquals(1, dao.count(Master.class, null)); c1.setMasterId(m.getId()); c2.setMasterId(m.getId()); catService.dao().insert(c1); catService.dao().insert(c2); assertEquals(2, dao.count(Cat.class, null)); synchronized (checker) { // System.out.println("\nanother: finished to myjob and I will tell checker..."); checker.notifyAll(); // System.out.println("\nanother: I done for notify checker"); } try { synchronized (tellor) { // System.out.println("\nanother: I will wait 100 ms"); tellor.wait(100); // System.out.println("\nanother: I am wake up"); } } catch (InterruptedException e) { throw Lang.wrapThrow(e); } } }); // System.out.println("\nanother: I will tell waiter"); synchronized (waiter) { waiter.notifyAll(); } // System.out.println("\nanother: done for tell waiter"); } } @Test public void testRollback() { // In transaction try { Trans.exec(new Atom() { public void run() { Company com = Company.create("dtri"); Master m = Master.create("zzh", com); Cat c1 = Cat.create("XiaoBai", m); Cat c2 = Cat.create("Tony", m); comService.dao().insert(com); assertEquals(1, dao.count(Company.class, null)); m.setComId(com.getId()); masterService.dao().insert(m); assertEquals(1, dao.count(Master.class, null)); c1.setMasterId(m.getId()); c2.setMasterId(m.getId()); catService.dao().insert(c1); catService.dao().insert(c2); assertEquals(2, dao.count(Cat.class, null)); throw new RuntimeException("Stop!!!"); } }); fail(); } catch (Exception e) { assertEquals(0, dao.count(Company.class, null)); assertEquals(0, dao.count(Master.class, null)); assertEquals(0, dao.count(Cat.class, null)); assertEquals(0, Trans.count.get().intValue()); assertNull(Trans.get()); } } }