I know how to build applications using Java/JavaEE. I know how to develop EJBs (particularly session beans and MDBs). I know how to use JSP and servlets. I know how to use JMS. I know how to use RMI. Most of this stuff is pretty automatic after having worked with these technologies over the past few years. Throw in some helper technologies, such as XDoclet (no more manual updating of deployment descriptors for me, thanks!), and things become even easier. While I can’t rattle off different parts of the J2EE 1.4 specifications, I like to think that I’m competent in my ability to use the technologies.
At least, that’s what I like to tell myself.
I had a bit of a disastrous interview last week. I got asked all kinds of questions about how different parts of J2EE work and, while I was able to come up with good answers for most of it, there were certain questions that I totally botched.
One topic that I really hosed was container-managed transactions (CMT). This is something that I should know something about, but in reality I never actually used them in the applications that were being built at my last job. We pretty much just assigned the “Required” transaction attribute to every EJB method and, as far as I was concerned, everything else just sort of worked. For the CWMP RPC method test system I built, transactions weren’t important at all, so I never really bothered researching about how they worked.
Using that botched interview to try to actually learn something, I set about experimenting with CMT. JBoss is the application server with which I am the most familiar, and MySQL is a free RDBMS that I am also familiar with, so I installed these two products for my testing.
Once everything was set up, I set about creating a simple EJB with a few public methods that my RMI-based test client would call. One method would successfully make changes to the database. A second method would make failed changes to the database. A third method would call the first two methods, allowing me to test multi-level transactions.
Based on what I had read about using transactions (and my own previously limited experience), it should be just a matter of setting up the XDoclet @ejb.transaction attribute on the appropriate methods and the container will take care of the rest.
Here’s an example of my “successful” method:
/*
* @ejb.interface-method
* @ejb.transaction
* type="Required"
*/
public boolean updateMovie(MovieDTO movie) {
boolean success = false;
Connection conn = null;
try {
conn = _ds.getConnection();
PreparedStatement stmt =
conn.prepareStatement("UPDATE movie SET title = ?, year = ? WHERE id = ?");
stmt.setString(1, movie.getTitle());
stmt.setInt(2, movie.getYear());
stmt.setInt(3, movie.getId());
success = stmt.execute();
} catch (SQLException e) {
e.printStackTrace();
throw new EJBException(e.getMessage());
} finally {
closeConnection(conn);
}
return success;
}
It’s a simple method that takes the contents of a DTO and updates the appropriate record in the database. Nothing fancy.Here’s the code for the method that always fails:
/*
* @ejb.interface-method
* @ejb.transaction
* type="Required"
*/
public boolean updateBroken(MovieDTO movie) {
boolean success = false;
Connection conn = null;
try {
conn = _ds.getConnection();
PreparedStatement stmt = conn.prepareStatement("UPDATE movies SET title = ?, year = ? WHERE id = ?");
stmt.setString(1, movie.getTitle());
stmt.setInt(2, movie.getYear());
stmt.setInt(3, movie.getId());
success = stmt.execute();
} catch (SQLException e) {
e.printStackTrace();
throw new EJBException(e.getMessage());
} finally {
closeConnection(conn);
}
return success;
}
The difference here is that the query will fail because the “movies” table does not exist (it’s called ‘movie’).When I installed the app and ran my test client, both methods behaved as they should. When I called the working method, the update occurred. When I called the non-working method, the update did not occur.
Then I added the following method:
/**
* @ejb.interface-method
* @ejb.transaction
* type="Required"
*/
public void doStuff() {
ArrayList movieList = getMovies();
for (MovieDTO movie : movieList)
System.out.println(movie.getId() + ": " + movie.getTitle() + " (" + movie.getYear() + ")");
MovieDTO movieChange = movieList.get(0);
movieChange.setYear(movieChange.getYear() + 1);
updateMovie(movieChange);
movieList = getMovies();
for (MovieDTO movie : movieList)
System.out.println(movie.getId() + ": " + movie.getTitle() + " (" + movie.getYear() + ")");
updateBroken(movieChange);
}
Again, pretty simple. It does the following:- Get the list of all movies and output them to the console
- Change the year for one of the movies and update it via the “successful” method
- Get the list of movies again and output them to the console (the idea to show that the update actually occurred)
- Try updating the movie record again via the “unsuccessful” method
This was not what happened. Every time I ran the doStuff() method, the year value continued to increment, despite the fact that the second update always failed. I was very confused.
And so began my 2 day odyssey to figure out what the heck was wrong.
I asked some former colleagues about our use of transactions with Oracle and MS SQL server. They claimed that everything was working as expected.
I read up on how to specify transactions in the deployment descriptors and verified that XDoclet was doing the right thing.
I started searching the web to see if anyone else was having problems getting transactions to work with MySQL and JBoss. It turned out that lots of people had problems getting them to work, and while people were eventually successful, I couldn’t see what they were doing to get it to work.
At one point, I stumbled across something that talked about needing to use XA drivers in order to get the transactions to work. While this should certainly do the trick, it seems to me that the XA stuff is really meant for establishing transactions across different types of systems (say the EJB container and some remote OSS or billing system). Still, I decided to try using the MySQL XA driver. I tried and tried to get things working properly, but the problem remained. Whenever I ran that test method, the year value was incremented.
At one point I really mucked with the XA driver configuration and things stopped working altogether. At that point, I figured I needed to move into a different direction. This was confirmed when I asked a colleague about XA drivers and he mentioned that the product never used them.
Ok, back to the web and more endless digging.
After a few more hours, I just happened to stumble across this article, which talks about how to get CMT working with Hibernate in a JBoss environment. The key section is “Configuring MySQL”.
I didn’t really pay much attention to the table definitions (which may have actually saved me a lot of time, since I may have come across similar definitions before). Instead, I saw this little gem:
As you can see, we are creating tables of the InnoDB type. That’s important. MySQL’s default type is MyISAM. MyISAM is an improved replacement for ISAM, but it’s a non-transactional storage engine and it follows a different paradigm for data integrity, which MySQL calls “atomicoperations.” Again, it’s non-transactional, meaning that it does not support transactions. Obviously, we need a transactional table type, and in MySQL that means InnoDB.
Well, well, well.
When I created the tables initially, I didn’t specify any sort of engine, thus they defaulted to MyISAM. Since this engine does not support transactions, there was no way for the container to undo the first successful update.
Giddy with excitement, I fired up the excellent MySQL Query Browser tool and modified the table definition for the ‘movie’ table to use the InnoDB engine instead of MyISAM. I then ran my test client against the doStuff() EJB method and…
Success! The console output showed the list of movie data, the data with the updated value, and the failure exception. When I queried the database afterward for the current values, the original values were all still in place. The transaction had been completely rolled back. Hurray!
So that’s it. In order to get container-managed transactions to work with a MySQL database, the tables must be configured to use the InnoDB engine. A full table definition script would look something like this:
CREATE TABLE movie (
id int(11) NOT NULL auto_increment,
title varchar(255) default NULL,
year int(4) default NULL,
PRIMARY KEY (id)
) TYPE=InnoDB;
I hope that this information helps someone avoid all of the headaches I experienced in trying to find out what turned out to be a very simple solution.
37 comments:
長見識了,謝謝你! ........................................
走光圖性關係情色vcdav寫真集台灣色情網台灣辣妹辣妹聊天室裸女寫真淫娃網免費視訊辣妹一夜情貼圖情色下載色情vcdav網女生奶頭情色影音色論壇成人影片交流性幻想女生自慰影片美女性交辣妹裸體色情美女情色交友自慰圖成人自拍貼圖上空秀黃色圖片情色性愛辣妹胸部一絲不掛成人網址成人18禁台灣性網一夜情人淫妹性愛招式脫衣辣妹視訊自拍成人色情網色情漫畫
色情漫畫
I do like ur article~!!!..................................................
haha~ funny! thank you for your share~ ........................................
幸福是人人都要,又怎麼可能都歸你所有?要知道這世界幸福本來就不多........................................
留言是種美德-感謝分享 ....................................................
春宮性愛教學taiwan sex大奶子台灣情色論壇台灣av女優一夜情留言板女生裸體色情電話a網情色片美女裸照成人笑話巨乳學院裸體寫真av寫真走光照巨奶做愛技巧a圖片淫亂撫摸情色聊天少婦自拍淫娃一夜情聊天av情色本土av女生高潮色情av成人情色貼圖一夜情留言成人圖庫亞洲成人口交技巧性經驗成人18三點全露蕩婦情色自拍貼圖成人漫畫成人漫畫
被人揭下面具是一種失敗,自己揭下面具卻是種勝利。......................................................
青春一逝不復返,事業一失難有成。.............................................
做愛 淫慾 s18x愛 寫真 77p2p 辣妹 淫婦 av 美女 美女聊天室 後宮聊天 打手槍 視訊 18禁視訊 貼圖 xPlus.av 援交打炮 女優 live173 173 85免費 uthome聊天室 一夜情 xvideo 免費視訊 情色聊天 交友聊天 影音視訊聊天室 情人視訊 a片
Well begun is half done. ............................................................
河水永遠是相同的,可是每一剎那又都是新的。......................................................................
Cast not the first stone. ....................................................
Necessity is the mother of invention...................................................................
被人揭下面具是一種失敗,自己揭下面具卻是種勝利。..................................................................
人因夢想而偉大,要堅持自己的理想哦......................................................................
欣賞是一種美德~回應是最大的支持^^....................................................................
成熟,就是有能力適應生活中的模糊。.................................................................
幸福不是一切,人還有責任。............................................................
如果成為一支火柴,也要點亮一個短暫的宇宙;如果是一隻烏鴉,也要叫疼閉塞的耳膜。.................................................................
支持你就對了!..................................................................
來看你了~心在、愛在、牽掛在,幸福才會繁衍不息^^............................................................
教育無他,愛與榜樣而已............................................................
Birthdays are good for you. The more you have, the longer you live.............................................................
嘿,你的部落格不錯哦!..................................................................
~~天天開心~~祝您快樂~~............................................................
No pains, no gains..................................................................
take care yourself!! ^^............................................................
愛情不是慈善事業,不能隨便施捨。.................................................................
一棵樹除非在春天開了花,否則難望在秋天結果。..................................................
人生的價值以及他的快樂,都在於他有能力看重自己的生存................................................
Learning makes a good man better and ill man worse.............................................................
相見亦無事,不來常思君......................................................................
No pains, no gains.......................................................................
寂寞又無聊 看到你的BLOG 加油喔!!..................................................
GOOD POST~~~~~~~~~~~~Support!!!!.................................[/url]...............
希望能常常看到你的更新..................................................
Post a Comment