달력

4

« 2024/4 »

  • 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
2006. 10. 30. 19:49

Understanding J2EE Class Loading Java2006. 10. 30. 19:49

Java Class Loading Concepts

The following two basic principles will always apply:

o Each class retains an association with the class loader that loaded it. The getClassLoader() method of java.lang.Class returns the class loader that loaded the class, which cannot be changed after the class is loaded. This is the class loader that will be used if the class attempts to load classes by name.

o If the same class is loaded by two class loaders, classes loaded by the two class loaders will not be type compatible (although serialization will work).

The documentation of the java.lang.ClassLoader class further defines the following behavior for class loaders:

o Class loaders are hierarchical.
When a class loader is asked to load a class, it first asks its parent class loader to try to load the class. Only if the parent (and parent's ancestors) cannot load the class, will the original classloader attempt to load the class. The top of the class loader hierarchy is the bootstrap loader built into the JVM, which loads java.lang.Object().

o Although a class loader can see classes loaded by its parent(s), it cannot see classes loaded by its children.

Class Loading in J2EE

J2EE servers use multiple class loaders, largely because this allows dynamic application reloading. Clearly we don't want to reload all the application server's own classes on redeploying an application. This would mean that the application server would always need to be restarted. So application servers use different class loaders for application code to those they use for their own standard libraries, for example. Typically one or more new class loaders will be created for each application deployed on a server.

However, multiple class loaders are not usually used for different application-specific classes in the same application unless we use EJB (JSP pages may be given a separate class loader, but this doesn't usually affect application code).

In an EAR, the EJB class loader is often the parent of the WAR class loader. Orion and WebLogic, for example, both use this approach. This is a natural implementation approach, as WARs will typically access EJBs (and therefore need to be able to see at least EJB client classes), while EJBs do not access web components.

In this diagram the class loader hierarchy is represented by enclosing boxes. The parent-child relationship is represented by an enclosing box:

Assuming standard J2SE hierarchical class loading behavior, such a hierarchy will mean that any class can access classes in boxes that enclose its class loader. However, classes associated with the outer boxes cannot load classes in the inner boxes. Thus web application classes can see classes deployed in the application's EJBs and system classes. However, EJB classes cannot see web application classes, and classes installed at server-wide level cannot see any application-specific classes.

Class Loading in Integrated Enterprise Applications

Thus there are two basic problems relating to J2EE class loading, in the common case where EJB and web modules are included in the same enterprise application:

o Where do we hold the definitions of classes used in both EJBs and web applications?

o How do we ensure that two class loaders don't end up holding independent versions of the same class, resulting in class cast exceptions?

The Servlet 2.3 Specification's Class Loading Recommendations

The Servlet 2.3 specification (9.7.2) states that "It is recommended also that the application class loader be implemented so that classes and resources packaged within the WAR are loaded in preference to classes and resources residing in container-wide library JARs".

This clearly conflicts with the standard J2SE class loading behavior, as described in the Javadoc for the java.lang.ClassLoader class. As the WAR class loader must be a dynamic class loader, it must be the child of another class loader provided by the application server. Hence the Servlet 2.3 recommendation is the opposite of normal Java 2 class loading behavior, which clearly states that classes will be loaded from the child class loader (in this case the WAR class loader) only if they cannot be resolved by the ancestor class loaders.

The Java 1.3 Extension Mechanism Architecture in J2EE

Changes in J2SE 1.3 make it possible for JAR files to specify dependencies on other JAR files, by specifying a space-separated list of relative file paths in a Class-Path header in their /META-INF/MANIFEST.MF file.

Class-Path: Iog4j-1.2.jar 121-core.jar 121-ejbimpl.jar 121-jdbc.jar

This declares that the application-specific classes in the ticket-ejb.jar file depend on four infrastructure JARs, meaning that the EJBJAR file doesn't need to include any third party classes. These paths are relative. All these JAR files are included with the EJBJAR file in the root directly of the application EAR, as the following listing of the EAR's contents shows:

       META-INF/
       META-INF/MANIFEST.MF
       META-INF/application.xml
       121-core.jar
       i21-ejbimpl. jar
       121-jdbc.jar
       ticket-ejb.jar
       ticket.war
       Iog4j-1.2.jar

how to enable WAR manifest classpaths on Oracle 9iAS Release 2.

IBM documentation, WebSphere 4.0 supports manifest classpaths for WAR filesM

above links recommends using this when WARs and EJBs reference the same classes.

Orion and Oracle will also load manifest classpaths in EARs by default.

JBoss/Jetty does not appear to respect manifest classpaths in WARs
I haven't relied on this non-portable feature in packaging the sample application. The J2EE Reference Implementation also ignores manifest classpaths in WARs.

BEA discussing the use of manifest classpath

Thread Context Class Loader

getContextClassLoader() method on java.util.Thread.

o Class.forName (classname):
Will use the class loader of the helper class: in this case, the EJB class loader. This means that, if the EJB class loader is the parent of the WAR class loader, the helper will never be able to load classes in the WAR by name.

o Class.forName(classname, true, Thread.currentThread().getContextClassLoader()):
Will use the current container's class loader. This means that the helper will behave differently wherever it is running. If the EJB class loader is the parent of the WAR class loader, when the helper is used in the EJB container, it will only be able to load EJB classes and classes loaded by higher class loaders. If the helper is used in the WAR, it will be able to load WAR classes as well.

:
Posted by codetemplate
2006. 10. 30. 19:47

iBATIS 2.0 Developer's Guide 정리 2 프레임워크2006. 10. 30. 19:47

4. Result Maps
   4.1 Implicit Result Maps
   4.2 Primitive Results(i.e. String, Integer, Boolean)
   4.3 Map Results
   4.4 Complex Properties(i.e. a property of a class defined by the user)
   4.5 Avoiding N+1 Selects (1:1)
   4.6 Complex Collection Properties(i.e. a property of a class defined by the user)
   4.7 Avoiding N+1 Selects (1:M and M:N)
   4.8 Complex Collection Properties
   4.9 Avoiding N+1 Selects(1:M and M:N)
   4.10 Composite Keys or Multiple Complex Parameters Properties
5. Supported Types for Parameter Maps and Result Maps
6. Caching Mapped Statement Results
   6.1 Read-Only vs Read/Write
   6.2 Serializable Read/Write Caches
   6.3 Cache Types
7. Dynamic Mapped Statements
   7.1 Binary Conditional Elements
   7.2 Unary Conditional Elements
   7.3 Other Elements
8. Simple Dynamic SQL Elements
9. Programming with Data Mapper: The API
   9.1 Configuration
   9.2 Transactions
   9.3 Batches
   9.4 Executing Statements via the SqlMapClient API
10. Logging SqlMap Activity with Jakarta Commons Logging
11. The One Page JavaBeans Course
12. Resources (comibatiscommonresources*)
13. SimpleDataSource (comibatiscommonjdbc*)

4.1 Implicit Result Maps

재사용이 필요 없는 경우 명시적으로 resultMap을 선언하지 않고도 원하는 결과를 얻을 수 있다.

<statement id="getProduct" resultClass="com.ibatis.example.Product">
   select
   PRD_ID as id,
   PRD_DESCRIPTION as description
   from PRODUCT
   where PRD_ID = #value#
</statement>

위의 예에서 resultClass를 정의하고 자바 빈의 프로퍼티와 맵칭되는 alias를 사용했다. 이러한 경우 resultMap이 없어도 된다.

명시적인 resultMap의 사용과 위와 같은 묵시적 result map 간에는 tradeoff가 존재한다. 묵시적인 result map 사용시 column type, null value를 명시할 수 없다. 또 resultClass를 통한 자동 맵핑 사용시 성능 오버헤드가 따른다.

4.2 Primitive Results(i.e. String, Integer, Boolean)

primitive type은 오직 하나의 프로퍼티만 가질 수 있다. 그리고 그 프로퍼티의 이름은 임의로 정의할 수 있다.

<resultMap id="get-product-result" class="java.lang.String">
   <result property="value" column="PRD_DESCRIPTION"/>
</resultMap>

위의 예보다 간단한 방법은 mapped statement에서 아래와 같이 resultClass를 사용하는 방법이다(이때 as를 사용하여 value라는 컬럼 alias를 사용한 점에 유의하기 바란다).

<statement id="getProductCount" resultClass="java.lang.Integer">
   select count(1) as value
   from PRODUCT
</statement>

4.3 Map Results

Result map은 HashMap, TreeMap과 같은 Map 인스턴스 쉽게 채울 수 있다. 이러한 객체들의 collection(Lis of Map 등)도 API(executeQueryForList 등)를 사용하여 검색할 수 있다.

<resultMap id="get-product-result" class="java.util.HashMap">
   <result property="id" column="PRD_ID"/>
   <result property="code" column="PRD_CODE"/>
   <result property="description" column="PRD_DESCRIPTION"/>
   <result property="suggestedPrice" column="PRD_SUGGESTED_PRICE"/>
</resultMap>

또 아래와 같이 Map 타입에 대한 묵시적 result map을 사용할 수도 있다.

<statement id="getProductCount" resultClass="java.util.HashMap">
   select * from PRODUCT
</statement>

4.4 Complex Properties(i.e. a property of a class defined by the user)

<resultMap id="get-product-result" class="com.ibatis.example.Product">
   <result property="id" column="PRD_ID"/>
   <result property="description" column="PRD_DESCRIPTION"/>
  <result property="category" column="PRD_CAT_ID" select="getCategory"/>
</resultMap>

<resultMap id="get-category-result" class="com.ibatis.example.Category">
   <result property="id" column="CAT_ID"/>
   <result property="description" column="CAT_DESCRIPTION"/>
</resultMap>

<statement id="getProduct" parameterClass="int" resultMap="get-product-result">
   select * from PRODUCT where PRD_ID = #value#
</statement>

<statement id="getCategory" parameterClass="int" resultMap="get-category-result">
   select * from CATEGORY where CAT_ID = #value#
</statement>

4.5 Avoiding N+1 Selects (1:1)

4.4와 같은 방법 사용시 N+1 Select 문제(Product을 읽기 위해 1번, CATEGORY를 읽기 위해 N개의 Product에 대해 N번 읽기)가 발생한다.

이러한 문제를 해결하기 위해 JOIN을 사용한다.

<resultMap id="get-product-result" class="com.ibatis.example.Product">
   <result property="id" column="PRD_ID"/>
   <result property="description" column="PRD_DESCRIPTION"/>
   <result property="category.id" column="CAT_ID" />
   <result property="category.description" column="CAT_DESCRIPTION" />

</resultMap>

<statement id="getProduct" parameterClass="int" resultMap="get-product-result">
   select *
   from PRODUCT, CATEGORY
   where PRD_CAT_ID=CAT_ID
   and PRD_ID = #value#
</statement>

4.6 Complex Collection Properties(i.e. a property of a class defined by the user)

<resultMap id="get-category-result" class="com.ibatis.example.Category">
   <result property="id" column="CAT_ID"/>
   <result property="description" column="CAT_DESCRIPTION"/>
   <result property="productList" column="CAT_ID" select=" getProductsByCatId"/>
</resultMap>

<resultMap id="get-product-result" class="com.ibatis.example.Product">
   <result property="id" column="PRD_ID"/>
   <result property="description" column="PRD_DESCRIPTION"/>
</resultMap>

<statement id="getCategory" parameterClass="int" resultMap="get-category-result">
   select * from CATEGORY where CAT_ID = #value#
</statement>

<statement id="getProductsByCatId" parameterClass="int" resultMap="get-product-result">
   select * from PRODUCT where PRD_CAT_ID = #value#
</statement>

4.7 Avoiding N+1 Selects (1:M and M:N)

4.6의 방법은 Product을 로딩할 때 마다 2개의 SQL 문장(Product을 위한 SQL 문장과 CATEGORY를 위한 문장)이 수행된다는 문제를 갖는다. 따라서 N+1 문제를 동일하게 갖는다.

이 문제를 해결하기 위해 JOIN과 NESTED PROPERTY MAPPING을 사용한다.

<resultMap id="get-product-result" class="com.ibatis.example.Product">
   <result property="id" column="PRD_ID"/>
   <result property="description" column="PRD_DESCRIPTION"/>
   <result property="category.id" column="CAT_ID" />
   <result property="category.description" column="CAT_DESCRIPTION" />

</resultMap>

<statement id="getProduct" parameterClass="int" resultMap="get-product-result">
   select *
   from PRODUCT, CATEGORY
   where PRD_CAT_ID=CAT_ID
   and PRD_ID = #value#
</statement>

JOIN을 사용하는 것이 항상 좋은 것은 아니다. JOIN으로 얻어 온 정보를 사용하지 않을 경우에는 JOIN 대신 lazy-loading을 적용하여 sub-select를 사용하는 것이 좋다.

4.8 Complex Collection Properties

<resultMap id="get-category-result" class="com.ibatis.example.Category">
   <result property="id" column="CAT_ID"/>
   <result property="description" column="CAT_DESCRIPTION"/>
   <result property="productList" column="CAT_ID" select=" getProductsByCatId"/>
</resultMap>

<resultMap id="get-product-result" class="com.ibatis.example.Product">
   <result property="id" column="PRD_ID"/>
   <result property="description" column="PRD_DESCRIPTION"/>
</resultMap>

<statement id="getCategory" parameterClass="int" resultMap="get-category-result">
   select * from CATEGORY where CAT_ID = #value#
</statement>

<statement id="getProductsByCatId" parameterClass="int" resultMap="get-product-result">
   select * from PRODUCT where PRD_CAT_ID = #value#
</statement>

4.9 Avoiding N+1 Selects(1:M and M:N)

<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>

4.10 Composite Keys or Multiple Complex Parameters Properties

위의 예에서 resultMap의 column 속성에 하나의 키 값만 사용된 것을 알 수 있다. 아래 예는 관련된 mapped statement에 다수의 컬럼을 전달하는 방법을 보이고 있다.

<resultMap id="get-order-result" class="com.ibatis.example.Order">
   <result property="id" column="ORD_ID"/>
   <result property="customerId" column="ORD_CST_ID"/>
   …
   <result property="payments" column="{itemId=ORD_ID, custId=ORD_CST_ID}"
       select=" getOrderPayments"/>
</resultMap>

<statement id="getOrderPayments" resultMap="get-payment-result">
   select * from PAYMENT
   where PAY_ORD_ID = #itemId#
   and PAY_CST_ID = #custId#
</statement>

5. Supported Types for Parameter Maps and Result Maps

iBATIS 프레임워크가 parameter/result로 지원하는 자바 타입은 아래와 같다.

Java TypeJavaBean/Map
Property Mapping
Result Class /
Parameter Class***
Type Alias**
booleanYESNOboolean
java.lang.Boolean
byte
YES
YES
YES
NO
boolean
byte
java.lang.ByteYESYESbyte
short
java.lang.Short
YES
YES
NO
YES
short
short
intYESNOint/integer
java.lang.IntegerYESYESint/integer
longYESNOlong
java.lang.LongYESYESlong
floatYESNOfloat
java.lang.FloatYESYESfloat
doubleYESNOdouble
java.lang.DoubleYESYESdouble
java.lang.String
java.util.Date
YES
YES
YES
YES
string
date
java.math.BigDecimal
* java.sql.Date
YES
YES
YES
YES
decimal
N/A

* java.sql.Time
YESYESN/A
* java.sql.TimestampYESYESN/A

* The use of java.sql. date types is discouraged.

6. Caching Mapped Statement Results

statement 태그에 cacheModel을 명시함으로써 Query Mapped Statement의 결과는 캐쉬될 수 있다.

<cacheModel id="product-cache" type ="LRU" readOnly="true" serialize="false">
   <flushInterval hours="24"/>
   <flushOnExecute statement="insertProduct"/>
   <flushOnExecute statement="updateProduct"/>
   <flushOnExecute statement="deleteProduct"/>
   <property name="cache-size" value="1000" />
</cacheModel>

flushinterval에는 hours, minutes, seconds, milliseconds 등을 사용할 수 있다.

<statement id="getProductList" cacheModel="product-cache">
   select * from PRODUCT where PRD_CAT_ID = #value#
</statement>

6.1 Read-Only vs Read/Write

iBATIS는 read-only, read-write 캐쉬를 모두 지원한다. read-only 캐쉬는 사용자 간에 공유되어 눈에 뛸 만한 성능 향상을 제공한다. 하지만 read-only 캐쉬에서 얻어진 객체는 변경되어서는 안된다. 변경을 하기 위해서는 새로운 객체를 데이터베이스에서 읽어야 한다.

6.2 Serializable Read/Write Caches

6.1에서 설명한 세션별 read-write 캐쉬는 전체적인 애플리케이션의 성능 향상에는 많은 도움이 되지 않는다. 전체적인 애플리케이션의 성능에 보다 도움이 되는 캐쉬 방법으로 serializable read-write 캐쉬가 있다. serializable read-write 캐쉬는 각 세션에 캐쉬된 객체에 대한 다른 인스턴스(복사본)를 제공한다. 그래서 각 세션은 안전하게 인스턴스를 변경할 수 있다. serializable read-write 캐쉬를 사용할 때 캐쉬될 객체들은 serializable해야 한다. 이로 인해 lazy loading과 serializable read-write 캐쉬가 함께 사용되면 lazy proxy가 serializable하지 않기 때문에 문제가 발생한다. 따라서 serializable read-write 캐쉬를 사용할 경우 lazy load 기능을 사용하지 말아야 한다.

6.3 Cache Types

CacheController 인터페이스를 지원하는 여러가지 캐쉬가 존재한다.

"Memory"(com.ibatis.db.sqlmap.cache.memory.MemoryCacheController)

캐쉬의 행위를 관리하기 위해 reference type을 사용한다. GC가 캐쉬에 남아 있어야 하는지 여부를 효과적으로 판단한다. 객체 재사용의 패턴이 인식 불가하거나 메모리가 부족한 경우 적합한 방법이다.

<cacheModel id="product-cache" type="MEMORY">
   <flushInterval hours="24"/>
   <flushOnExecute statement="insertProduct"/>
   <flushOnExecute statement="updateProduct"/>
   <flushOnExecute statement="deleteProduct"/>
   <property name="reference-type" value="WEAK" />
</cacheModel>

reference type은 아래와 같은 값을 가질 수 있다.

WEAK(default)

대부분의 경우에 적합한 방법

자주 참조된는 결과에는 우수한 성능을 제공하지만 다른 객체를 위해 메모리를 할당하기 위해 메모리가 더 이상 사용되지 않는다고 가정하고 메모리를 릴리즈한다.

SOFT결과가 현재 사용되지 않고 있고 다른 객체에 의해 필요한 경우 메모리가 부족한 경우를 최소화한다.
STRONG

캐쉬가 명시적으로 flush되기 전에는 메모리에 유지한다. 다음과 같은 경우에 이상적이다.

1. 메모리가 아주 적게 사용된다.

2. 상대적으로 정적인 경우

3. 아주 자주 사용된다.

위와 같은 경우 성능 향상에 크게 도움이 된다. 하지만 메모리 부족이 발생할 수 있다.

"LRU"(com.ibatis.db.sqlmap.cache.lru.LruCacheController)

객체를 캐쉬에서 자동으로 제거하기 위해 LRU 알고리즘을 사용한다.

<cacheModel id="product-cache" type="LRU">
   <flushInterval hours="24"/>
   <flushOnExecute statement="insertProduct"/>
   <flushOnExecute statement="updateProduct"/>
   <flushOnExecute statement="deleteProduct"/>
   <property name="size" value="1000" />
</cacheModel>

"FIFO"(com.ibatis.db.sqlmap.cache.fifo.FifoCacheController)

캐쉬가 가득 차게 되면 가장 오래된 객체가 캐쉬에서 제거된다.

<cacheModel id="product-cache" type="FIFO">
   <flushInterval hours="24"/>
   <flushOnExecute statement="insertProduct"/>
   <flushOnExecute statement="updateProduct"/>
   <flushOnExecute statement="deleteProduct"/>
   <property name=”size” value=”1000” />
</cacheModel>

"OSCACHE"(com.ibatis.db.sqlmap.cache.oscache.OSCacheController)

highly configurable, distributed and flexible

<cacheModel id="product-cache" type="OSCACHE">
   <flushInterval hours="24"/>
   <flushOnExecute statement="insertProduct"/>
   <flushOnExecute statement="updateProduct"/>
   <flushOnExecute statement="deleteProduct"/>
</cacheModel>

루트 클래스 패스에 존재하는 oscache.properties를 통해 캐쉬를 설정

http://www.opensymphony.com/oscache 참조

7. Dynamic Mapped Statements

<select id="dynamicGetAccountList"
       cacheModel="account-cache"
       resultMap="account-result" >
   select * from ACCOUNT
       <isGreaterThan prepend="and" property="id" compareValue="0">
           where ACC_ID = #id#
       </isGreaterThan>
   order by ACC_LAST_NAME
</select>

<statement id="dynamicGetAccountList"
       resultMap="account-result" >
   select * from ACCOUNT
   <dynamic prepend="WHERE">
       <isNotNull prepend="AND" property="firstName">
           (ACC_FIRST_NAME = #firstName#
       <isNotNull prepend="OR" property="lastName">
           ACC_LAST_NAME = #lastName#
       </isNotNull>
           )
       </isNotNull>
       <isNotNull prepend="AND" property="emailAddress">
           ACC_EMAIL like #emailAddress#
       </isNotNull>
       <isGreaterThan prepend="AND" property="id" compareValue="0">
           ACC_ID = #id#
       </isGreaterThan>
   </dynamic>
   order by ACC_LAST_NAME
</statement>

<statement id="someName"
       resultMap="account-result" >
   select * from ACCOUNT
   <dynamic prepend="where">
       <isGreaterThan prepend="and" property="id" compareValue="0">

           ACC_ID = #id#
       </isGreaterThan>
       <isNotNull prepend=”and" property="lastName">

           ACC_LAST_NAME = #lastName#
       </isNotNull>
   </dynamic>

   order by ACC_LAST_NAME
</statement>

7.1 Binary Conditional Elements

binary conditional attributes는 아래와 같은 요소를 갖는다.

prepend - the overridable SQL part that will be prepended to the statement (optional)
property - the property to be compared (required)
compareProperty - the other property to be compared (required or compareValue)
compareValue - the value to be compared (required or compareProperty)

<isEqual>Checks the equality of a property and a value, or another property.
<isNotEqual>Checks the inequality of a property and a value, or another property.
<isGreaterThan>Checks if a property is greater than a value or another property
<isGreaterEqual>Checks if a property is greater than or equal to a value or another property.
<isLessThan>Checks if a property is less than a value or another property.
<isLessEqual>Checks if a property is less than or equal to a value or another property.
Example Usage:
<isLessEqual prepend=”AND” property=”age” compareValue=”18”>
   ADOLESCENT = ‘TRUE’
</isLessEqual>

7.2 Unary Conditional Elements

아래와 같은 attributes를 갖는다.

prepend - the overridable SQL part that will be prepended to the statement (optional)
property - the property to be checked (required)

<isPropertyAvailable>Checks if a property is available (i.e is a property of the parameter bean)
<isNotPropertyAvailable>Checks if a property is unavailable (i.e not a property of the parameter bean)
<isNull>Checks if a property is null.
<isNotNull>Checks if a property is not null.
<isEmpty>Checks to see if the value of a Collection, String or String.valueOf() property
is null or empty (“” or size() < 1).
<isNotEmpty> Checks to see if the value of a Collection, String or String.valueOf() property
is not null and not empty (“” or size() < 1).
Example Usage:
<isNotEmpty prepend=”AND” property=”firstName” >
   FIRST_NAME=#firstName#
</isNotEmpty>

7.3 Other Elements

Parameter Present: These elements check for parameter object existence.
Parameter Present Attributes:
prepend - the overridable SQL part that will be prepended to the statement (optional)

<isParameterPresent>Checks to see if the parameter object is present (not null).
<isNotParameterPresent>Checks to see if the parameter object is not present (null).
Example Usage:
<isNotParameterPresent prepend=”AND”>
   EMPLOYEE_TYPE = ‘DEFAULT’
</isNotParameterPresent>

Iterate: This tag will iterate over a collection and repeat the body content for each item in a List
Iterate Attributes:
prepend - the overridable SQL part that will be prepended to the statement (optional)
property - a property of type java.util.List that is to be iterated over (required)
open - the string with which to open the entire block of iterations, useful for brackets (optional)
close - the string with which to close the entire block of iterations, useful for brackets (optional)
conjunction - the string to be applied in between each iteration, useful for AND and OR (optional)

<iterate>Iterates over a property that is of type java.util.List
Example Usage:
<iterate prepend=”AND” property=”userNameList”
   open=”(” close=”)” conjunction=”OR”>
   username=#userNameList[]#
</iterate>
Note: It is very important to include the square brackets[] at the end of the List
property name when using the Iterate element. These brackets distinguish this
object as an List to keep the parser from simply outputting the List as a string.

8. Simple Dynamic SQL Elements

full dynamic mapped statement가 강력하지만 때론 간단하고 작은 SQL이 dynamic일 필요가 생긴다.

<statement id="getProduct" resultMap="get-product-result">
   select * from PRODUCT order by $preferredOrder$
</statement>

<statement id="getProduct" resultMap="get-product-result">
   SELECT * FROM PRODUCT
   <dynamic prepend="WHERE">
       <isNotEmpty property="description">
           PRD_DESCRIPTION $operator$ #description#
       </isNotEmpty>
   </dynamic>
</statement>

9. Programming with Data Mapper: The API

SqlMapClient API는 아래와 같은 4가지 주요한 기능을 제공한다.

9.1 Configuration


9.2 Transactions


9.3 Batches

많은 수의 CUD operation이 있으면 이들을 배치로 실행함으로써 네트워크 부하를 최소화하고 JDBC 드라이버로 하여금 추가적인 최적화를 수행하도록 할 수 있다(eg. compression). 2개의 메소드를 사용하여 배치 블록을 지정함으로써 쉽게 배치를 구현할 수 있다.

       sqlMap.startBatch();
       //…execute statements in between
       sqlMap.executeBatch();

9.4 Executing Statements via the SqlMapClient API

아래와 같은 API가 제공된다.

public int insert(String statementName, Object parameterObject)
       throws SQLException
public int update(String statementName, Object parameterObject)
       throws SQLException
public int delete(String statementName, Object parameterObject)
       throws SQLException
public Object queryForObject(String statementName,
Object parameterObject)
       throws SQLException
public Object queryForObject(String statementName,
Object parameterObject, Object resultObject)
       throws SQLException
public List queryForList(String statementName, Object parameterObject)
       throws SQLException
public List queryForList(String statementName, Object parameterObject,
int skipResults, int maxResults)
       throws SQLException
public List queryForList (String statementName,
Object parameterObject, RowHandler rowHandler)
       throws SQLException
public PaginatedList queryForPaginatedList(String statementName,
Object parameterObject, int pageSize)
       throws SQLException
public Map queryForMap (String statementName, Object parameterObject,
String keyProperty)
       throws SQLException
public Map queryForMap (String statementName, Object parameterObject,
String keyProperty, String valueProperty)
       throws SQLException

10. Logging SqlMap Activity with Jakarta Commons Logging

log4j.properties

   # Global logging configuration
   log4j.rootLogger=ERROR, stdout
   # SqlMap logging configuration...
   #log4j.logger.com.ibatis=DEBUG
   #log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=DEBUG
   #log4j.logger.com.ibatis.common.jdbc.ScriptRunner=DEBUG
   #log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=DEBUG
   #log4j.logger.java.sql.Connection=DEBUG
   #log4j.logger.java.sql.Statement=DEBUG
   #log4j.logger.java.sql.PreparedStatement=DEBUG
   #log4j.logger.java.sql.ResultSet=DEBUG
   # Console output...
   log4j.appender.stdout=org.apache.log4j.ConsoleAppender
   log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
   log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n=

11. The One Page JavaBeans Course

12. Resources (comibatiscommonresources*)

13. SimpleDataSource (comibatiscommonjdbc*)

Property NameRequiredDefaultDescription
JDBC.DriverYesn/aThe usual JDBC driver class name.
JDBC.ConnectionURLYesn/aThe usual JDBC connection URL.
JDBC.UsernameYesn/a The username to log into the database.
JDBC.PasswordYesn/aThe password to log into the database.
JDBC.DefaultAutoCommitNodriver
dependant
The default autocommit setting for all
connections created by the pool.
Pool.MaximumActiveConnectionsNo10Maximum number of connections that can
be open at any given time.
Pool.MaximumIdleConnectionsNo 5The number of idle connections that will be
stored in the pool.
Pool.MaximumCheckoutTime No 20000The maximum length of time (milliseconds)
that a connection can be “checked out”
before it becomes a candidate for forced
collection.
Pool.TimeToWaitNo20000If a client is forced to wait for a connection
(because they are all in use), this is the
maximum length of time in (milliseconds)
that the thread will wait before making a
repeat attempt to acquire a connection. It is
entirely possible that within this time a
connection will be returned to the pool and
notify this thread. Hence, the thread may
not have to wait as long as this property
specifies (it is simply the maximum).
Pool.PingQueryNon/aThe ping query will be run against the
database to test the connection. In an
environment where connections are not
reliable, it is useful to use a ping query to
guarantee that the pool will always return a
good connection. However, this can have a
significant impact on performance. Take
care in configuring the ping query and be
sure to do a lot of testing.
Pool.PingEnabledNofalseEnable or disable ping query. For most
applications a ping query will not be
necessary.
Pool.PingConnectionsOlderThanNo0Connections that are older than the value
(milliseconds) of this property will be tested
using the ping query. This is useful if your
database environment commonly drops
connections after a period of time (e.g. 12
hours).
Pool.PingConnectionsNotUsedForNo0Connections that have been inactive for
longer than the value (milliseconds) of this
property will be tested using the ping query.
This is useful if your database environment
commonly drops connections after they have
been inactive for a period of time (e.g. after
12 hours of inactivity).
Driver.*NoN/AMany JDBC drivers support additional
features configured by sending extra
properties. To send such properties to your
JDBC driver, you can specify them by
prefixing them with “Driver.” and then the
name of the property. For example, if your
driver has a property called
“compressionEnabled”, then you can set it
in the SimpleDataSource properties by
setting “Driver.compressionEnabled=true”.
Note: These properties also work within the
dao.xml and sqlMap-config.xml files.
:
Posted by codetemplate
2006. 10. 30. 19:45

iBATIS 2.0 Developer's Guide 정리 1 프레임워크2006. 10. 30. 19:45

1. SQL Map XML Configuration File
 1.1 <settings> element
 1.2 <typeAlias> Element
 1.3 <transactionManager> Element
 1.4 <dataSource> Element
 1.5 <sqlMap> Element
 1.6 SqlMap 파일 예
2. Mapped Statements
 2.1 Auto-Generated Keys
 2.2 Stored Procedures
 2.3 parameterClass
 2.4 parameterMap
 2.5 Inline Parameters
 2.6 resultClass
 2.7 resultMap
 2.8 cacheModel
 2.9 xmlResultName
3. Parameter Maps and Inline Parameters
   3.1 Inline Parameter Maps
   3.2 Primitive Type Parameters
   3.3 Map Type Parameters

1. SQL Map XML Configuration File

1.1 <settings> element

maxRequests
동시에 SQL 문장을 수행할 수 있는 최대 쓰레드 개수
이 개수를 초과한 쓰레드가 수행될 경우 해당 쓰레드는 다른 쓰레드의 수행이 종료될 때 까지 block 된다.
보통 이 값은 maxTransactions의 적어도 10배 정도의 값을 설정한다. 그리고 항상 maxSessions, maxTransactions 보다 큰 값을 설정한다.
종종 동시 요청의 최대 값을 줄임으로써 성능을 증가시킬 수 있다.
디폴트는 512이다.

maxSessions
해당 시점에 활성화될 수 있는 세션의 개수
세션은 프로그램에 의해 생성되는 명시적 세션, SqlMapClient 인스턴스 사용으로 인한 자동 세션이 있다.
maxTransactions <= maxSessions < maxRequests
이 값을 줄이면 메모리 사용을 줄일 수 있다.
디폴트는 128이다.

maxTransactions
동시에 SqlMapClient.startTransaction()에 진입할 수 있는 최대 쓰레드 개수
지정된 수를 초과하는 쓰레드는 다른 쓰레드가 종료할 때 까지 block된다.
maxTransactions <= maxSessions
maxTransactions << maxRequests
이 값을 줄이면 성능이 증가하는 경우가 많다.
디폴트는 32

cacheModelsEnabled
전역적으로 SqlMapClient에 대한 모든 캐쉬 모델들을 enable/disable 한다.
디버깅시 유용하다.
디폴트는 true(enabled)

lazyLoadingEnabled
전역적으로 SqlMapClient에 대한 lazy loading을 enable/disable 한다.
디버깅시 유용하다.
디폴트는 true(enabled)

enhancementEnabled
enhanced lazy loading, 최적화된 JavaBean 프로퍼티 access를 위해 런타임 bytecode enhancement를 enable한다.
디폴트는 false(disabled)

useStatementNamespaces
이 값이 enable되면 mapped statement를 항상 fully qualified name(sqlMap 이름과 statement 이름)으로 참조해야 한다.
예). queryForObject("sqlMapName.statementName");
디폴트는 false(disabled)

1.2 <typeAlias> Element


일반적으로 긴 fully qualified name을 짧은 이름으로 참조하는 것을 허용하도록 한다.

예). <typeAlias alias="shortName" type="com.long.class.path.Class" />

아래와 같은 predefied alias가 SQL Map Config 파일에서 사용된다.

Transaction Manager Aliases

JDBC com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig
JTA com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig
EXTERNAL com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig

Data Source Factory Aliases

SIMPLE com.ibatis.sqlmap.engine.datasource.SimpleDataSourceFactory
DBCP com.ibatis.sqlmap.engine.datasource.DbcpDataSourceFactory
JNDI com.ibatis.sqlmap.engine.datasource.JndiDataSourceFactory

1.3 <transactionManager> Element


JDBC, JTA, EXTERNAL이 가능

JDBC - JDBC가 Connection의 commit(), rollback() 메소드를 통해 트랜잭션을 제어하도록 한다.
JTA - SQL Map Activities가 JTA 트랜잭션에 일부로써 포함되도록 한다. 이 구성은 JNDI 자원에서 user transaction을 locate할 수 있도록 UserTransaction 프로퍼티를 설정하는 것을 필요로 한다.
EXTERNAL - 개발자가 트랜잭션을 제어하도록 한다.

1.4 <dataSource> Element


SimpleDateSourceFactory, DbcpDataSourceFactory, JndiDataSourceFactory를 제공한다.

1.4.1 SimpleDateSourceFactory


컨테이너가 제공하는 Datasource가 없을 때 pooling data source를 제공하는 기본 구현
iBATIS의 SimpleDateSource 컨넥션 풀 구현에 기본한다.

아래와 같이 구성한다.

<transactionManager type="JDBC">
 <dataSource type="SIMPLE">
   <property name="JDBC.Driver" value="org.postgresql.Driver"/>
   <property name="JDBC.ConnectionURL" value="jdbc:postgresql://server:5432/dbname"/>
   <property name="JDBC.Username" value="user"/>
   <property name="JDBC.Password" value="password"/>
   <!-- OPTIONAL PROPERTIES BELOW -->
   <property name="Pool.MaximumActiveConnections" value="10"/>
   <property name="Pool.MaximumIdleConnections" value="5"/>
   <property name="Pool.MaximumCheckoutTime" value="120000"/>
   <property name="Pool.TimeToWait" value="10000"/>
   <property name="Pool.PingQuery" value="select * from dual"/>
   <property name="Pool.PingEnabled" value="false"/>
   <property name="Pool.PingConnectionsOlderThan" value="0"/>
   <property name="Pool.PingConnectionsNotUsedFor" value="0"/>
 </dataSource>
</transactionManager>

1.4.2 DbcpDataSourceFactory


자카르타 DBCP(Database Connection Pool)을 이용하여 DataSource API를 통해 컨넥션 풀링 서비스를 제공한다.
Application/Web 컨테이너가 DAtaSource 구현을 제공하지 못하거나 standalone 애플리케이션을 개발할 때 적절한 방법

아래와 같이 구성한다.

<transactionManager type="JDBC">
 <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}"/>
   <!-- OPTIONAL PROPERTIES BELOW -->
   <property name="Pool.MaximumActiveConnections" value="10"/>
   <property name="Pool.MaximumIdleConnections" value="5"/>
   <property name="Pool.MaximumWait" value="60000"/>
   <!-- Use of the validati??|??р誘м?????on query can be problematic.
   If you have difficulty, try without it. -->
   <property name="Pool.ValidationQuery" value="select * from ACCOUNT"/>
   <property name="Pool.LogAbandoned" value="false"/>
   <property name="Pool.RemoveAbandoned" value="false"/>
   <property name="Pool.RemoveAbandonedTimeout" value="50000"/>
 </datasource>
</transactionManager>

1.4.3 JndiDataSourceFactory

애플리케이션 컨테이너에서 JNDI 컨텍스트를 통해 DataSource 구현을 검색한다. 애플리케이션 서버가 사용되고, container managed connection pool과 관련된 DataSource 구현이 제공될 때 적절한 방법

아래와 같이 구성한다.

<transactionManager type="JDBC" >
 <dataSource type="JNDI">
   <property name="DataSource" value="java:comp/env/jdbc/jpetstore"/>
 </dataSource>
</transactionManager>

<transactionManager type="JTA" >
 <property name="UserTransaction" value="java:/ctx/con/UserTransaction"/>
 <dataSource type="JNDI">
   <property name="DataSource" value="java:comp/env/jdbc/jpetstore"/>
 </dataSource>
</transactionManager>

1.5 <sqlMap> Element

명시적으로 SQL Map이나 다른 SQL Map Configuration 파일을 포함하기 위해 사용된다.
SqlMapClient 인스턴스에 의해 사용될 각 SQL Map XML 파일은 반드시 선언되어야 한다.
SQL Map XML 파일은 classpath나 URL로 부터 얻어지는 stream resource로써 로딩된다.
아래와 같이 모든 Data Mapper를 명시해야 한다.


<!-- CLASSPATH RESOURCES -->
<sqlMap resource="com/ibatis/examples/sql/Customer.xml" />
<sqlMap resource="com/ibatis/examples/sql/Account.xml" />
<sqlMap resource="com/ibatis/examples/sql/Product.xml" />
<!-- URL RESOURCES -->
<sqlMap url="file:///c:/config/Customer.xml " />
<sqlMap url="file:///c:/config/Account.xml " />
<sqlMap url="file:///c:/config/Product.xml" />

1.6 SqlMap 파일 예

지금까지의 설명을 종합한 예를 보면 아래와 같다.

<sqlMap id="Product">
 <cacheModel id="productCache" type="LRU">
   <flushInterval hours="24"/>
   <property name="size" value="1000" />
 </cacheModel>

 <typeAlias alias="product" type="com.ibatis.example.Product" />

 <parameterMap id="productParam" class="product">
   <parameter property="id"/>
 </parameterMap>

 <resultMap id="productResult" class="product">
   <result property="id" column="PRD_ID"/>
   <result property="description" column="PRD_DESCRIPTION"/>
 </resultMap>

 <select id="getProduct" parameterMap="productParam"
     resultMap="productResult" cacheModel="product-cache">
   select * from PRODUCT where PRD_ID = ?
 </select>
</sqlMap>

위의 예를 간략하게 변경하면 아래와 같다.

<sqlMap id="Product">
 <select id="getProduct" parameterClass="com.ibatis.example.Product"
     resultClass="com.ibatis.example.Product">
   select
     PRD_ID as id,
     PRD_DESCRIPTION as description
   from PRODUCT
   where PRD_ID = #id#
 </select>
</sqlMap>

위의 두 문장은 완전히 동일한 것은 아니다. 아래와 같은 차이가 있다.
1. 후자는 캐쉬를 정의하지 않아서 요청은 항상 데이터베이스로 전파된다.
2. 후자는 iBATIS 프레임워크의 auto-mapping 기능을 사용한다. 이 기능은 약간의 오버헤드를 유발한다.


2. Mapped Statements

Mapped Statements는 파라미터 맵(input)과 result map(output)을 갖는 SQL 문장이다.
mapped statement는 캐쉬 모델을 사용하여 빈번히 발생하는 result를 메모리에 캐쉬하도록 설정될 수 있다.

<statement id="statementName"
 [parameterClass="some.class.Name"]
 [resultClass="some.class.Name"]
 [parameterMap="nameOfParameterMap"]
 [resultMap="nameOfResultMap"]
 [cacheModel="nameOfCache"]
>
 select * from PRODUCT where PRD_ID = [?|#propertyName#]
 order by [$simpleDynamic$]
</statement>

Statement ElementAttributesChild ElementsMethods
<statement>id
parameterClass
resultClass
parameterMap
resultMap
cacheModel
xmlResultName
All dynamic elements

insert
update
delete
All query methods

<insert>id
parameterClass
parameterMap
All dynamic elements
<selectKey>
insert
update
delete
<update>id
parameterClass
parameterMap
All dynamic elementsinsert
update
delete
<delete>id
parameterClass
parameterMap
All dynamic elementsinsert
update
delete
<select>id
parameterClass
resultClass
parameterMap
resultMap
cacheModel
All dynamic elementsAll query methods
<procedure>id
parameterClass
resultClass
parameterMap
resultMap
xmlResultName
All dynamic elementsinsert
update
delete
All query methods

xml에 정의하는 SQL 문장 중에 XML에서 사용되는 문자(<, > 등)가 포함되는 경우 아래와 같이 CDATA를 사용한다.

<statement id="getPersonsByAge" parameterClass=”int” resultClass="examples.domain.Person">
<![CDATA[
 SELECT *
 FROM PERSON
 WHERE AGE > #value#
 ]]>
 </statement>

2.1 Auto-Generated Keys

Data Mapper는 <insert> 문에 <selectKey>문을 이용하여 auto-generated key를 지원한다. <selectKey>는 pre-generated key(oracle sequence)와 post-generated key(MS-SQL 서버)를 모두 지원한다.

<!-Oracle SEQUENCE Example -->
<insert id="insertProduct-ORACLE" parameterClass="com.domain.Product">
<selectKey resultClass="int" >
 SELECT STOCKIDSEQUENCE.NEXTVAL AS ID FROM DUAL
 </selectKey>
 insert into PRODUCT (PRD_ID,PRD_DESCRIPTION)
 values (#id#,#description#)
</insert>


<!- Microsoft SQL Server IDENTITY Column Example -->
<insert id="insertProduct-MS-SQL" parameterClass="com.domain.Product">
 insert into PRODUCT (PRD_DESCRIPTION)
 values (#description#)
 <selectKey resultClass="int" >
 SELECT @@IDENTITY AS ID
 </selectKey>
 </insert>

2.2 Stored Procedures

<procedure> 문장을 통해 stored procedure를 제공한다. 아래 예는 output 파라미터를 갖는 SP가 어떻게 호출되는지 보여준다.

<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>

2.3 parameterClass

parameterClass 속성의 값은 java class에 대한 fully qualified name이다.
parameterClass 속성은 optional이지만 highly recommended된다.
parameterClass 속성은 statement로 전달되는 파라미터를 제한하기도 하지만 성능 최적화에도 영향을 미친다.
parameterMap을 사용하면 parameterClass 속성을 사용할 필요가 없다. 아래 예는 examples.domain.Product을 파라미터로 전달하는 예이다.

<statement id=”statementName” parameterClass=” examples.domain.Product”>
 insert into PRODUCT values (#id#, #description#, #price#)
 </statement>

2.4 parameterMap


parameterMap 속성의 값은 정의된 parameterMap element의 이름이다.
parameterMap 속성은 parameterClass 속성이나 inline parameter에 비해 잘 사용되지 않는다.
하지만 XML의 purity/consistency가 주요한 이슈이거나 보다 descriptive parameterMap을 원한다면 parameterMap 속성은 좋은 접근법이 된다.

주의 !
dynamic mapped statements는 inline parameters만 지원하고 parameterMap과는 함께 사용될 수 없다.

parameterMap 속성의 개념은 JDBC PreparedStatement에 사용될 값들에 대한 ordered list를 정의하는 것이다.

<parameterMap id="insert-product-param" class="com.domain.Product">
 <parameter property="id"/>
 <parameter property="description"/>
 </parameterMap>

<statement id="insertProduct" parameterMap="insert-product-param">
 insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?);
 </statement>

2.5 Inline Parameters

inline parameter는 mapped statement 내부에서 아래와 같이 사용된다.

<statement id="insertProduct" >
 insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)
 values (#id#, #description#);
 </statement>

위의 예에서 inline parameter는 #id#와 #description#이다. inline parameter는 빈의 프로퍼티를 나타낸다.

2.6 resultClass

resultClass 속성은 java class에 대한 fully qualified name이다.
resultClass 속성은 ResultSetMetaData에 의해 JDBC ResultSet에 자동으로 맵핑될 클래스를 명시하도록 한다.
자바 빈의 프로퍼티 이름과 테이블의 컬럼 이름이 매치되면 프로퍼티는 컬럼의 값으로 채워진다.

<statement id="getPerson" parameterClass=”int” resultClass="examples.domain.Person">
 SELECT
 PER_ID as id,
 PER_FIRST_NAME as firstName,
 PER_LAST_NAME as lastName,
 PER_BIRTH_DATE as birthDate,
 PER_WEIGHT_KG as weightInKilograms,
 PER_HEIGHT_M as heightInMeters
 FROM PERSON
 WHERE PER_ID = #value#
</statement>

위의 예에서 Person 클래스는 id, firstName 등과 같은 프로퍼티를 갖는다.
alias를 통해 컬럼의 값은 빈의 프로퍼티에 채워진다.

2.7 resultMap

resultMap의 값은 정의된 resultMap element의 이름이다. resultMap 속성을 사용하여 result set에서 데이터가 추출되는 방법과 어떤 프로퍼티가 어떤 컬럼에 맵핑되는지 제어할 수 있다.
자동으로 맵핑되는 resultClass와 달리 resultMap은 컬럼 타입, 널 값 치환 방법, complex property mapping 등을 지원한다.

<resultMap id="get-product-result" class="com.ibatis.example.Product">
 <result property="id" column="PRD_ID"/>
 <result property="description" column="PRD_DESCRIPTION"/>
 </resultMap>

<statement id="getProduct" resultMap="get-product-result">
 select * from PRODUCT
 </statement>

위의 예에서 SQL 쿼리의 result set은 resultMap 정의를 이용하여 Product 인스턴스로 맵핑된다. resultMap은 id 프로퍼티는 PRD_ID 컬럼의 값으로, description 프로퍼티는 PRD_DESCRIPTION 컬럼의 값으로 채워지도록 한다.
"select *"가 지원됨에 주의해야 한다. 이러한 기능은 result set에 반환된 모든 컬럼에 대한 맵핑을 정의할 필요는 없다는 것을 의미한다.

2.8 cacheModel


cacheModel 속성은 정의된 cacheModel element에 대한 이름이다.
cacheModel은 query mapped statement와 사용될 캐쉬를 서술하기 위해 사용된다. 각 query mapped statement는 같은 cacheModel을 사용할 수 도 있고, 서로 다른 cacheModel을 사용할 수 있다.

<cacheModel id="product-cache" imlementation="LRU">
 <flushInterval hours="24"/>
 <flushOnExecute statement="insertProduct"/>
 <flushOnExecute statement="updateProduct"/>
 <flushOnExecute statement="deleteProduct"/>
 <property name="size" value="1000" />
 </cacheModel>

<statement id="getProductList" parameterClass="int"   cacheModel="product-cache">
 select * from PRODUCT where PRD_CAT_ID = #value#
 </statement>

위의 예에서 product에 대해 정의된 캐쉬는 WEAK reference type을 사용한다. 이 캐쉬는 24 시간 마다 혹은 관련된 update 문장들이 수행될 때 마다 flush된다.

2.9 xmlResultName


결과를 XML 문서로 직접 맵핑할 때 xmlResultName의 값은 XML 문서의 root element의 이름이 된다.

<select id="getPerson" parameterClass=”int” resultClass="xml"   xmlResultName=”person”>
 SELECT
 PER_ID as id,
 PER_FIRST_NAME as firstName,
 PER_LAST_NAME as lastName,
 PER_BIRTH_DATE as birthDate,
 PER_WEIGHT_KG as weightInKilograms,
 PER_HEIGHT_M as heightInMeters
 FROM PERSON
 WHERE PER_ID = #value#
 </select>

위의 문장은 아래와 같은 구조를 갖는 XML 객체를 생성한다.

<person>
 <id>1</id>
 <firstName>Clinton</firstName>
 <lastName>Begin</lastName>
 <birthDate>1900-01-01</birthDate>
 <weightInKilograms>89</weightInKilograms>
 <heightInMeters>1.77</heightInMeters>
</person>

3. Parameter Maps and Inline Parameters

<parameterMap id="parameterMapName" [class="com.domain.Product"]>
   <parameter property ="propertyName" [jdbcType="VARCHAR"] [javaType="string"]
       [nullValue="NUMERIC"] [null="-9999999"]/>
   <parameter …… />
   <parameter …… />
</parameterMap>

class 속성은 optional이지만 사용하는 것이 좋다. parameterClass와 유사하게 class 속성은 프레임워크가 incoming parameter를 validate하고 성능을 위해 엔진을 최적화하도록 한다.

property

자바 빈의 프로퍼티

jdbcType

데이터베이스 컬럼 타입을 명시한다.

일반적인 컬럼이 nullable이면 필요 없지만 PreparedStatement.setNull(int parameterIndex, int sqlType) 메소드가 호출될 때는 필요하다. 어떤 데이터 타입에 대한 Null 값을 설정할 것인지 지정해야 하기 때문이다. 또한 명시적으로 데이터 타입을 지정한다는 점에서 유용하다. setNull 외에 Date 타입의 경우에도 유용하다. 자바는 java.util.Date 만을 제공하지만 대부분의 데이터베이스는 3개 정도의 date 관련 데이터 타입을 제공하기 때문에 명시적으로 지정하는 것이 좋다.

특히 오라클에서 컬럼 타입을 명시하지 않고 null을 설정하고자 할 때 "Invalid column type" 오류가 발생한다.

javaType

파라미터에 대한 자바 빈의 프로퍼티 타입을 지정한다.

일반적으로 이 타입은 빈의 프로퍼티 타입에서 reflection을 통해 자동으로 설정된다. 하지만 Map과 같은 타입은 프레임워크에 정확한 타입 정보를 제공할 수 없다. 만일 javaType이 설정되지 않았고 프레임워크가 타입을 결정할 수 없는 경우 타입은 Object로 간주된다.

nullValue

null을 표현할 수 없는 자바 빈의 프로퍼티에 대해 null 값을 정의하기 위해 사용된다.

아래는 parameterMap에 대한 예제이다.

<parameterMap id="insert-product-param" class="com.domain.Product">
   <parameter property="id" jdbcType="NUMERIC" javaType="int" nullValue="-9999999"/>
   <parameter property="description" jdbcType="VARCHAR" nullValue="NO_ENTRY"/>
</parameterMap>

<statement id="insertProduct" parameterMap="insert-product-param">
   insert into PRODUCT (PRD_ID, PRD_DESCRIPTION) values (?,?);
</statement>

3.1 Inline Parameter Maps

<statement id="insertProduct" parameterClass="com.domain.Product">
   insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)
   values (#id#, #description#);
</statement>

<statement id="insertProduct" parameterClass="com.domain.Product">
   insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)
   values (#id:NUMERIC#, #description:VARCHAR#);
</statement>

<statement id="insertProduct" parameterClass="com.domain.Product">
   insert into PRODUCT (PRD_ID, PRD_DESCRIPTION)
   values (#id:NUMERIC:-999999#, #description:VARCHAR:NO_ENTRY#);
</statement>

인라인 파라미터 맵을 사용할 경우에는 type 설정 없이 null value를 설정할 수는 없다.

3.2 Primitive Type Parameters

단지 파라미터로 사용하고자 자바 빈을 만드는 것은 바람직하지 않다. 이러한 경우 primitive type wrapper 객체(String, Integer, Date 등)를 사용한다.

<statement id="insertProduct" parameter="java.lang.Integer">
   select * from PRODUCT where PRD_ID = #value#
</statement>

3.3 Map Type Parameters

Map(HashMap, TreeMap 등)을 파라미터 객체로 사용하는 방법

<statement id="insertProduct" parameterClass="java.util.Map">
   select * from PRODUCT
   where PRD_CAT_ID = #catId#
   and PRD_CODE = #code#
</statement>

위와 같은 경우 Map에는 catId, code를 키로 갖는 값이 존재해야 한다.

4. Result Maps

<resultMap id="resultMapName" class="some.domain.Class" [extends="parent-resultMap"]>
   <result property="propertyName" column="COLUMN_NAME"
       [columnIndex="1"] [javaType="int"] [jdbcType="NUMERIC"]
       [nullValue="-999999"] [select="someOtherStatement"]
       />
   <result ……/>
   <result ……/>
   <result ……/>
</resultMap>

extends 속성은 선택적으로 사용되어 베이스로 삼을 다른 resultMap을 설정하기 위해 사용된다. super resultMap의 속성은 항상 sub resultMap의 속성 보다 먼저 insert 된다. 또 parent resultMap은 항상 child 보다 먼저 정의되어야 한다.

property

column

columnIndex

설정되었을 경우 jdbc 드라이버 구현에 따라 성능 향상을 얻을 수 있다.

jdbcType

javaType

nullValue

<resultMap id="get-product-result" class="com.ibatis.example.Product">
   <result property="id" column="PRD_ID"/>
   <result property="description" column="PRD_DESCRIPTION"/>
   <result property="subCode" column="PRD_SUB_CODE" nullValue="-999"/>
</resultMap>

select

select 속성은 객체와 자동으로 로딩되는 complex(user defined 등) 프로퍼티 타입 간의 관계를 서술하기 위해 사용된다. select 속성의 값은 다른 mapped statement의 이름이여야 한다. select 속성이 정의된 프로퍼티의 데이터베이스 컬럼은 반드시 지원되는 primitive type이여야 한다.

:
Posted by codetemplate

http://jwebunit.sourceforge.net

jWebUnit이란 ?

jWebUnit은 웹 어플리케이션을 위한 acceptance test 생성을 위한 편의를 제공하는 자바 프레임워크이다.

jWebUnit은 acceptance test 생성을 위해 HttpUnit, JUnit으로 부터 파생되었다.

jWebUnit이 제공하는 기능 ?

jWebUnit은 웹 어플리케이션 네비게이션, 정합성 확인을 위한 assertion을 등을 포함한 high level API를 제공한다. jWebUnit의 API는 링크를 이용한 네비게이션, 폼 입력 및 서밋, 테이블 내용 validation 및 비지니스 웹 어플리케이션 특징을 포함한다. jWebUnit API는 내부적으로 HttpUnit을 이용한다. jWebUnit은 간단한 네비게이션, 바로 사용 가능한 assertion등을 통해 HttpUnit, JUnit만을 사용했을때 보다 빨리 테스트를 생성할 수 있도록 한다.

아래는 jWebUnit을 이용한 테스트 코드와 HttpUnit, JUnit만을 사용한 테스트 코드이다. 테스트는 HttpUnit 홈 페이지를 구글에서 검색하고, 해당 페이지로 네비게이트하고 사용자 메뉴얼로의 링크가 있는지 조사한다.

먼저 JUnit/HttpUnit Test를 이용한 테스트 코드는 아래와 같다.

public class SearchExample extends TestCase {
  public void testSearch() throws Exception {
     WebConversation wc = new WebConversation();
     WebResponse resp = wc.getResponse( "http://www.google.com");
     WebForm form = resp.getForms()[0];
     form.setParameter("q", "HttpUnit");
     WebRequest req = form.getRequest("btnG");
     resp = wc.getResponse(req);
     assertNotNull(resp.getLinkWith("HttpUnit"));
     resp = resp.getLinkWith("HttpUnit").click();
     assertEquals(resp.getTitle(), "HttpUnit");
     assertNotNull(resp.getLinkWith("User's Manual"));
  }
}

위의 코드를 jWebUnit을 이용하여 구현한 테스트 코드는 아래와 같다.

public class JWebUnitSearchExample extends WebTestCase {
  public JWebUnitSearchExample(String name) {
     super(name);
  }

  public void setUp() {
     getTestContext().setBaseUrl("http://www.google.com");
  }

  public void testSearch() {
     beginAt("/");
     setFormElement("q", "httpunit");
     submit("btnG");
     clickLinkWithText("HttpUnit");
     assertTitleEquals("HttpUnit");
     assertLinkPresentWithText("User's Manual");
  }
}

java script error 무시하기

  protected void setUp() throws Exception {
       super.setUp();

       getTestContext().setBaseUrl("http://oracle.kyobodirect.com:8801");

       HttpUnitOptions.setExceptionsThrownOnScriptError(false);
  }

한글을 이용하여 값을 비교하기


  private String encodeCharSet(String s) {
       try {
           return new String(s.getBytes("EUC_KR"), "8859_1");
       } catch (UnsupportedEncodingException e) {
           e.printStackTrace();
           throw new RuntimeException(e);
       }
  }

  public void testLogIn() {
       beginAt("/login.jsp");

       setFormElement("id", "myid");
       setFormElement("password", "mypassword");

       submit();

       assertTitleEquals(encodeCharSet("환영합니다."));

  }

jWebUnit의 보다 구체적인 사용예를 위해 jsp 파일과 테스트 코드를 첨부한다.

:
Posted by codetemplate
2006. 10. 30. 19:26

nio Selector을 이용한 Multiplexed I/O Java2006. 10. 30. 19:26

새로운 소켓 컨넥션(채널)마다 새로운 쓰레드를 생성하는 대신 채널을 multiplexing하면 하나 이상의 쓰레드가 모든 채널을 처리할 수 있다.
특정 오퍼레이션에 관심사항을 등록한다. 그러면 가용한 쓰레드가1G사용해서 키의 상태를 검사할 수 있다. 이러한 메소드들은 어떤 키를 기다리는 오퍼레이션이 처리될 것인지 알려준다.

  if (selectionKey.isAcceptable()) {
    socket = serverSocket.accept();
    channel = socket.getChannel();
    if (channel != null) {
      channel.configureBlocking(false);
      channel.register(selector, SelectionKey.OP_READ);
    }
  }

만일 exception이 발생하면 아래와 같이 key를 cancel할 수 있다.

  } catch (SomeException e) {
    selectionKey.cancel();
  }

위의 코드는 key와 관련된 컨넥션을 무효화한다.

다음은 지금까지 설명한 예로 작성한 서버와 클라이언트이다.

package test;

import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.*;

public class SelectorTest {
  private static int PORT = 9876;
  private static int BUFFER_SIZE = 1024;
 
  public static void main (String args[]) {
       ByteBuffer sharedBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE);
       Selector selector = null;
       ServerSocket serverSocket = null;
      
       try {
           ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
           serverSocketChannel.configureBlocking(false);
          
           serverSocket = serverSocketChannel.socket();
           InetSocketAddress inetSocketAddress = new InetSocketAddress(PORT);
           serverSocket.bind(inetSocketAddress);
          
           selector = Selector.open();
           serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
       }
       catch (IOException e) {
           System.err.println("Unable to setup environment\");
           System.exit(-1);
       }
      
       try {
           while (true) {
               int count = selector.select();
              
               // nothing to process
               if (count == 0) {
                   continue;
               }
              
               Set keySet = selector.selectedKeys();
               Iterator itor = keySet.iterator();

               while (itor.hasNext()) {
                   SelectionKey selectionKey = (SelectionKey) itor.next();
                   itor.remove();
                  
                   Socket socket = null;
                   SocketChannel channel = null;
                  
                   if (selectionKey.isAcceptable()) {
                       System.out.println("["+Thread.currentThread().getName()+"] Got acceptable key\");
                       try {
                           socket = serverSocket.accept();
                           System.out.println("["+Thread.currentThread().getName()+"] Connection from: " + socket);
                           channel = socket.getChannel();
                       }
                       catch (IOException e) {
                           System.err.println("["+Thread.currentThread().getName()+"] Unable to accept channel");
                           e.printStackTrace();
                           selectionKey.cancel();
                       }
                       if (channel != null) {
                           try {
                               System.out.println("["+Thread.currentThread().getName()+"] Watch for something to read");
                               channel.configureBlocking(false);
                               channel.register(selector, SelectionKey.OP_READ);
                           }
                           catch (IOException e) {
                               System.err.println("["+Thread.currentThread().getName()+"] Unable to use channel\");
                               e.printStackTrace();
                               selectionKey.cancel();
                           }
                       }
                   }
                  
                   if (selectionKey.isReadable()) {
                       System.out.println("["+Thread.currentThread().getName()+"] Reading channel");
                       SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                       sharedBuffer.clear();
                      
                       int bytes = -1;
                       try {
                           while ((bytes = socketChannel.read(sharedBuffer)) &gt; 0) {
                               System.out.println("["+Thread.currentThread().getName()+"] Reading...\");
                               sharedBuffer.flip();
                               while (sharedBuffer.hasRemaining()) {
                                   System.out.println("["+Thread.currentThread().getName()+"] Writing...");
                                   socketChannel.write(sharedBuffer);
                               }
                               sharedBuffer.clear();
                           }
                       }
                       catch (IOException e) {
                           System.err.println("["+Thread.currentThread().getName()+"] Error writing back bytes\");
                           e.printStackTrace();
                           selectionKey.cancel();
                       }
                      
                       try {
                           System.out.println("["+Thread.currentThread().getName()+"] Closing...\");
                           socketChannel.close();
                       }
                       catch (IOException e) {
                           e.printStackTrace();
                           selectionKey.cancel();
                       }
                   }
                   System.out.println("["+Thread.currentThread().getName()+"] Next...\");
               }
           }
       }
       catch (IOException e) {
           System.err.println("["+Thread.currentThread().getName()+"] Error during select()\");
           e.printStackTrace();
       }
  }
}


package test;

import java.net.*;
import java.io.*;

public class Connection {
  private static final int LOOP_COUNT = 100;
  private static final int SLEEP_TIME = 500;
  private static final int PORT = 9876;
 
  public static void main(String args[]) throws IOException, InterruptedException {
       for (int i=0; i&lt;LOOP_COUNT; i++) {
           Socket socket = new Socket("localhost", PORT);
           InputStream is = socket.getInputStream();
           OutputStream os = socket.getOutputStream();
           Writer writer = new OutputStreamWriter(os, "US-ASCII");
           PrintWriter out = new PrintWriter(writer, true);
           out.println("Hello, World");
           BufferedReader in = new BufferedReader(new InputStreamReader(is, "US-ASCII"));
           String line;
           while ((line = in.readLine()) != null) {
               System.out.println(i + \": \" + line);
           }
           socket.close();
           Thread.sleep(SLEEP_TIME);
       }
  }
}

:
Posted by codetemplate

ApplicationContext ctx = WebApplicationContextUtils.
getWebApplicationContext(servletContext);
Bar bar = (Bar) ctx.getBean("bar");

:
Posted by codetemplate
2006. 10. 30. 19:17

오라클에서 escape 문자 사용법 DB2006. 10. 30. 19:17

set escape '\'

insert into <table_name>
values('s\&p');
:
Posted by codetemplate