??xml version="1.0" encoding="utf-8" standalone="yes"?>
First lets start with a simple POJO (Plain Old Java Object).
import java.util.Date; public class Address { private String line1; private String line2; private String city; private String state; private String postalCode; private Date purchaseDate; private Date soldDate; ... Standard mutators (getters setters) ... }
Here is the example table that we will be using.
Address { line1 varchar2(100), line2 varchar2(100), city varchar2(100), state varchar2(100), postal_code varchar2(25), purchase_date date, sold_date date }
With this in place we can create the sqlmaps to work with it. With this error only your insert and update statements will be of concern. I will use an insert both the solution works for both.
<typeAlias alias="Address" type="com.abc.domain.Address"/> <insert id="insertAddress" parameterClass="Address"> INSERT INTO ADDRESS ( line1, line2, city, state, postal_code, purchase_date, sold_date ) values ( #line1#, #line2#, #city#, #state#, #postal_code#, #purchase_date#, #sold_date# ) </insert>
So far everything looks good and will work part of the time. In the following example the classic invalid column type error will be thrown.
Address address = new Address(); address.setLine1("123 Anywhere Street"); address.setCity("Somewhere over the Rainbow"); address.setState("Never never land"); address.setPostalCode("01234"); address.setPurcahseDate(purcahseDate); //purchaseDate defined somewhere above this code
Notice in the code above that I leave the line2 and soldDate properties as null. This is where the problem creeps in. When the insert sqlmap come to the line2 column it will throw an error. There are two solutions for this problem.
The first is to use dynamic conditions.
<typeAlias alias="Address" type="com.abc.domain.Address"/> <insert id="insertAddress" parameterClass="Address"> INSERT INTO ADDRESS ( line1, line2, city, state, postal_code, purchase_date, sold_date ) values ( #line1#, <isNull property="line2"> null, </isNull> <isNotNull property="line2"> #line2#, </isNotNull> #city#, #state#, #postal_code#, #purchase_date#, #sold_date# ) </insert>
As you can see this can become a time consuming process to make sure that every column that can be null has these tags. A second and cleaner option is to define what the jdbc type is.
<typeAlias alias="Address" type="com.abc.domain.Address"/> <insert id="insertAddress" parameterClass="Address"> INSERT INTO ADDRESS ( line1, line2, city, state, postal_code, purchase_date, sold_date ) values ( #line1#, #line2:VARCHAR#, #city#, #state#, #postal_code#, #purchase_date#, #sold_date:DATE# ) </insert>
Just do this only any column that can be null! Now you may be asking yourself what are the valid jdbc types. Look no further they are posted in the Java API documents java.sql.Types.
property | getter | setter | comment |
---|---|---|---|
AString | getAString | setAString | Surprise! This is not a property named aString as you expected.. |
aString | getaString | setaString | Notice the lower case letter after get/set? That's the key. |
string | getString | setString |
The queryForMap()'s are most useful when you need to work with the result of a query. In the example below I will use a query that return Student objects as a result. Lets start with the Student class.
public class Student implements java.io.Serializable { private String id, firstName, lastName; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; }
And now the SqlMap ....
<resultMap id="studentResult" class="com.domain.Student"> <result column="id" property="id" /> <result column="first_name" property="firstName" /> <result column="last_name" property="lastName" /> </resultMap> <select id="getAllStudents" resultMap="studentResult"> select * from students </select>
Now that the basics are out of the way query away!
Lets say that you need to do some fast serching on the returned students. The best way is to have a Map of the Student objects to their id's.
This is where the queryForMap() comes into play. There are two different options and both will be dicussed. The first is perfect for the example given above.
public Map getAllStudents() { Map<String, Student> studentsMap = queryForMap("getAllStudents", null, "id"); Student student = studentsMap.get("123456"); ... }
You can see that the third parameter in the call looks for the property "id" in the result map and then creates the map with that property as the key.
The second option allow you to specify both the key and the value.
Map<String, String> studentsMap = queryForMap("getAllStudents", null, "id", "firstName");
The above code will return a Map with the id as the key and the firstName as the value.
There are many other uses for these method so feel free to add them on
This FAQ is based on iBATIS DataMapper (Java) 2.2.0.638
Release 2.2.0 of iBATIS DataMapper natively supports Oracle REF Cursors - without the need to create a custom type handler. This FAQ shows a very small example on how to use it.
iBatis2.2.0之后Q开始支持Oracle的CursorQ不需要依赖于CTH?br />
We start with the (Oracle) database related stuff. For this example we've created an Oracle user ibatis. In SQL*Plus (or whichever tool you use) log in as this user and run the commands shown below.
CREATE TABLE REFS ( ID NUMBER NOT NULL PRIMARY KEY , NAME VARCHAR2(50) NOT NULL ); CREATE OR REPLACE PACKAGE REFS_PCK AS TYPE REF_CURSOR_T IS REF CURSOR; FUNCTION GET_REFS RETURN REF_CURSOR_T; END REFS_PCK; / CREATE OR REPLACE PACKAGE BODY REFS_PCK IS FUNCTION GET_REFS RETURN REF_CURSOR_T IS L_CURSOR REF_CURSOR_T; BEGIN OPEN L_CURSOR FOR SELECT * FROM REFS; RETURN L_CURSOR; END GET_REFS; END REFS_PCK; / insert into refs values(1,'Jan'); insert into refs values(2,'Danielle'); insert into refs values(3,'Tessa');
We create a simple Java bean class to hold REFS' records.
package com.cumquatit.examples.ibatis.refs; public class Ref { private int id; private String name; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String toString() { return ("id=" + id + ", name=" + name); } }
Next create a mapping file for the REFS table.
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlMap> <typeAlias alias="Ref" type="com.cumquatit.examples.ibatis.refs.Ref" /> <resultMap class="Ref" id="ref-mapping"> <result property="id" column="ID" /> <result property="name" column="NAME" /> </resultMap> <parameterMap id="output" class="map"> <parameter property="o" javaType="java.sql.ResultSet" jdbcType="ORACLECURSOR" mode="OUT" resultMap="ref-mapping" /> </parameterMap> <procedure id="getRefs" parameterMap="output">{ ? = call refs_pck.get_refs }</procedure> </sqlMap>
Also we need a sqlMapConfig file. Below an example of this file.
<?xml version="1.0"?> <!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-config-2.dtd"> <sqlMapConfig> <transactionManager type="JDBC" commitRequired="false"> <dataSource type="SIMPLE"> <property name="JDBC.Driver" value="oracle.jdbc.OracleDriver" /> <property name="JDBC.ConnectionURL" value="jdbc:oracle:thin:@//flash.cumquat.office:1524/neon" /> <property name="JDBC.Username" value="ibatis" /> <property name="JDBC.Password" value="ibatis" /> </dataSource> </transactionManager> <sqlMap resource="com/cumquatit/examples/ibatis/refs/Ref.xml" /> </sqlMapConfig>
And to finish up, a small tester program.
package com.cumquatit.examples.ibatis.refs; import java.io.Reader; import java.util.HashMap; import java.util.Map; import com.ibatis.common.resources.Resources; import com.ibatis.sqlmap.client.SqlMapClient; import com.ibatis.sqlmap.client.SqlMapClientBuilder; public class RefTester { public static void main(String[] args) throws Exception { String resource; Reader reader; SqlMapClient sqlMap; resource = "com/cumquatit/examples/ibatis/refs/SqlMapConfig.xml"; reader = Resources.getResourceAsReader(resource); sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader); Map map = new HashMap(); sqlMap.queryForObject("getRefs", map); System.out.println(map.get("o")); } }
That's all folks.
The example below was not the intended way to use CTH's but it works great for me!
First lets take a look at the table.
REPORT { id varchar2(5), name varchar2(25), description varchar2(1000), data BLOB }
Next we continue by creating a plain old java object (POJO) to represent this table.
/* * Report.java * * Created on March 23, 2005, 11:00 AM */ package reporting.viewer.domain; /** * * @author Nathan Maves */ public class Report { /** * Holds value of property id. */ private String id; /** * Holds value of property name. */ private String name; /** * Holds value of property description. */ private String id; /** * Holds value of property data. */ private byte[] data; //Standard accessors and mutators public byte[] getData() { return this.data; } public void setData(byte[] data) { this.data = data; } }
Now that the easy stuff is completed let connect both the database and the POJO together using iBatis.
<typeAlias alias="Report" type="reporting.viewer.domain.Report"/> <resultMap class="Report" id="ReportResult"> <result column="id" property="id" /> <result column="name" property="name" /> <result column="description" property="description" /> <result column="data" property="data" jdbcType="BLOB"/> </resultMap> <select id="getReportById" parameterClass="string" resultMap="ReportResult"> SELECT * FROM REPORT WHERE id = #value# </select> <insert id="insertReport" parameterClass="Report"> INSERT INTO REPORT ( id, name, description, data ) values ( #id#, #name#, #description#, #data# ) </insert> <update id="updateReport" parameterClass="Report"> UPDATE REPORT set name = #name#, description = #description#, data = #data# WHERE id = #id# </update>
As you can see there is nothing special that you need to do.
When working with a CLOB the only that the you need to change is the property in your bean. Just change byte[] to java.lang.String.
![]() |
Data size bigger than max size for this type: ???? Some of the older jdbc drivers for Oracle have trouble with Strings that are larger then 4k. The first step to correct this issue it to get a jdbc driver from Oracle that is newer then 10g Release 2. This driver will work with both 9i and 10g databases. If you are stuck with an older driver you can try to set a driver property. The property is SetBigStringTryClob=true. If you are using the SimpleDataSource with iBatis use the follow line in the config file. |
![]() |
Data size always 86 bytes? If you find that the length of your byte[] is always 86, check that you have the jdbcType="BLOB" or jdbcType="CLOB" in your result map. |
The old implementation uses the prefix 'Pool.' to pass settings to DBCP, these are hardcoded and are: ValidationQuery, MaximumActiveConnections, MaximumIdleConnections and MaximumWait. The following example illustrates the prefix:
旧版本的dbcp使用方式
<dataSource type="DBCP"> <property name="JDBC.Driver" value="${driver}"/> <property name="JDBC.ConnectionURL" value="${url}"/> <property name="JDBC.Username" value="${username}"/> <property name="JDBC.Password" value="${password}"/> <property name="Pool.MaximumActiveConnections" value="10"/> </datasource>
To achieve the same and more, use the new implementation, it uses Bean-style settings:
新版本的配置
<dataSource type="DBCP"> <property name="driverClassName" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> <property name="maxActive" value="10"/> <property name="initialSize" value="5"/> <!-- Oracle specific for getString() / setString() CLOB-handling --> <property name="Driver.SetBigStringTryClob" value="true"/> </datasource>
Notice the use of the property 'initialSize', this one could not be specified in the previous configuration. Additional driver-specific properties can now be specified too, these have to be prefixed with 'Driver.' (see example).
An exhaustive listing of all available properties can be found at the Commons DBCP projectsite: http://jakarta.apache.org/commons/dbcp/configuration.html
分两个逻辑部分Q首先你必须在Tomcat里定义JNDI的DataSourceQ然后你必须告诉 iBatis通过JNDI名称使用那个对象?br />
Putting a DataSource into JNDI in Tomcat depends on the version of Tomcat
you have. We'll use Tomcat 5.5 as an example here. Take a look at the
Tomcat docs for specifics on your version or more details in general.
讄JNDI数据源取决于tomcat的版本,我们用Tomcat5.5作ؓ例子。通常需要参考Tomcat文在不同版本的tomcat上设|数据源?/p>
You declare JNDI DataSource objects in Tomcat using the Resource xml
element. Here's an example:
用Resource元素定义JNDI数据源。这是一个例子:
<Resource name="jdbc/AS400B" type="javax.sql.DataSource" password="password" driverClassName="com.ibm.as400.access.AS400JDBCDriver" maxIdle="2" maxWait="5000" validationQuery="select * from PODATA" username="userid" url="jdbc:as400:AS400B/RPTSTEXTDB;naming=system;date format=iso;time format=hms;prompt=false" maxActive="4"/>
The Resource element either goes in your global JNDI context or in a webapp
specific context. To put it in your global JNDI context, put your
DataSource Resource element as a child of the GlobalNamingResources element
in conf/server.xml. If you put it in the global JNDI context, you need to
use a ResourceLink in either your webapp's context or in the
conf/context.xml (which is shared by all webapps).
如果xJNDI数据源定义在全局Q需要修改conf/context.xml,加入ResourceLink?br />
Here's a ResourceLink
example:
<ResourceLink name="jdbc/AS400B" global="jdbc/AS400B" type="javax.sql.DataSource"/>
To put it in a webapp specific context depends on how your webapps are set
up. If you have a context file for your webapp, you can put the Resource as
a child of the Context element. If you don't have a context file for you
webapp, you can put the Resource element as a child of the webapp's Context
element in conf/server.xml.
Another place for the Resource element could be in the conf/context.xml
file, which is shared by all webapps.
Once the DataSource is bound in JNDI, you need to tell iBATIS to use it.
NOTE: that Tomcat helpfully prepends the string "java:/comp/env/" to your
names, which is needed to do the lookups.
If you're using the iBATIS sqlmap xml config files, you need this:
iBatis的sqlmap配置文g中,你需要这栯|数据源
<transactionManager type="JDBC"> <dataSource type="JNDI"> <property name="DataSource" value="java:/comp/env/jdbc/AS400B"/> </dataSource> </transactionManager>
If you're using Spring, you need to reference the JNDI DataSource object as
a JndiObjectFactoryBean and then set that as the DataSource on your
SqlMapClientFactoryBean. Here's an example:
Spring的配|需要如下,先用JNDI定义数据源,然后注入到SqlMapClientFacotryBean的dataSource属性里?br />
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"> <value>java:/comp/env/jdbc/AS400B</value> </property> </bean> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation" value="classpath:sqlmap-config.xml"/> <property name="dataSource" ref="dataSource"/> </bean>
Lets start by defining the database table
SHAPE { name varchar2(25), color number(100) }
As you see the color is stored as a number, but in the class Shape is is a Color object. There are two question that I ran into. First, how do you cleanly map the number stored in the database with the instance of a Color? Second, how can iBatis and custom type handlers help?
To answer the first let me show you the code for the Shape class.
/* * Shape.java * * Created on September 23, 2005 */ package domain; /** * * @author nmaves */ public class Shape { public enum Color { BLUE(1, "0000FF"), RED(2, "FF0000"), GREEN(3, "00FF00"); private int id; private String rgb; private Type(int id, String name) { this(id, name, "no mime type"); } private Type(int id, String rgb) { this.id = id; this. rgb = rgb; } public int getId() { return this.id; } public String getRGB() { return this.name; } public static Type getInstance(int i) { for(Color color : Color.values()) { if(color.id == i) { return type; } } throw new IllegalArgumentException("No such Color"); } } private String name; private Color color; /** Creates a new instance of Frequency */ private Shape(String name, Color color) { this.color = color; this.name = name; } public String toString() { return this.name; } public void setName(String name) { this.name = name; } public void setColor(Color color) { this.color = color; } public String getName() { return this.name; } public Color getColor() { return this.color; } }
As you see the getInstance(int i) method allows for the mapping of the number stored in the database to an instance of this class. I am sure there is a better way of pulling these initial value from a properties file but this is only an example.
For the second part you will need a way to allow iBatis to make the conversion. This is where the custom type handler comes into play.
Below is my ColorTypeHandler.java
package dao.ibatis; import com.ibatis.sqlmap.client.extensions.ParameterSetter; import com.ibatis.sqlmap.client.extensions.ResultGetter; import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback; import java.sql.SQLException; import java.sql.Types; import Shape.Color; public class ColorTypeHandler implements TypeHandlerCallback { public Object getResult(ResultGetter getter) throws SQLException { int value = getter.getInt(); if (getter.wasNull()) { return null; } Color color = Color.getInstance(value); return color; } public void setParameter(ParameterSetter setter, Object parameter) throws SQLException { if (parameter == null) { setter.setNull(Types.INTEGER); } else { Color color = (Color) parameter; setter.setInt(color.getId()); } } public Object valueOf(String s) { return s; } }
You are almost there! All you need left to do is tell iBatis to map instances of Frequency object to this handler.
I added the following line into my SqlMapConfig file.
<typeHandler javaType="domain.Shape$Color" callback="dao.ibatis.ColorTypeHandler"/>
![]() |
Useful Information Notice the $ in the class name. This is how Java 1.5 denotes enums. If this were a type safe enum you would just use standard notation. |
That is it. No need to change any of your other sqlmap entries. Here is an example of a select and insert that use this handler.
<resultMap class="Shape" id="ShapeResult"> <result column="name" property="name" /> <result column="color" property="color" /> </resultMap> <select id="getShapeByName" parameterClass="string" resultMap="ShapeResult"> SELECT * FROM SHAPE WHERE name = #value# </select> <insert id="insertReport" parameterClass="Report"> INSERT INTO SHAPE ( name, color ) values ( #name#, #color#, ) </insert>
Notice that there is nothing special that need to be done for the color columns.
因ؓiBatis用PreparedStatement来映statementQ你必须?value$语法Q或者确保你的参数包含你? 或_字符。例如,假设你项执行下面的语句:
select * from foo where value like 'x%'
You can do this:
你可以这么写sqlmap语句Q?br />
select * from foo where value like #parm#
But if you do, it becomes this:
但是如果你这么做Q他按照下面语句执?br /> select * from foo where value like ?
To make that do what you want, you need to make the parameter "x%" by setting parm to "x%".
Z保证你的目标Q你需要把"x%"作ؓ参数倹{?br />
If you do not like that approach, you can do this instead:
如果你不惌么做Q你可以用下面的取代Q?/p>
select * from foo where value like '$parm$%'
That still uses a PreparedStatement, but the $parm$ gets inserted as a literal instead of a parameter. So, to get the same results as before, you would set parm to "x". Note that this can be vulnerable to SQL injection attacks, so make sure that all single quotes are escaped in parm.
q仍然用PreparedStatementQ但?parm$用parameter作ؓ占位W,所以,Z得到l果Q你应该讄参数为x?br />
注意q可能引起SQL注入dQ所以确保所有参数里的单引号被过滤到
如果你项用SQL Maps2.0Q那么下面就是你要作?br />
1. Ensure you've specified a parameterMap or parameterClass attribute
on your statement.
1,保在在你的statement里描qCparameterMap或parameterClass
2. Use an explicitly defined <resultMap> and DON'T allow remapping of
results (new 2.0.9 feature)
2Q用一个显C的定义<resultMap>q且不要允许重映一个结果?br />
3. Use a Java``Bean (not a Map or XML etc.)
3Q用JavaBeanQ不要用Map或xml{?/p>
4. Make sure all of your properties are "simple properties" (no
dot.notation, array[] access or mixed bean/map properties).
4Q确保所有你的属性是单属性,不是点号格开Q数l下表访问或混合bean、map属性?br />
5. Try enabling bytecode enhancement in your <settings> element.
5Q在<setting>元素打开bytcode enhancement
iBatis用commons-logging来记录这些日志?br />
Depending on your logging implementation, your configuration may vary.
依赖于你的日志实玎ͼ你的配置可能改变?br />
If you are using log4j, put this line into log4j.properties:
如果你用log4jQ把下面q行攑օlog4j.properties:
log4j.logger.java.sql=DEBUG
If you set your default log level to DEBUG, that will work, too...but you will get debug level logging for everything in the world.
如果你设|默认的日志U别为DEBUGQ他工作,但是你获得全世界每个debugU别的日志?br />
Here is another example of how to configure ibatis with log4j using an xml config file.
下面是另一个例子,怎么Lxml格式的log4j配置ibatis
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <!-- Console output --> <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss} %m (%F:%L) \n"/> </layout> </appender> <category name="com.ibatis"> <priority value="debug" /> </category> <category name="java.sql"> <priority value="debug" /> </category> <!--The root category defines the top level of appenders all catagories inherit --> <root> <priority value ="error" /> <appender-ref ref="STDOUT" /> </root> </log4j:configuration>
A common question is "Is it possible to show the ACTUAL query being executed?".
一个更通常的问题:有可能展C某个真正的查询被执行了吗?
The simple answer is NO.
单得回答Q没?/p>
Why? Because the only information we have available is the PreparedStatement and the parameters.
Z么?因ؓ我们只能获得preparestatement和参?br />
Quite often, in the process of applying those parameters to the mapped statement it will become very clear where issues lie in the execution of the query.
更经常,在把参数匚w到statementq个q程中,很明显能看出查询q程哪里有问题?br />
If you feel that you NEED to see the completed query, you may be interested in hooking up a debugging tool like p6spy, as it may answer your questions for you.
如果你项看到完整得查询,你最好用p6spyq样得debug工具?/p>
For WebSphere-specific instructions see also How do I Configure Logging in WebSphere.
Websphare规范介参考W@!#%@%RQEWRWQR
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE sqlMap PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN" "http://ibatis.apache.org/dtd/sql-map-2.dtd"> <sqlMap namespace="Calendar"> <resultMap id="quarterMap" class="calendarQuarter" groupBy="quarter"> <result property="quarter" column="quarter"/> <result property="name" column="name"/> <result property="description" column="description"/> <result property="months" resultMap="Calendar.monthMap"/> </resultMap> <resultMap id="monthMap" class="calendarMonth"> <result property="name" column="monthName"/> <result property="description" column="monthDescription"/> <result property="broadcastStartDate" column="broadcastStartDate"/> <result property="broadcastEndDate" column="broadcastEndDate"/> </resultMap> <select id="getQuartersForServiceYear" resultMap="quarterMap"> select distinct QuarterNumber as quarter, QuarterName as name, QuarterDesc as description, SeasonYear as year, MonthName as monthName, MonthDesc as monthDescription, min(broadcastDate) as broadcastStartDate, max(broadcastDate) as broadcastEndDate from BroadcastDate where SeasonYear = #year# and MonthName is not null group by QuarterDesc, QuarterNumber, QuarterName, SeasonYear, MonthName, MonthDesc order by broadcastStartDate </select> </sqlMap>
When you call
接着你可以调?br />
List myList = executeQueryForList("Calendar.getQuartersForServiceYear", 2005);
L询被执行Qƈ且在myList里存储calendarQuarter为别名的对象。在List里的每个“months”属性里q有一个初始化的子列表Q这个子列表的数据也来自q次查询。但是用monthMapl果map来渲染子列表。所以,你得C一个含有子列表的列表,q且只有一ơ数据库查询被执行?br />
The important items here are the
重要的项在groupby的属性和months属性?br />
groupBy
<result property="months" resultMap="Calendar.monthMap"/>
另一个需要注意的是month属性的l果映射名是命名I间敏感的-如果配置?#8220;monthMap”,他将不能工作?br />
Summary: You have a single query that will return results such as
ȝQ你有一个简单的查询Q他q回下面q样的结?br />
parent1, child1 parent1, child2 parent2, child1 parent3, child1 parent3, child2 parent3, child3 ....
The groupby will take care of figuring out that you really want a list of parent objects with their matching child objects as a list under them.
q个groupby处理你惛_到的父对象组成的列表和相应的在父对象之下的子对象l成的列表?br />
q在用户指南里提到过Q但是既然你昄没有看过Q这里复qC遍?br />
Stored procedures are supported via the <procedure> statement element. The following example shows how a stored procedure would be used with output parameters.
存储q程通过<procedure>元素来支持,下面的例子展C怎么样通过output参数来显C怎么使用存储q程?/p>
<parameterMap id="swapParameters" class="map" > <parameter property="email1" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/> <parameter property="email2" jdbcType="VARCHAR" javaType="java.lang.String" mode="INOUT"/> </parameterMap> <procedure id="swapEmailAddresses" parameterMap="swapParameters" > {call swap_email_address (?, ?)} </procedure>
Calling the above procedure would swap two email addresses between two columns (database table) and also in the parameter object (Map). The parameter object is only modified if the parameter mappings mode attribute is set to ?INOUT? or ?OUT?. Otherwise they are left unchanged. Obviously immutable parameter objects (e.g. String) cannot be modified.
调用上面的存储过E将交换表的两列和输入参数的map里的两个倹{参数对象只有在讄位INOUT或OUTӞ调换元素位置。否则他们保持不动,昄不便对象String时不能够改动的?br />
Note! Always be sure to use the standard JDBC stored procedure syntax. See the JDBC CallableStatement documentation for more information.
注意QL保使用标准的JDBC存储q程语法。看JDBC的CallableStatement文获取更多内容?/p>
我应该用哪些SqlMapClientҎQ?/p>
It depends. Here is some help...
那样看情况了Q下面这些帮你作出选择...
If your procedure returns a result set (not a result set in an OUT parameter, but a result set from the procedure itself), then use queryForList() or queryForObject(). Use queryForList() if you expect more than one result object, or queryForObject() if you expect only one result Object.
如果存储q程q回一个Result SetQ不是一个OUT参数Q而是来自存储q程自n的Result SetQ,那么用queryForList()或者queryForObject().
当有多个l果对象Ӟ用queryForList(),否则如果有一个结果对象,使用QueryForObject().
If your procedure returns a result set AND updates data, then you should also configure your <transactionManager> with the attribute commitRequired="true".
如果你的存储q程q回一个结果,q且更新了数据,那么你应该还要配|?lt;transactionManager>Q设|commitRequired="true"
If your procedure does not return result sets, or only returns result sets in OUT parameters, then use the update() method.
如果你的存储q程没有q回l果集,而是仅仅从OUT 参数q回l果集,那么用update()Ҏ?/p>
JPetshop5到IBatisQSpring的{?/span>
Prerequisite is to have a project similar to jpetstore5, and have it connected to a 'normal' database
There is support for hssqldb in the DaoConfig class what we are going to give up (for now).
更新jar文g
Remove the old iBATIS jar files from the class path and create the reference to the new iBATIS and Spring jars:
删除旧的ibatis JAR文gQ添加到新的ibatis和spring JAR文g
改变代码
First, we need to extend the Spring support classes:
首先Q我们需要扩展spring的支持类
public class BaseSqlMapDao extends SqlMapClientTemplate { protected static final int PAGE_SIZE = 4; }
Next, we can remove the old constructors (that have the DaoManager parameter), as well as the import for the DaoManager from all of the other SqlMapDao files.
下一步,我们删除旧的构造器(有DaoManager参数)Q在所有的sqlMapDao文g里删除对DaoManager的引入?/span>
We also need to remove the default constructors from the service classes in the com.ibatis.jpetstore.service package, because the DAO will be injected by spring.
我们q需要从com.ibatis.jpetstore.service包的服务c里删除默认的构造器Q因为DAO被Spring注入?/span>
处理事务
There is some code to work with transactions in OrderService.java which we'll need to handle differently, but for the moment, we'll remove it here, and remove the DaoManager from the constructor.
在OrderService里,有一些与事务有关的代码,q个OrderService我们需要不同的处理Q但是目前,我们删除他,q且删除从构造器里删除DaoManager?br />
public OrderService(ItemDao itemDao, OrderDao orderDao, SequenceDao sequenceDao) { this.itemDao = itemDao; this.orderDao = orderDao; this.sequenceDao = sequenceDao; }
链接Spring和iBatis
We'll add the SpringInit.java class:
我们增加SpringInit.javac?/span>
public class SpringInit implements ServletContextListener { private static WebApplicationContext springContext; public void contextInitialized(ServletContextEvent event) { springContext = WebApplicationContextUtils.getWebApplicationContext(event.getServletContext()); } public void contextDestroyed(ServletContextEvent event) { springContext = null; } public static ApplicationContext getApplicationContext() { return springContext; } }
This class will be instantiated when the website starts, and will contain a reference to the spring managed objects.
当网站启动时Q这个类被实例化Qƈ前包含了一个对spring理的对象的引用?/span>
The presentation layer must connect with the spring managed service layer, so remove the default constructors because we'll be pulling the services from Spring instead.
展示层必Mspring理的服务层的关联,所以必d除默认的构造器Q因为我们从spring拉服务?br />
public AccountBean() { this(new AccountService(), new CatalogService()); }
Would be replaced with:
上面代码用下面的代码替换Q?/span>
public AccountBean() { this( (AccountService) SpringInit.getApplicationContext().getBean("accountService"), (CatalogService) SpringInit.getApplicationContext().getBean("catalogService") ); }
This would be repeated for all of the other classes in the presentation package.
在展C层的其他类里也需要做相同的处理?br />
配置
First setup the listeners to have SpringInit initiated on loading of the site
W一步,建立一个listener让SpringInit在站点启动时初始?/span>
<context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <listener> <listener-class>com.listeners.SpringInit</listener-class> </listener>
This tells your context to look for /WEB-INF/spring.xml - this is the one created for jpetstore5:
q个告诉你的上下文去/web-inf/spring.xml里查找-q个时ؓjpetstore5创徏的文件?/span>
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:properties/database.properties"/> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="${driver}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </bean> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation"> <value>classpath:com/ibatis/jpetstore/persistence/sqlmapdao/sql/sql-map-config.xml</value> </property> <property name="useTransactionAwareDataSource"> <value>true</value> </property> <property name="dataSource"> <ref bean="dataSource"/> </property> </bean> <bean id="sqlMapClientTemplate" class="org.springframework.orm.ibatis.SqlMapClientTemplate"> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> </property> </bean> <bean id="accountDao" class="com.ibatis.jpetstore.persistence.sqlmapdao.AccountSqlMapDao"> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> </property> </bean> <bean id="categoryDao" class="com.ibatis.jpetstore.persistence.sqlmapdao.CategorySqlMapDao"> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> </property> </bean> <bean id="itemDao" class="com.ibatis.jpetstore.persistence.sqlmapdao.ItemSqlMapDao"> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> </property> </bean> <bean id="orderDao" class="com.ibatis.jpetstore.persistence.sqlmapdao.OrderSqlMapDao"> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> </property> </bean> <bean id="productDao" class="com.ibatis.jpetstore.persistence.sqlmapdao.ProductSqlMapDao"> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> </property> </bean> <bean id="sequenceDao" class="com.ibatis.jpetstore.persistence.sqlmapdao.SequenceSqlMapDao"> <property name="sqlMapClient"> <ref bean="sqlMapClient"/> </property> </bean> <bean id="accountService" class="com.ibatis.jpetstore.service.AccountService"> <constructor-arg index="0" ref="accountDao"/> </bean> <bean id="catalogService" class="com.ibatis.jpetstore.service.CatalogService"> <constructor-arg index="0" ref="categoryDao"/> <constructor-arg index="1" ref="itemDao"/> <constructor-arg index="2" ref="productDao"/> </bean> <bean id="orderService" class="com.ibatis.jpetstore.service.OrderService"> <constructor-arg index="0" ref="itemDao"/> <constructor-arg index="1" ref="orderDao"/> <constructor-arg index="2" ref="sequenceDao"/> </bean> </beans>
清扫
Remove the DaoConfig.java class and the dao.xml file from the com.ibatis.jpetstore.persistence package.
从com.ibatis.jpetstore.persistence包删除DaoConfig.javacddao.xml文g?/span>
ȝ
This is still a work in progress, so please feel free to add comments and suggestions for making it better.
q个个工作还在进行中Q所以请随意d备注和徏议,使得他更完善?/span>
DAO is an abstraction layer that sits between your persistence solution and your service/domain layer. It serves to maintain a consistent API and transaction management facility to all of the higher layers (like service and domain). Without it, you will end up with various different types of artifacts from your persistence solution (like Session, or Connection) all mixed together. You'll also find yourself more tied to your persistence solution. One point about DAO – don't be afraid to write your own DAO! iBATIS is one implementation, but of anything else in iBATIS, DAO is the part that's is often best written for your specific application and customized to your needs.
DAO是处于持久化Ҏ和服务或领域层之间的抽象层。他l护一致的API和事务管理。没有他Q你需要ؓ持久层方案篏U各U不同类型的东西Q例如Session和Connection。你同时发现处理你的持久化Ҏ很类。DAO的好处就是,不必担心写你的DAO?br />
IBatis是一个具体实玎ͼ但是iBatis之外QDAO是ؓ你的特定的应用和定制你的需要而准备的最好的东西?/span>
SQL Maps is a persistence solution that allows you to easily map SQL to java objects. It is NOT an object-relational mapping tool, but can be used to map tables to objects using SQL.
SQL Maps是一个持久化ҎQ允怽Ҏ的实现sql到对象的映射。他不是一个对象关pL工P但是可以通过sql把表格映到对象集?/span>