锘??xml version="1.0" encoding="utf-8" standalone="yes"?>
PreparedStatement stmt=conn.prepareStatement(
聽聽聽聽聽 "update account set balance=? where acct_id=?");
int[] rows;
for(int i=0;i<accts.length;i++){
聽聽聽 stmt.setInt(1,i);
聽聽聽 stmt.setLong(2,i);
聽聽聽 stmt.addBatch();
聽 }
rows=stemt.executeBatch();
select * from (select rownum as numrow from table_name where numrow>80 and numrow<100 )
涓嶈兘鐩存帴浣跨敤 select * from rownum>100 and rownum<200;
in oracle return null;
2 sql server 鐨勫疄鐜?br />3 mysql 鐨勫疄鐜?/p>
select id from table_name where id in
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 select * from (select rownum as numrow ,id from tabl_name)
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 where numrow>80 and num<100;聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽
浣跨敤瀛樺偍榪囩▼
綆鍗曠殑鑰佺殑JDBC閫氳繃CallableStatement綾繪敮鎸佸瓨鍌ㄨ繃紼嬬殑璋冪敤銆傝綾誨疄闄呬笂鏄疨reparedStatement鐨勪竴涓瓙綾匯傚亣璁炬垜浠湁涓涓猵oets鏁版嵁搴撱傛暟鎹簱涓湁涓涓緗瘲浜洪濅笘騫撮緞鐨勫瓨鍌ㄨ繃紼嬨備笅闈㈡槸瀵硅侀厭楝糄ylan Thomas錛坥ld soak Dylan Thomas錛屼笉鎸囧畾鏄惁鏈夊叧鍏告晠銆佹枃鍖栵紝璇鋒壒璇勬寚姝c傝瘧娉級榪涜璋冪敤鐨勮緇嗕唬鐮侊細
try{
int age = 39;
String poetName = "dylan thomas";
CallableStatement proc = connection.prepareCall("{ call set_death_age(?, ?) }");
proc.setString(1, poetName);
proc.setInt(2, age);
cs.execute();
}catch (SQLException e){ // ....}
浼犵粰prepareCall鏂規硶鐨勫瓧涓叉槸瀛樺偍榪囩▼璋冪敤鐨勪功鍐欒鑼冦傚畠鎸囧畾浜嗗瓨鍌ㄨ繃紼嬬殑鍚嶇О錛岋紵浠h〃浜嗕綘闇瑕佹寚瀹氱殑鍙傛暟銆?
鍜孞DBC闆嗘垚鏄瓨鍌ㄨ繃紼嬬殑涓涓緢澶х殑渚垮埄錛氫負浜嗕粠搴旂敤涓皟鐢ㄥ瓨鍌ㄨ繃紼嬶紝涓嶉渶瑕佸瓨鏍癸紙stub錛夌被鎴栬呴厤緗枃浠訛紝闄や簡浣犵殑DBMS鐨凧DBC椹卞姩紼嬪簭澶栦粈涔堜篃涓嶉渶瑕併?
褰撹繖孌典唬鐮佹墽琛屾椂錛屾暟鎹簱鐨勫瓨鍌ㄨ繃紼嬪氨琚皟鐢ㄣ傛垜浠病鏈夊幓鑾峰彇緇撴灉錛屽洜涓鴻瀛樺偍榪囩▼騫朵笉榪斿洖緇撴灉銆傛墽琛屾垚鍔熸垨澶辮觸灝嗛氳繃渚嬪寰楃煡銆傚け璐ュ彲鑳芥剰鍛崇潃璋冪敤瀛樺偍榪囩▼鏃剁殑澶辮觸錛堟瘮濡傛彁渚涚殑涓涓弬鏁扮殑綾誨瀷涓嶆紜級錛屾垨鑰呬竴涓簲鐢ㄧ▼搴忕殑澶辮觸錛堟瘮濡傛姏鍑轟竴涓緥澶栨寚紺哄湪poets鏁版嵁搴撲腑騫朵笉瀛樺湪鈥淒ylan Thomas鈥濓級
緇撳悎SQL鎿嶄綔涓庡瓨鍌ㄨ繃紼?
鏄犲皠Java瀵硅薄鍒癝QL琛ㄤ腑鐨勮鐩稿綋綆鍗曪紝浣嗘槸閫氬父闇瑕佹墽琛屽嚑涓猄QL璇彞錛涘彲鑳芥槸涓涓猄ELECT鏌ユ壘ID錛岀劧鍚庝竴涓狪NSERT鎻掑叆鎸囧畾ID鐨勬暟鎹傚湪楂樺害瑙勬牸鍖栵紙絎﹀悎鏇撮珮鐨勮寖寮忥紝璇戞敞錛夌殑鏁版嵁搴撴ā寮忎腑錛屽彲鑳介渶瑕佸涓〃鐨勬洿鏂幫紝鍥犳闇瑕佹洿澶氱殑璇彞銆侸ava浠g爜浼氬緢蹇湴鑶ㄨ儉錛屾瘡涓涓鍙ョ殑緗戠粶寮閿涔熻繀閫熷鍔犮?
灝嗚繖浜汼QL璇彞杞Щ鍒頒竴涓瓨鍌ㄨ繃紼嬩腑灝嗗ぇ澶х畝鍖栦唬鐮侊紝浠呮秹鍙婁竴嬈$綉緇滆皟鐢ㄣ傛墍鏈夊叧鑱旂殑SQL鎿嶄綔閮藉彲浠ュ湪鏁版嵁搴撳唴閮ㄥ彂鐢熴傚茍涓旓紝瀛樺偍榪囩▼璇█錛屼緥濡侾L/SQL錛屽厑璁鎬嬌鐢⊿QL璇硶錛岃繖姣擩ava浠g爜鏇村姞鑷劧銆備笅闈㈡槸鎴戜滑鏃╂湡鐨勫瓨鍌ㄨ繃紼嬶紝浣跨敤Oracle鐨凱L/SQL璇█緙栧啓錛?
create procedure set_death_age(poet VARCHAR2, poet_age NUMBER)
poet_id NUMBER;
begin SELECT id INTO poet_id FROM poets WHERE name = poet;
INSERT INTO deaths (mort_id, age) VALUES (poet_id, poet_age);
end set_death_age;
寰堢嫭鐗癸紵涓嶃傛垜鎵撹祵浣犱竴瀹氭湡寰呯湅鍒頒竴涓猵oets琛ㄤ笂鐨刄PDATE銆傝繖涔熸殫紺轟簡浣跨敤瀛樺偍榪囩▼瀹炵幇鏄涔堝鏄撶殑涓浠朵簨鎯呫俿et_death_age鍑犱箮鍙互鑲畾鏄竴涓緢鐑傜殑瀹炵幇銆傛垜浠簲璇ュ湪poets琛ㄤ腑娣誨姞涓鍒楁潵瀛樺偍閫濅笘騫撮緞銆侸ava浠g爜涓茍涓嶅叧蹇冩暟鎹簱妯″紡鏄庝箞瀹炵幇鐨勶紝鍥犱負瀹冧粎璋冪敤瀛樺偍榪囩▼銆傛垜浠互鍚庡彲浠ユ敼鍙樻暟鎹簱妯″紡浠ユ彁楂樻ц兘錛屼絾鏄垜浠笉蹇呬慨鏀規垜浠唬鐮併?
涓嬮潰鏄皟鐢ㄤ笂闈㈠瓨鍌ㄨ繃紼嬬殑Java浠g爜錛?
public static void setDeathAge(Poet dyingBard, int age) throws SQLException{
Connection con = null;
CallableStatement proc = null;
try {
con = connectionPool.getConnection();
proc = con.prepareCall("{ call set_death_age(?, ?) }");
proc.setString(1, dyingBard.getName());
proc.setInt(2, age);
proc.execute();
}聽
finally {
try { proc.close(); }
catch (SQLException e) {}
con.close();
}
}
涓轟簡紜繚鍙淮鎶ゆэ紝寤鴻浣跨敤鍍忚繖鍎胯繖鏍風殑static鏂規硶銆傝繖涔熶嬌寰楄皟鐢ㄥ瓨鍌ㄨ繃紼嬬殑浠g爜闆嗕腑鍦ㄤ竴涓畝鍗曠殑妯$増浠g爜涓傚鏋滀綘鐢ㄥ埌璁稿瀛樺偍榪囩▼錛屽氨浼氬彂鐜頒粎闇瑕佹嫹璐濄佺矘璐村氨鍙互鍒涘緩鏂扮殑鏂規硶銆傚洜涓轟唬鐮佺殑妯$増鍖栵紝鐢氳嚦涔熷彲浠ラ氳繃鑴氭湰鑷姩鐢熶駭璋冪敤瀛樺偍榪囩▼鐨勪唬鐮併?
Functions
瀛樺偍榪囩▼鍙互鏈夎繑鍥炲鹼紝鎵浠allableStatement綾繪湁綾諱技getResultSet榪欐牱鐨勬柟娉曟潵鑾峰彇榪斿洖鍊箋傚綋瀛樺偍榪囩▼榪斿洖涓涓兼椂錛屼綘蹇呴』浣跨敤registerOutParameter鏂規硶鍛婅瘔JDBC椹卞姩鍣ㄨ鍊肩殑SQL綾誨瀷鏄粈涔堛備綘涔熷繀欏昏皟鏁村瓨鍌ㄨ繃紼嬭皟鐢ㄦ潵鎸囩ず璇ヨ繃紼嬭繑鍥炰竴涓箋?
涓嬮潰鎺ョ潃涓婇潰鐨勪緥瀛愩傝繖嬈℃垜浠煡璇ylan Thomas閫濅笘鏃剁殑騫撮緞銆傝繖嬈$殑瀛樺偍榪囩▼浣跨敤PostgreSQL鐨刾l/pgsql錛?
create function snuffed_it_when (VARCHAR) returns integer ''declare
poet_id NUMBER;
poet_age NUMBER;
begin
--first get the id associated with the poet.
SELECT id INTO poet_id FROM poets WHERE name = $1;
--get and return the age.
SELECT age INTO poet_age FROM deaths WHERE mort_id = poet_id;
return age;
end;'' language ''pl/pgsql'';
鍙﹀錛屾敞鎰弍l/pgsql鍙傛暟鍚嶉氳繃Unix鍜孌OS鑴氭湰鐨?n璇硶寮曠敤銆傚悓鏃訛紝涔熸敞鎰忓祵鍏ョ殑娉ㄩ噴錛岃繖鏄拰Java浠g爜鐩告瘮鐨勫彟涓涓紭瓚婃с傚湪Java涓啓榪欐牱鐨勬敞閲婂綋鐒舵槸鍙互鐨勶紝浣嗘槸鐪嬭搗鏉ュ緢鍑屼貢錛屽茍涓斿拰SQL璇彞鑴辮妭錛屽繀欏誨祵鍏ュ埌Java String涓?
涓嬮潰鏄皟鐢ㄨ繖涓瓨鍌ㄨ繃紼嬬殑Java浠g爜錛?
connection.setAutoCommit(false);
CallableStatement proc = connection.prepareCall("{ ? = call snuffed_it_when(?) }");
proc.registerOutParameter(1, Types.INTEGER);
proc.setString(2, poetName);
cs.execute();
int age = proc.getInt(2);
濡傛灉鎸囧畾浜嗛敊璇殑榪斿洖鍊肩被鍨嬩細鎬庢牱錛熼偅涔堬紝褰撹皟鐢ㄥ瓨鍌ㄨ繃紼嬫椂灝嗘姏鍑轟竴涓猂untimeException錛屾濡備綘鍦≧esultSet鎿嶄綔涓嬌鐢ㄤ簡涓涓敊璇殑綾誨瀷鎵紕板埌鐨勪竴鏍楓?
澶嶆潅鐨勮繑鍥炲?
鍏充簬瀛樺偍榪囩▼鐨勭煡璇嗭紝寰堝浜哄ソ鍍忓氨鐔熸倝鎴戜滑鎵璁ㄨ鐨勮繖浜涖傚鏋滆繖鏄瓨鍌ㄨ繃紼嬬殑鍏ㄩ儴鍔熻兘錛岄偅涔堝瓨鍌ㄨ繃紼嬪氨涓嶆槸鍏跺畠榪滅▼鎵ц鏈哄埗鐨勬浛鎹㈡柟妗堜簡銆傚瓨鍌ㄨ繃紼嬬殑鍔熻兘姣旇繖寮哄ぇ寰楀銆?
褰撲綘鎵ц涓涓猄QL鏌ヨ鏃訛紝DBMS鍒涘緩涓涓彨鍋歝ursor錛堟父鏍囷級鐨勬暟鎹簱瀵硅薄錛岀敤浜庡湪榪斿洖緇撴灉涓凱浠f瘡涓琛屻俁esultSet鏄綋鍓嶆椂闂寸偣鐨勬父鏍囩殑涓涓〃紺恒傝繖灝辨槸涓轟粈涔堟病鏈夌紦瀛樻垨鑰呯壒瀹氭暟鎹簱鐨勬敮鎸侊紝浣犲彧鑳藉湪ResultSet涓悜鍓嶇Щ鍔ㄣ?
鏌愪簺DBMS鍏佽浠庡瓨鍌ㄨ繃紼嬩腑榪斿洖娓告爣鐨勪竴涓紩鐢ㄣ侸DBC騫朵笉鏀寔榪欎釜鍔熻兘錛屼絾鏄疧racle銆丳ostgreSQL鍜孌B2鐨凧DBC椹卞姩鍣ㄩ兘鏀寔鍦≧esultSet涓婃墦寮鍒版父鏍囩殑鎸囬拡錛坧ointer錛夈?
璁炬兂鍒楀嚭鎵鏈夋病鏈夋椿鍒伴浼戝勾榫勭殑璇椾漢錛屼笅闈㈡槸瀹屾垚榪欎釜鍔熻兘鐨勫瓨鍌ㄨ繃紼嬶紝榪斿洖涓涓墦寮鐨勬父鏍囷紝鍚屾牱涔熶嬌鐢≒ostgreSQL鐨刾l/pgsql璇█錛?
create procedure list_early_deaths () return refcursor as ''declare
toesup refcursor;
begin
open toesup for SELECT poets.name, deaths.age FROM poets, deaths -- all entries in deaths are for poets. -- but the table might become generic.
WHERE poets.id = deaths.mort_id AND deaths.age < 60;
return toesup;
end;'' language ''plpgsql'';
涓嬮潰鏄皟鐢ㄨ瀛樺偍榪囩▼鐨凧ava鏂規硶錛屽皢緇撴灉杈撳嚭鍒癙rintWriter錛?
PrintWriter:
static void sendEarlyDeaths(PrintWriter out){
Connection con = null;
CallableStatement toesUp = null;
try {
con = ConnectionPool.getConnection();
// PostgreSQL needs a transaction to do this... con.
setAutoCommit(false); // Setup the call.
CallableStatement toesUp = connection.prepareCall("{ ? = call list_early_deaths () }");
toesUp.registerOutParameter(1, Types.OTHER);
toesUp.execute();
ResultSet rs = (ResultSet) toesUp.getObject(1);
while (rs.next()) {
String name = rs.getString(1);
int age = rs.getInt(2);
out.println(name + " was " + age + " years old.");
}
rs.close();
}
catch (SQLException e) { // We should protect these calls. toesUp.close(); con.close();
}
}
鍥犱負JDBC騫朵笉鐩存帴鏀寔浠庡瓨鍌ㄨ繃紼嬩腑榪斿洖娓告爣錛屾垜浠嬌鐢═ypes.OTHER鏉ユ寚紺哄瓨鍌ㄨ繃紼嬬殑榪斿洖綾誨瀷錛岀劧鍚庤皟鐢╣etObject()鏂規硶騫跺榪斿洖鍊艱繘琛屽己鍒剁被鍨嬭漿鎹€?
榪欎釜璋冪敤瀛樺偍榪囩▼鐨凧ava鏂規硶鏄痬apping鐨勪竴涓ソ渚嬪瓙銆侻apping鏄涓涓泦涓婄殑鎿嶄綔榪涜鎶借薄鐨勬柟娉曘備笉鏄湪榪欎釜榪囩▼涓婅繑鍥炰竴涓泦錛屾垜浠彲浠ユ妸鎿嶄綔浼犻佽繘鍘繪墽琛屻傛湰渚嬩腑錛屾搷浣滃氨鏄妸ResultSet鎵撳嵃鍒頒竴涓緭鍑烘祦銆傝繖鏄竴涓煎緱涓句緥鐨勫緢甯哥敤鐨勪緥瀛愶紝涓嬮潰鏄皟鐢ㄥ悓涓涓瓨鍌ㄨ繃紼嬬殑鍙﹀涓涓柟娉曞疄鐜幫細
public class ProcessPoetDeaths{
public abstract void sendDeath(String name, int age);
}
static void mapEarlyDeaths(ProcessPoetDeaths mapper){
Connection con = null;
CallableStatement toesUp = null;
try {
con = ConnectionPool.getConnection();
con.setAutoCommit(false);
CallableStatement toesUp = connection.prepareCall("{ ? = call list_early_deaths () }");
toesUp.registerOutParameter(1, Types.OTHER);
toesUp.execute();
ResultSet rs = (ResultSet) toesUp.getObject(1);
while (rs.next()) {
String name = rs.getString(1);
int age = rs.getInt(2);
mapper.sendDeath(name, age);
}
rs.close();
} catch (SQLException e) { // We should protect these calls. toesUp.close();
con.close();
}
}
榪欏厑璁稿湪ResultSet鏁版嵁涓婃墽琛屼換鎰忕殑澶勭悊錛岃屼笉闇瑕佹敼鍙樻垨鑰呭鍒惰幏鍙朢esultSet鐨勬柟娉曪細
static void sendEarlyDeaths(final PrintWriter out){
ProcessPoetDeaths myMapper = new ProcessPoetDeaths() {
public void sendDeath(String name, int age) {
out.println(name + " was " + age + " years old.");
}
};
mapEarlyDeaths(myMapper);
}
榪欎釜鏂規硶浣跨敤ProcessPoetDeaths鐨勪竴涓尶鍚嶅疄渚嬭皟鐢╩apEarlyDeaths銆傝瀹炰緥鎷ユ湁sendDeath鏂規硶鐨勪竴涓疄鐜幫紝鍜屾垜浠笂闈㈢殑渚嬪瓙涓鏍風殑鏂瑰紡鎶婄粨鏋滃啓鍏ュ埌杈撳嚭嫻併傚綋鐒訛紝榪欎釜鎶宸у茍涓嶆槸瀛樺偍榪囩▼鐗規湁鐨勶紝浣嗘槸鍜屽瓨鍌ㄨ繃紼嬩腑榪斿洖鐨凴esultSet緇撳悎浣跨敤錛屾槸涓涓潪甯稿己澶х殑宸ュ叿銆?
緇撹
瀛樺偍榪囩▼鍙互甯姪浣犲湪浠g爜涓垎紱婚昏緫錛岃繖鍩烘湰涓婃繪槸鏈夌泭鐨勩傝繖涓垎紱葷殑濂藉鏈夛細
• 蹇熷垱寤哄簲鐢紝浣跨敤鍜屽簲鐢ㄤ竴璧鋒敼鍙樺拰鏀瑰杽鐨勬暟鎹簱妯″紡銆?
• 鏁版嵁搴撴ā寮忓彲浠ュ湪浠ュ悗鏀瑰彉鑰屼笉褰卞搷Java瀵硅薄錛屽綋鎴戜滑瀹屾垚搴旂敤鍚庯紝鍙互閲嶆柊璁捐鏇村ソ鐨勬ā寮忋?
• 瀛樺偍榪囩▼閫氳繃鏇村ソ鐨凷QL宓屽叆浣垮緱澶嶆潅鐨凷QL鏇村鏄撶悊瑙c?
• 緙栧啓瀛樺偍榪囩▼姣斿湪Java涓紪鍐欏祵鍏ョ殑SQL鎷ユ湁鏇村ソ鐨勫伐鍏鳳紞錛嶅ぇ閮ㄥ垎緙栬緫鍣ㄩ兘鎻愪緵璇硶楂樹寒錛?
• 瀛樺偍榪囩▼鍙互鍦ㄤ換浣昐QL鍛戒護琛屼腑嫻嬭瘯錛岃繖浣垮緱璋冭瘯鏇村姞瀹規槗銆?
騫朵笉鏄墍鏈夌殑鏁版嵁搴撻兘鏀寔瀛樺偍榪囩▼錛屼絾鏄瓨鍦ㄨ澶氬緢媯掔殑瀹炵幇錛屽寘鎷厤璐?寮婧愮殑鍜岄潪鍏嶈垂鐨勶紝鎵浠ョЩ妞嶅茍涓嶆槸涓涓棶棰樸侽racle銆丳ostgreSQL鍜孌B2閮芥湁綾諱技鐨勫瓨鍌ㄨ繃紼嬭璦錛屽茍涓旀湁鍦ㄧ嚎鐨勭ぞ鍖哄緢濂藉湴鏀寔銆?
瀛樺偍榪囩▼宸ュ叿寰堝錛屾湁鍍廡OAD鎴朤ORA榪欐牱鐨勭紪杈戝櫒銆佽皟璇曞櫒鍜孖DE錛屾彁渚涗簡緙栧啓銆佺淮鎶L/SQL鎴杙l/pgsql鐨勫己澶х殑鐜銆?
瀛樺偍榪囩▼紜疄澧炲姞浜嗕綘鐨勪唬鐮佺殑寮閿錛屼絾鏄畠浠拰澶у鏁扮殑搴旂敤鏈嶅姟鍣ㄧ浉姣旓紝寮閿灝忓緱澶氥傚鏋滀綘鐨勪唬鐮佸鏉傚埌闇瑕佷嬌鐢―BMS錛屾垜寤鴻鏁翠釜閲囩敤瀛樺偍榪囩▼鐨勬柟寮忋?/p>
鍙﹀錛屼綘涓嶆効鎰忎綘鐨凞AO嫻嬭瘯浠g爜姣忔閮芥墦寮鍏崇郴Session錛屽洜姝わ紝鎴戜滑涓鑸細閲囩敤OpenSessionInView妯″紡銆?
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { SessionFactory sessionFactory = lookupSessionFactory(); logger.debug("Opening Hibernate Session in OpenSessionInViewFilter"); Session session = getSession(sessionFactory); TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); try { filterChain.doFilter(request, response); } finally { TransactionSynchronizationManager.unbindResource(sessionFactory); logger.debug("Closing Hibernate Session in OpenSessionInViewFilter"); closeSession(session, sessionFactory); } }
涓轟粈涔堢粦瀹氫互鍚庯紝灝卞彲浠ラ槻姝㈡瘡嬈′笉浼氭柊寮涓涓猄ession鍛紵鐪嬬湅HibernateDaoSupport鐨勬儏鍐碉細
publicfinal void setSessionFactory(SessionFactory sessionFactory) { this.hibernateTemplate = new HibernateTemplate(sessionFactory); } protectedfinal HibernateTemplate getHibernateTemplate() { return hibernateTemplate; }
鎴戜滑鐨凞AO灝嗕嬌鐢ㄨ繖涓猼emplate榪涜鎿嶄綔錛?
publicabstract class BaseHibernateObjectDao extends HibernateDaoSupport implements BaseObjectDao {protected BaseEntityObject getByClassId(finallong id) { BaseEntityObject obj = (BaseEntityObject) getHibernateTemplate() .execute(new HibernateCallback() {
publicObject doInHibernate(Session session) throws HibernateException { return session.get(getPersistentClass(), newLong(id)); }
}); return obj; }
public void save(BaseEntityObject entity) { getHibernateTemplate().saveOrUpdate(entity); }
public void remove(BaseEntityObject entity) { try {
getHibernateTemplate().delete(entity); } catch (Exception e) { thrownew FlexEnterpriseDataAccessException(e); } }
public void refresh(final BaseEntityObject entity) { getHibernateTemplate().execute(new HibernateCallback() {
publicObject doInHibernate(Session session) throws HibernateException { session.refresh(entity); returnnull; }
}); }
public void replicate(finalObject entity) { getHibernateTemplate().execute(new HibernateCallback() {
publicObject doInHibernate(Session session) throws HibernateException { session.replicate(entity, ReplicationMode.OVERWRITE); returnnull; }
}); }
publicObject execute(HibernateCallback action) throws DataAccessException { Session session = (!this.allowCreate ? SessionFactoryUtils.getSession(getSessionFactory(), false) : SessionFactoryUtils.getSession(getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator())); boolean existingTransaction = TransactionSynchronizationManager.hasResource(getSessionFactory()); if (!existingTransaction && getFlushMode() == FLUSH_NEVER) { session.setFlushMode(FlushMode.NEVER); } try { Object result = action.doInHibernate(session); flushIfNecessary(session, existingTransaction); return result; } catch (HibernateException ex) { throw convertHibernateAccessException(ex); } catch (SQLException ex) { throw convertJdbcAccessException(ex); } catch (RuntimeException ex) { // callback code threw application exception throw ex; } finally { SessionFactoryUtils.closeSessionIfNecessary( session, getSessionFactory()); } }
publicstatic void closeSessionIfNecessary(Session session, SessionFactory sessionFactory) throws CleanupFailureDataAccessException { if (session == null || TransactionSynchronizationManager.hasResource(sessionFactory)) { return; } logger.debug("Closing Hibernate session"); try { session.close(); } catch (JDBCException ex) { // SQLException underneath thrownew CleanupFailureDataAccessException( "Cannot close Hibernate session", ex.getSQLException()); } catch (HibernateException ex) { thrownew CleanupFailureDataAccessException( "Cannot close Hibernate session", ex); } }
浣跨敤鍚屾牱鐨勬柟娉曪紝榪欎袱涓狪nterceptor鍙互鐢ㄦ潵瑙e喅闂銆備絾鏄叧閿殑涓嶅悓涔嬪鍦ㄤ簬錛屽畠浠殑鍔涘害鍙兘瀹氫箟鍦―AO鎴栦笟鍔℃柟娉曚笂錛岃屼笉鏄湪鎴戜滑鐨凾est鏂規硶涓婏紝闄ら潪鎴戜滑鎶婂畠浠簲鐢ㄥ埌TestCase鐨勬柟娉曚笂錛屼絾浣犱笉澶у彲鑳戒負TestCase鍘誨畾涔変竴涓帴鍙o紝鐒跺悗鎶奍nterceptor搴旂敤鍒拌繖涓帴鍙g殑鏌愪簺鏂規硶涓娿傜洿鎺ヤ嬌鐢℉ibernateTransactionManager涔熸槸涓鏍風殑銆傚洜姝わ紝濡傛灉鎴戜滑鏈夎繖鏍風殑嫻嬭瘯錛?
Category parentCategory = new Category (); parentCategory.setName("parent"); dao.save(parentCategory);Category childCategory = new Category(); childCategory.setName("child");
parentCategory.addChild(childCategory); dao.save(childCategory);
Category savedParent = dao.getCategory("parent"); Category savedChild = (Category ) savedParent.getChildren().get(0); assertEquals(savedChild, childCategory);
涓縐嶆柟娉曟槸瀵筎estCase搴旂敤Interceptor鎴栬匱ransactionManager錛屼絾榪欎釜鎭愭曚細閫犳垚寰堝楹葷儲銆傞櫎闈炴槸浣跨敤澧炲己鏂瑰紡鐨凙OP.鎴戝墠鏈熼噰鐢ㄨ繖縐嶆柟娉?Aspectwerkz)錛屽湪Eclipse閲岄潰涔熻窇寰楀惈濂姐?
鍙︿竴縐嶆柟娉曟槸鍦═estCase鐨剆etup鍜宼eardown閲岄潰瀹炵幇鍜孎ilter瀹屽叏涓鏍風殑澶勭悊錛屽叾浠栫殑TestCase閮戒粠榪欎釜TestCase緇ф壙錛岃繖縐嶆柟娉曟槸鎴戠洰鍓嶆墍浣跨敤鐨勩?
Jolestar琛ュ厖:openSessionInView鐨勯厤緗柟娉?
聽聽 <filter>
聽 聽 聽 聽 <filter-name>opensession</filter-name>
聽 聽 聽 聽 <filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
聽 聽 聽 聽 <init-param>
聽 聽 聽 聽 聽 聽 <param-name>singleSession</param-name>
聽 聽 聽 聽 聽 聽 <param-value>false</param-value>
聽 聽 聽 聽 </init-param>
聽 聽 </filter>
聽聽 c Read-Only
聽聽 If a transaction performs only read operation against the underlying datastore.when a transaction begin ,it only make sense to declare a transaction as read only on mehtods with
propagation behavior which start a new transaction.
聽聽 Furthermore ,if you are Hibernate as persistence mechanism,declaring a transaction as read only will reult in Hibernate flush mode being set to FLUST_NEVER.this tell hibernate to avoid synchroniztion of objects with database.
聽聽 d Transaction timeout
聽聽 Suppose that your transaction becomes unexpectedly long-running transaction.Because transaction may invole locks on the underlying database.Instead of waiting it out ,you can delcare a transaction to automaitically roll back.
聽聽 because timeout clock begin ticking when a transaction start. it only make sense to declare a transaction timeout on methods with propagation behavior that start a new transaction.
聽 2) Declaring a simple transaction policy
聽聽 <bean id="myTransactionAttribute"
聽聽聽 class="org.springframework.transaction.interceptor.
聽聽聽聽聽聽聽聽聽聽聽 DefaultTransactionAttribute">
聽 <property name="propagationBehaviorName">
聽聽聽 <value>PROPAGATION_REQUIRES_NEW</value>
聽 </property>
聽 <property name="isolationLevelName">
聽聽聽 <value>ISOLATION_REPEATABLE_READ</value>
聽 </property>
聽</bean>
聽<bean id="transactionAttributeSource"
聽聽聽 class="org.springframework.transaction.interceptor.
聽聽聽聽聽聽聽聽聽聽聽 MatchAlwaysTransactionAttributeSource">
聽 <property name="transactionAttribute">
聽聽聽 <ref bean="myTransactionAttribute"/>
聽 </property>
聽</bean>
4 Declaring transactions by method name
聽 1) Using NameMatchTransactionAttributeSource
聽 The properties property of NameMatchTransactionAttributeSource maps mehtod to a transaction property descriptor. the property descriptor takes the following form:
聽 Propagation,isolation,readOnly,-Exception,+Exception
聽
聽 <bean id="transactionAttributeSource"
聽聽聽 class="org.springframework.transaction.interceptor.
聽聽聽聽聽聽聽聽聽聽聽 NameMatchTransactionAttributeSource">
聽 <property name="properties">
聽聽聽 <props>
聽聽聽聽聽 <prop key="enrollStudentInCourse">
聽聽聽聽聽聽聽聽聽 PROPAGATION_REQUIRES_NEW
聽聽聽聽聽 </prop>
聽聽聽 </props>
聽 </property>
聽</bean>
聽2) Specifying the transaction Isolation level
聽 <bean id="transactionAttributeSource"
聽聽聽 class="org.springframework.transaction.interceptor.
聽聽聽聽聽聽聽聽聽聽聽 NameMatchTransactionAttributeSource">
聽聽聽聽 <property name="properties">
聽聽聽 <props>
聽聽聽聽聽 <prop key="enrollStudentInCourse">
聽聽聽聽聽聽聽 PROPAGATION_REQUIRES_NEW,ISOLATION_REPEATABLE_READ
聽聽聽聽聽 </prop>
聽聽聽 </props>
聽 </property>
聽</bean>
聽3) Using real-only transaction
聽 <bean id="transactionAttributeSource"
聽聽聽 class="org.springframework.transaction.interceptor.
聽聽聽聽聽聽聽聽聽聽聽 NameMatchTransactionAttributeSource">
聽 <property name="properties">
聽聽聽 <props>
聽聽聽聽聽 <prop key="getCompletedCourses">
聽聽聽聽聽聽聽 PROPAGATION_REQUIRED,ISOLATION_REPEATABLE_READ,readOnly
聽聽聽聽聽 </prop>
聽聽聽 </props>
聽 </property>
</bean>
聽4)Specifying聽 rollback rules
聽 You can sepcify that a transaction be rollback on specify checked exception
聽 <bean id="transactionAttributeSource"
聽聽聽 class="org.springframework.transaction.interceptor.
聽聽聽聽聽聽聽聽聽聽聽 NameMatchTransactionAttributeSource">
聽 <property name="properties">
聽聽聽 <props>
聽聽聽聽聽 <prop key="enrollStudentInCourse">
聽聽聽聽聽聽聽 PROPAGATION_REQUIRES_NEW,ISOLATION_REPEATABLE_READ,
聽聽聽聽聽聽聽 -CourseException
聽聽聽聽聽聽聽 </prop>
聽聽聽聽 </props>
聽聽聽 </property>
聽 </bean>
聽 Exception can be marked as negative(-) or postive(+)
聽 Negative exception will trigger the roll back if the exception (or sublclass of it) is thrown.Postive exception on the other hand indicate that the transacton should be commit
even if the exception is thrown
聽5)Using wildcard matches
聽<bean id="transactionAttributeSource"
聽聽聽 class="org.springframework.transaction.interceptor.
聽聽聽聽聽聽聽聽聽聽聽 NameMatchTransactionAttributeSource">
聽 <property name="properties">
聽聽聽 <props>
聽聽聽聽聽 <prop key="get*">
聽聽聽聽聽聽聽 PROPAGATION_SUPPORTS
聽聽聽聽聽 </prop>
聽聽聽 </props>
聽 </property>
</bean>
聽6 Short-cut name match transaction
聽<bean id="courseService" class="org.springframework.transaction.
聽聽聽聽聽聽 interceptor.TransactionProxyFactoryBean">
聽聽 <property name="transactionProperties">
聽聽聽 <props>
聽聽聽聽聽 <prop key="enrollStudentInCourse">
聽聽聽聽聽聽聽 PROPAGATION_REQUIRES_NEW
聽聽聽聽聽 </prop>
聽聽聽 </props>
聽聽 </property>
聽</bean>
聽聽聽 </props>
聽 </property>
聽 鈥?br />聽</bean>
聽and the last thing is whick map files is read
聽 <bean id="sessionFactory" class="org.springframework.
聽聽聽聽聽聽 orm.hibernate.LocalSessionFactoryBean">
聽 <property name="mappingResources">
聽聽聽聽 <list>
聽聽聽聽 <value>Student.hbm.xml</value>
聽聽聽聽 <value>Course.hbm.xml</value>
聽聽聽聽 鈥?br />聽聽 </list>
聽 </property>
聽聽聽聽 鈥?br />聽 </bean>
聽Now聽 you have fully configured your sessionfactory ,so we need do create an object which we
will access hibernate. As we know, we will use a template class
聽 <bean id="hibernateTemplate"
聽聽聽聽聽 class="org.springframework.orm.hibernate.HibernateTemplate">
聽 <property name="sessionFactory">
聽聽聽 <ref bean="sessionFactory"/>
聽 </property>
聽</bean>
聽 <bean id="courseDao" class="com.springinaction.
聽聽聽聽聽聽 training.dao.hibernate.CourseDaoHibernate">
聽 <property name="hibernateTemplate">
聽聽聽 <ref bean="hibernateTemplate"/>
聽 </property>
聽</bean>
聽2 Accessing Hibernate through HibernatTemplate
聽 The template-callback mechanism in Hibernatee is pretty simple.There is the HibernatTmpplate and one callback interface
聽 public Student getStudent(final Integer id) {
聽 return (Student) hibernateTemplate.execute(
聽聽聽 new HibernateCallback() {
聽聽聽聽聽 public Object doInHibernate(Session session)
聽聽聽聽聽聽聽聽聽 throws HibernateException {
聽聽聽聽聽聽聽 return session.load(Student.class, id);
聽聽聽聽聽 }
聽聽聽 });
聽
聽 The HibernateTemplate class provides some convience methods that implicit create a HibernateCallback instance:
聽 (Student) hibernateTemplate.load(Student.class, id);
聽聽 hibernateTemplate.update(student);
聽 hibernateTemplate.find("from Student student " +
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 "where student.lastName = ?",
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 lastName, Hibernate.STRING);
聽 3 Subclassing HibernateDaoSupport
聽 public class StudentDaoHibernate extends HibernateDaoSupport
聽聽聽 implements StudentDao {
聽 鈥?br />聽 }
聽 getHibernateTemplate()
聽 getSession()
聽 緙虹偣錛氬唴瀛樹腑鍔犺澆浜嗗ぇ閲忔暟鎹?br />聽聽聽聽聽聽聽 鎵ц浜嗗嬈pdate 璇彞
聽
聽聽 鏀硅繘
聽聽 Iterator customers=session.find("from Customer c where c.age>0");
聽聽 while(customers.hasNext()){
聽聽聽聽 Customer customer=(Customer)customers.next();
聽聽聽聽 customer.setAge(customer.getAge()+1);
聽聽聽聽 session.flush();
聽聽聽聽 session.evict(customer);
聽聽 }
聽聽 tx.commit();
聽聽 session.close();
聽聽 閬楃暀闂
聽聽 鎵ц浜嗗嬈pdate 璇彞
聽聽
聽聽 閲囩敤jdbc api 榪涜璋冪敤
聽聽 Connection con=session.connection();
聽聽 PrepareStatement stmt=con.prepareStatement("update customers set age=age+1 where age>0");
聽聽 stmt.executeUpdate();
聽聽 tx.commit();
聽聽 鍙﹀錛屼篃鍙互璋冪敤搴曞眰鐨勫瓨鍌ㄨ繃紼嬭繘琛屾壒閲忔洿鏂?br />聽聽 create or replace procedure batchUpdateCustomer(p_age,in number) as
聽聽 begin
聽聽聽聽聽 update customer set age=age+1 where age>p_age;
聽聽 end;
聽聽
聽聽 tx=session.beginTransaction();
聽聽 Connection con=session.connection();
聽聽 CallableStatement cstmt=con.prepareCall(batchUpdateCustomer);
聽聽 cstmt.setInt(1,0);
聽聽 cstmt.eqecuteUpdate();
聽聽 tx.commit();
聽聽 2) 鎵歸噺鏁版嵁鐨勫垹闄?br />聽聽聽 session.delete("from聽 Customer c where c.age>0");
聽聽聽 瀹為檯璋冪敤鐨勮繃紼?br />聽聽聽 session * from Customer where age>0;
聽聽聽 鍦ㄦ妸鎵鏈夋暟鎹姞杞藉埌鍐呭瓨涔嬪悗鎵ц澶氭潯delete 璇彞
聽聽聽 delete from customer where id=i;
聽聽聽聽 .......................
聽聽 鏀硅繘鍔炴硶閲囩敤jdbc api 榪涜鎵歸噺鏁版嵁鐨勫垹闄?br />聽聽聽聽聽
聽聽 tx=session.beginTransaction();
聽聽 Connection con=session.connection();
聽聽 con.execute("delete from customers where age>0");
聽聽 tx.commit();
integer or int聽聽聽聽聽聽聽聽聽聽聽聽 int or Integer聽聽聽聽聽聽聽聽聽聽 INTEGER
long聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 long or Long聽聽聽聽聽聽聽聽聽聽聽聽 BIGINT
short聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 short or Short聽聽聽聽聽聽聽聽聽聽 SMALLINT
byte聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 byte or Byte聽聽聽聽聽聽聽聽聽聽聽聽 TINYINT
float聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 float or Float聽聽聽聽聽聽聽聽聽聽 FLOAT
double聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 double or Double聽聽聽聽聽聽聽聽 DOUBLE
big_decimal聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 java.math.BigDecimal聽聽聽聽 NUMBERBIC
character聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 char java.lang.Character CHAR(1)
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 String聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽
string聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 String聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 VARCHAR
boolean聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 boolean or Boolean聽聽聽聽聽聽聽 BIT
date聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 java.util.Date聽聽聽聽聽聽聽聽聽聽聽 DATE
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 java.sql.Date
time聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 Date or java.sql.time聽聽聽聽 TIME
timestamp聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 Date or java.sql.Timestamp TIMESTAMP聽聽聽聽聽聽聽聽聽聽聽聽聽聽
binary聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 byte[]聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 blog聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 blog
text聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 String聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 clob聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 clog
serializable聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 blog聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 blog聽聽聽聽聽聽聽聽聽聽聽聽
clob聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 java.sql.clob聽聽聽聽聽聽聽聽聽聽聽 clob聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 clob
blob聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 java.sql.blob聽聽聽聽聽聽聽聽聽聽聽聽 blog聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 blob
contexts.Beyong there two basic types of contains .Srping come with sereral implementss of BeanFacotory and ApplicationContext.
聽 1 introducing the BeanFactory
聽 There are several implementations of BeanFactory in Spring .But the most userful one is XmlBeanFactory,whick loads
its bean based on the definitions contained in an xml file.
聽 Creat a XmlBeanFactory聽 BeanFactory factory=new XMLBeanFactory(new FileInputStream("beans.xml"));
聽But at that time ,the BeanFactory does not initialize the bean ,it is loaded lazliy.
聽 Get the Bean :MyBean myBean=(MyBean)factory.getBean("myBean");
聽When getBean() is called ,the factory will instantiate the bean and being setting the bean using dependency injection.
聽 2 Working with an application context
聽an ApplicationContextis prefered over a BeanFactory in nearly all application ,the only time you might consider using a BeanFactory
are in circumtance where resource s are scarce.
聽聽 Amony the many implements of ApplicationContext are three that are commonly used:
聽聽聽聽聽 ClassPathXmlApplicationContext,FileSystemXmpApplicationContext,XmlWebApplicationContext.
聽聽 ApplicationContext context=new ClassPathXmlApplicationContext("foo.xml");
聽wiring the beans
聽 <beans>
聽聽聽 <bean id="foo" class="com.springinaction.Foo" />
聽</beans>
聽Prototyping vs.singleton
聽By default ,all Spring beans are singletons.When the container dispenses a bean it will always give the exact same instance of the聽聽聽聽
In this case ,you would want to define a prototype bean .Defining a prototype means that instead of defining a single bean.
聽聽聽聽 <bean id="foo" class="com.springinaction.Foo"聽 single/>on="false" />
Initialization and destruction
聽聽 <bean id="foo" class="com.springinaction.Foo" init-method="setup" destory-method="teardown">
Injectiong dependencies via setter method
聽聽 <bean id="foo" class="com.srpinginaction.Foo" >
聽聽聽聽聽聽聽 <property name="name">Foo McFoo</value>
聽聽 </bean>
聽Referencing other beans
聽聽 <bean id="foo" class="com.springinaction.Foo">
聽聽聽聽 <property name="bar" >
聽聽聽聽聽聽聽 <ref bean="bar" />
聽聽聽聽 </property>
聽聽聽 </bean>
聽聽聽 <bean id="bar" colass="com.srpinginaction.Bar" />
聽Inner beans
聽聽 <bean id="courseService"
聽聽聽聽聽聽聽聽聽聽聽聽 class="com.CourseWericeImpl">
聽聽聽聽 <property nanme="studentService">
聽聽聽聽聽聽聽聽 <bean
聽聽聽聽聽聽聽聽聽聽聽聽聽 class="com....." />
聽聽聽聽聽 </property>
聽聽 </bean>
聽 Wiring collections
聽聽聽 1Wiring lists and arrays聽 java.util.List
聽聽聽聽聽 <property name="barList">
聽聽聽聽聽聽聽聽聽聽 <list>
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 <value>bar1</value>
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 <ref bean="bar2"/>
聽聽聽聽聽聽聽聽聽聽 </lsit>
聽聽聽聽聽 </property>
聽聽 2 Wiring set聽 java.tuil.Set
聽聽聽 <property name="barSet">
聽聽聽聽聽聽聽聽聽 <set>
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 <value>bar1</value>
聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽聽 <ref bean="bar2" />
聽聽聽聽聽聽聽聽聽 </set>
聽聽 </property>
聽聽 3 Wiring maps java.util.Map
聽聽 <property name="barMap">
聽聽聽聽聽聽聽 <ebtry key="key1">
聽聽聽聽聽聽聽 <value>bar1</value>
聽聽 </property>
聽 4 Wiring propertyies
聽聽聽 <property name="barProps">
聽聽聽聽聽聽聽聽 <props>
聽聽聽聽聽聽聽聽聽聽聽 <prop key="key1">bar1</prop>
聽聽聽聽聽聽聽聽聽聽聽 <prop key="key2">bar2</prop>
聽聽聽聽聽聽聽聽 </props>
聽聽聽 </property>
聽 5 Setting null values
聽聽 <property name="foo"><null/><property>
聽聽 injecting dependencies via constructor
聽聽 <id="foo" class="com.springinaction.Foo">
聽聽聽 <constructor-arg>
聽聽聽聽聽聽聽 <value>42<value>( <ref bean="bar">)
聽聽聽聽 </constructor-arg>
聽聽聽 <bean id="foo" class="com.springinaction.Foo">
聽聽聽聽聽聽 <constructor-arg>
聽聽聽聽聽聽聽聽聽聽聽 <value>http://www.manning.com</value>
聽聽聽聽聽聽 </constructor-arg>
聽 <constructor-arg>
聽聽聽聽聽聽聽聽聽聽聽 <value>http://www.manning.com</value>
聽聽聽聽聽聽 </constructor-arg>
聽聽 </bean>