Friday, January 25, 2013

Spring MVC + Hibnerate + jQuery DataTables + Pagination

JQuery datatables (http://www.datatables.net/) always gets all data from server for its build-in pagination, it is not good for huge data, the better approach is to pass current position and page size to server and get data from database batch by batch.

so refer to http://datatables.net/usage/server-side, there are some para we must pass from client to server.

1) on the client side,


oTBExample2 = $("#example2").dataTable({
"bProcesing" : true,
"bServerSide" : true,
"bLenthChange" : false,
"iDisplayLength" : 10,
"sAjaxSource" : "users2.do",
"aoColumns" : [
{
"sTitle" : "User ID",
"mData" : "userid"
},
{
"sTitle" : "Password",
"mData" : "password"
},
{
"sTitle" : "Date & Time",
"mData" : "timeStamp"
},
{
"mData" : "checked",
"fnRender" : function(obj) {
if (obj.aData.checked == "1")
return '<input type="checkbox" name="aCheckBox" class="dt_checked" checked value="'+obj.aData.checked+'\"/>';

return '<input type="checkbox" name="aCheckBox"  class="dt_checked" value="'+obj.aData.checked+'\"/>';
}
},
{
"mData" : null,
"sClass" : "center",
"sDefaultContent" : '<a href="" class="editor_remove" >Delete</a>'
} ],
"fnServerData" : function(sSource, aoData, fnCallback) {
$.ajax({
"dataType" : 'json',
"type" : "GET",
"url" : sSource,
"data" : aoData,
"success" : fnCallback
});
},
"sPaginationType" : "full_numbers"

});// dataTable



<table id="example2" cellpadding="0" cellspacing="0" border="0"
class="display datatable">
<thead>
<tr>
<th width="20%">userid</th>
<th width="20%">password</th>
<th width="20%">time</th>
<th width="10%">selected</th>
<th width="10%">Delete</th>
</tr>
</thead>
<tbody>
</tbody>
</table>


2) on server side,



@RequestMapping(value = "/users2", produces = "application/json")
public @ResponseBody
String showUser(@RequestParam int iDisplayStart,
            @RequestParam int iDisplayLength, @RequestParam int sEcho) {
String method="showUser";
info(method,"para0---"+iDisplayStart);
info(method,"para1---"+iDisplayLength);
info(method,"para2---"+sEcho);
DataTablesTO<Account> dt = new DataTablesTO<Account>();

List<Account> accts = accountService.getAccounts(iDisplayStart,iDisplayLength);
List<Account> accts2 = accountService.getAccounts();
dt.setAaData(accts);  // this is the dataset reponse to client
dt.setiTotalDisplayRecords(accts2.size());  // // the total data in db for datatables to calculate page no. and position
dt.setiTotalRecords(accts2.size());   // the total data in db for datatables to calculate page no.
dt.setsEcho(sEcho);

return toJson(dt);
}


private String toJson(DataTablesTO<?> dt){
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(dt);
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}

need jackson jar files to support json conversion,

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;


public class DataTablesTO<T> implements java.io.Serializable{

private static final long serialVersionUID = -8220588043068200705L;
private List<T> aaData;
private int sEcho;
private Integer iTotalRecords;
private Integer iTotalDisplayRecords;
...... getter and setter.......
}

3) hibernate query


public List<Account> getAccounts(int currPosition, int pageSize) {

return sessionFactory.getCurrentSession().createQuery("from Account").setMaxResults(pageSize).setFirstResult(currPosition).list();
}


current position which means the first row to be shown on the page in the table.

4) actual result on page









5) this is the request data when call ajax


  1. sEcho:
    1
  2. iColumns:
    5
  3. sColumns:
  4. iDisplayStart:
    0
  5. iDisplayLength:
    10
  6. mDataProp_0:
    userid
  7. mDataProp_1:
    password
  8. mDataProp_2:
    timeStamp
  9. mDataProp_3:
    checked
  10. mDataProp_4:
  11. sSearch:
  12. bRegex:
    false
  13. sSearch_0:
  14. bRegex_0:
    false
  15. bSearchable_0:
    true
  16. sSearch_1:
  17. bRegex_1:
    false
  18. bSearchable_1:
    true
  19. sSearch_2:
  20. bRegex_2:
    false
  21. bSearchable_2:
    true
  22. sSearch_3:
  23. bRegex_3:
    false
  24. bSearchable_3:
    true
  25. sSearch_4:
  26. bRegex_4:
    false
  27. bSearchable_4:
    true
  28. iSortCol_0:
    0
  29. sSortDir_0:
    asc
  30. iSortingCols:
    1
  31. bSortable_0:
    true
  32. bSortable_1:
    true
  33. bSortable_2:
    true
  34. bSortable_3:
    true
  35. bSortable_4:
    true

6) right now, the datatables does not support order by column, if click the second column to order by it,let's check the request data.

  1. sEcho:
    7
  2. iColumns:
    5
  3. sColumns:
  4. iDisplayStart:
    0
  5. iDisplayLength:
    10
  6. mDataProp_0:
    userid
  7. mDataProp_1:
    password
  8. mDataProp_2:
    timeStamp
  9. mDataProp_3:
    checked
  10. mDataProp_4:
  11. sSearch:
  12. bRegex:
    false
  13. sSearch_0:
  14. bRegex_0:
    false
  15. bSearchable_0:
    true
  16. sSearch_1:
  17. bRegex_1:
    false
  18. bSearchable_1:
    true
  19. sSearch_2:
  20. bRegex_2:
    false
  21. bSearchable_2:
    true
  22. sSearch_3:
  23. bRegex_3:
    false
  24. bSearchable_3:
    true
  25. sSearch_4:
  26. bRegex_4:
    false
  27. bSearchable_4:
    true
  28. iSortCol_0:
    1 // means sorted by the second column
  29. sSortDir_0:
    asc // means order by asc, if click again, it will change to desc
  30. iSortingCols:
    1
  31. bSortable_0:
    true
  32. bSortable_1:
    true
  33. bSortable_2:
    true
  34. bSortable_3:
    true
  35. bSortable_4:
    true


so, we also need to change the hql as follows

from Account order by ${iSortCol_0}+1 ${sSortDir_0}








jQuery datatables Tips


  • basic useage
1) add necessary js and css files

<script src="<c:url value='js/jquery-1.8.3.min.js'/>" /></script>
<script src="<c:url value='js/jquery-ui-1.10.0.custom.min.js'/>"></script>
<script src="<c:url value='js/jquery.dataTables.min.js'/>"></script>
<script src="<c:url value='js/jquery.dataTables.editable.js'/>"></script>
<link rel="stylesheet" href="css/jquery-ui-1.9.2.custom.css" media="all" />
<link rel="stylesheet" href="css/demo_page.css" />
<link rel="stylesheet" href="css/demo_table.css" />


2)
var oTable = $('#myDataTable').dataTable({
"bProcessing" : true,
"bDestroy" : true,
"sAjaxSource" : 'users.do',  // ajax to take the data
"sAjaxDataProp" : "",
//"bFilter":false,
"aoColumns" : [ {
"mData" : "userid"
}, {
"mData" : "password"
}, {
"mData" : "timeStamp"
} ],
"sPaginationType" : "full_numbers",
"aLengthMenu" : [ [ 4, 10, 20, -1 ], [ 4, 10, 20, "All" ] ],
"iDisplayLength" : 4
});

3)
<body id="dt_example">
<div id="container">

<table id="myDataTable" cellpadding="0" cellspacing="0" border="0"
class="display">
<thead>
<tr>
<th width="20%">userid</th>
<th width="20%">password</th>
<th width="20%">time</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</body>

4) why need to put "sAjaxDataDrop" as empty, because the data returned as follows,


[{"userid":"a1","password":"a","timeStamp":1359012297000,"checked":0},
{"userid":"abgh0","password":"dddd0","timeStamp":1359012165000,"checked":1},
{"userid":"abgh1","password":"dddd1","timeStamp":1359012165000,"checked":1},
{"userid":"abgh10","password":"dddd10","timeStamp":1359012165000,"checked":1}]

if the data returned looks like this,


["Demo":[{"userid":"a1","password":"a","timeStamp":1359012297000,"checked":0},
{"userid":"abgh0","password":"dddd0","timeStamp":1359012165000,"checked":1},
{"userid":"abgh1","password":"dddd1","timeStamp":1359012165000,"checked":1},
{"userid":"abgh10","password":"dddd10","timeStamp":1359012165000,"checked":1}]]

then need to configure 'sAjaxDataDrop' as 'Demo'


5) actual result on page:








  • make inline cells editable, show checkbox column, show delete column

oTable2 = $('#example').dataTable( {
      "bProcessing" : true,
      "sAjaxSource" : 'users.do',
       "sAjaxDataProp" : "",
       "bSort" : true,
        "bSearchable" : false,
        "aoColumns" : [
{
"sTitle" : "User ID",
"mData" : "userid"
},
{
"sTitle" : "Password",
"mData" : "password"
},
{
"sTitle" : "Date & Time",
"mData" : "timeStamp"
},
{
"mData" : "checked",
"fnRender" : function(obj) {
if (obj.aData.checked == "1")
return '<input type="checkbox" name="aCheckBox" class="dt_checked" checked value="'+obj.aData.checked+'\"/>';

return '<input type="checkbox" name="aCheckBox"  class="dt_checked" value="'+obj.aData.checked+'\"/>';
}
},
{
"mData" : null,
"sClass" : "center",
"sDefaultContent" : '<a href="" class="editor_remove" >Delete</a>'
} ],

//"sPaginationType" : "full_numbers",
"sPaginationType" : "two_button",
//"aLengthMenu" : [ [ 4, 10, 20, -1 ],
// [ 4, 10, 20, "All" ] ],
"iDisplayLength" : 10,
}).makeEditable({
sUpdateURL : "updateUser.do",
"aoColumns" : [ {}, null, null, null, null ]  // which means only first row is allowed to edit
});


1) use "fnRender" to show checkbox which is selected or not decided by data from server called by ajax

2) add delete link in each row
                   {
"mData" : null,
"sClass" : "center",
"sDefaultContent" : '<a href="" class="editor_remove" >Delete</a>'
}
3) .makeEditable() to make the cells editable, but we also can control which columns are allowed to edit

4) if click delete link, trigger this event to delete data in database.


$("a.editor_remove", oTable2).live("click", function(e) {
e.preventDefault();
var r = confirm("Do you want to delete this item?");
if (r == true) {

var nRow = $(this).parents('tr')[0];
var userid = nRow.cells[0].innerHTML;
$.ajax({
type : "POST",
url : "deleteUser.do",
data : {
"value" : userid
}
}).success(function(msg) {
oTable2.fnDeleteRow(nRow);
oTable2.fnDraw();
}).error(function(msg) {
alert(msg);
});
} else {

}

});


5) add event if checkbox status is changed


$("#example").on('change', 'input[type=checkbox]', function(e) {
var r = confirm("Do you want to change this item?");
if (r == true) {
var nRow = $(this).parents('tr')[0];
var userid = nRow.cells[0].innerHTML;
var oldchecked = $(nRow.cells[3].innerHTML).attr('value');

$.ajax({
type : "POST",
url : "updateUserStatus.do",
data : {
"userId" : userid,
"checked" : oldchecked
}
}).success(function(msg) {
oTable2.fnDraw(false);
}).error(function(msg) {
alert(msg);
});
} else {

}

});

6) actual result:










  • hide search box on the right-top corner
$(".dataTables_filter").css({
"display" : "none"
});
  • hide the drop-down list to show page size
$(".dataTables_length").css({
"display" : "none"
});

Spring MVC v3.2 Json Response

1) example to put a list of userid in a combobox via ajax call, and spring mvc returns json data directly


$.ajax({
type : 'POST',
url : "users.do",
success : function(data) {
var userid = data[0].userid;
console.info("2-------------" + userid);
$(data).each(function() {
var option = $('<option/>');
option.attr('value', this.userid).text(this.userid);
$("#combobox").append(option); // loop response data and add option to combobox
}) },
error : function(XMLHttpRequest, textStatus, errorThrown) {
alert(textStatus);
},
dataType : "json"
});


2) use datatables jquery plugin to show data in a table on front end, spring mvc returns json data directly

var oTable = $('#myDataTable').dataTable({
"bProcessing" : true,
"bDestroy" : true,
"sAjaxSource" : 'users.do',
"sAjaxDataProp" : "",
//"bFilter":false,
"aoColumns" : [ {
"mData" : "userid"
}, {
"mData" : "password"
}, {
"mData" : "timeStamp"
} ],
"sPaginationType" : "full_numbers",
"aLengthMenu" : [ [ 4, 10, 20, -1 ], [ 4, 10, 20, "All" ] ],
"iDisplayLength" : 4
});


<table id="myDataTable" cellpadding="0" cellspacing="0" border="0" class="display datatable"> <thead> <tr> <th width="20%">userid</th> <th width="20%">password</th> <th width="20%">time</th> </tr> </thead> <tbody> </tbody>
</table>

3) java code in servlet


@RequestMapping(value = "/users", produces = "application/json")
public @ResponseBody List<Account> showUser() {
return accountService.getAccounts();
}

From the source code, we can see it just returns a pure java list, the @ResponseBody annotation and produces = "application/json" will help to convert java objects to json string.


4) need add jackson dependency

<!-- support json response --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> <!-- End support json response -->

otherwise, you may encounter 406 (Not Acceptable)

5) This is the example data returned by spring mvc

[{"userid":"a1","password":"a","timeStamp":1359012297000,"checked":0}, {"userid":"abgh0","password":"dddd0","timeStamp":1359012165000,"checked":1}, {"userid":"abgh1","password":"dddd1","timeStamp":1359012165000,"checked":1}, {"userid":"abgh10","password":"dddd10","timeStamp":1359012165000,"checked":1}]



Maven Tips


  •  skip test while building
<!-- maven plugin to skip test while building -->
<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-surefire-plugin</artifactId>
      <configuration>
         <skipTests>true</skipTests>
      </configuration>
</plugin>

  •  dependency management
in the pom.xml of parent module, use <dependencyManagement>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

in the pom.xml of individual modules,

<dependencies>
<dependency>
<groupId>junit</groupId>
   <artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>

It is not necessary to specify version, let parent pom.xml to manage the version. If there is any change in parent pom, all sub-modules will be changed as well.

and also need to add this element to import above dependencies in the individual modules' pom.xml

<dependencyManagement>
<dependencies>
<dependency>
<groupId>{parent groupId}</groupId>
<artifactId>{parent artifactId}</artifactId>
<version>{parent version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>


  • source encoding
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>



  • copy generated jar file to another folder
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-jar</id>
<!-- here the phase you need -->
<phase>package</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/dist/lib</outputDirectory>
<resources>
<resource>
<directory>target</directory>
<includes>
<include>*.jar</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>




  • build for multiple environment
1) define envs

<profiles>
<profile>
<id>prod</id>
<properties>
<package.environment>prod</package.environment>
</properties>
</profile>
<profile>
<id>dev</id>
<properties>
<package.environment>dev</package.environment>
</properties>
</profile>
<profile>
<id>sit</id>
<properties>
<package.environment>sit</package.environment>
</properties>
</profile>
<profile>
<id>uat</id>
<properties>
<package.environment>uat</package.environment>
</properties>
</profile>
</profiles>

2) specify default env if build

<properties>
<package.environment>dev</package.environment>
</properties>



3) copy files based on env


<build>
<plugins>
<!-- maven plugin to skip test while building -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
<!-- copy files based on different environments -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<!-- here the phase you need -->
<phase>install</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>target/dist/conf</outputDirectory>
<resources>
<resource>
<directory>src/main/resources/${package.environment}</directory>

                                                                        <includes>
<include>log4j.properties</include>
<include>hibernate.xml</include>
</includes>

<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

if copy jar files,etc,  TAKE NOTE <filtering>false</filtering>
4) command to build for sit env:
 mvn clean install -P sit

Wednesday, January 23, 2013

maven tomcat plugin

maven: v3.x
tomcat: v6.0.32
maven tomcat plugin: v2.0  (http://tomcat.apache.org/maven-plugin-2.0/)

1. add maven tomcat plugin in pom.xml


<build>
<!-- apache tomcat maven plugin -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat6-maven-plugin</artifactId>
<version>2.0</version>
<configuration>
<url>http://localhost:8080/manager</url>
<server>demo-dev</server>
<path>/demo</path>
</configuration>
</plugin>
<!-- maven plugin to skip test while building -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>

2. edit .m2\settings.xml


<server>
    <id>demo-dev</id>
            <username>manager</username>
            <password>tomcat</password>
        </server>

3. make sure above username/password is configured in %tomcat%/conf/tomcat-users.xml

<role rolename="manager"/>
<user username="manager" password="tomcat" roles="manager"/>

4. Configure JNDI
4.1 configure JNDI in %tomcat%/conf/server.xml   (refer to http://tomcat.apache.org/tomcat-6.0-doc/jndi-resources-howto.html)


4.2 Alternatively,configure JNDI in  META-INF/context.xml for individual project, for example,

<Context>

    <!-- maxActive: Maximum number of database connections in pool. Make sure you
         configure your mysqld max_connections large enough to handle
         all of your db connections. Set to -1 for no limit.
         -->

    <!-- maxIdle: Maximum number of idle database connections to retain in pool.
         Set to -1 for no limit.  See also the DBCP documentation on this
         and the minEvictableIdleTimeMillis configuration parameter.
         -->

    <!-- maxWait: Maximum time to wait for a database connection to become available
         in ms, in this example 10 seconds. An Exception is thrown if
         this timeout is exceeded.  Set to -1 to wait indefinitely.
         -->

    <!-- username and password: MySQL username and password for database connections  -->

    <!-- driverClassName: Class name for the old mm.mysql JDBC driver is
         org.gjt.mm.mysql.Driver - we recommend using Connector/J though.
         Class name for the official MySQL Connector/J driver is com.mysql.jdbc.Driver.
         -->
    
    <!-- url: The JDBC connection url for connecting to your MySQL database.
         -->

  <Resource name="jdbc/demodb" auth="Container" type="javax.sql.DataSource"
               maxActive="100" maxIdle="30" maxWait="10000"
               username="{userid}" password="{password}" driverClassName="com.mysql.jdbc.Driver"
               url="jdbc:mysql://localhost:3306/demodb"/>

</Context>

4.3 configure <resource-ref> element in web.xml

<resource-ref>
<description>JNDI config to access MySQL Database</description>
<res-ref-name>jdbc/demodb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>


jetty maven plugin

maven: v3.x
maven jetty plugin : 7.6.3.v20120416
install m2eclipse plugin

1. add jetty dependency in pom.xml

<build>
<!-- jetty maven plugin -->
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>7.6.3.v20120416</version>
<configuration>
<jettyXml>src/test/resources/jetty-plus.xml</jettyXml>
<!-- scan project folder to check if any changes, restart jetty if any changes found -->
<scanIntervalSeconds>10</scanIntervalSeconds>
<webAppConfig>
<contextPath>/demo</contextPath>
</webAppConfig>
<stopPort>9966</stopPort>
<stopKey>stop</stopKey>
<systemProperties>
<systemProperty>
<name>log4j.configurationFile</name>
<value>src/main/resources/log4j.properties</value>
</systemProperty>
</systemProperties>
</configuration>
<dependencies>
<!-- modules dependency -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</plugin>
<!-- maven plugin to skip test while building -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>

2. open 'Run Configurations...' dialogue in eclipse
2.1 under menu 'Maven Build' to create a new task 'jetty-stop'













2.2 create a new task 'jetty-debug'




























3. configure JNDI in jetty
take note the configuration highlighted in pom.xml

This is the JNDI settings in jetty-plus.xml


<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd">

<Configure id="Server" class="org.eclipse.jetty.server.Server">
<Array id="plusConfig" type="java.lang.String">
<Item>org.eclipse.jetty.webapp.WebInfConfiguration</Item>
<Item>org.eclipse.jetty.webapp.WebXmlConfiguration</Item>
<Item>org.eclipse.jetty.webapp.MetaInfConfiguration</Item>
<Item>org.eclipse.jetty.webapp.FragmentConfiguration</Item>
<Item>org.eclipse.jetty.plus.webapp.EnvConfiguration</Item> <!-- add for JNDI -->
<Item>org.eclipse.jetty.plus.webapp.PlusConfiguration</Item>    <!-- add for JNDI -->
<Item>org.eclipse.jetty.webapp.JettyWebXmlConfiguration</Item>
<Item>org.eclipse.jetty.webapp.TagLibConfiguration</Item>
</Array>

<Call name="setAttribute">
<Arg>org.eclipse.jetty.webapp.configuration</Arg>
<Arg>
<Ref id="plusConfig" />
</Arg>
</Call>

<!-- binding Jetty JNDI datasource to jdbc/mydatasource -->
<New id="integration_datasource_jndi" class="org.eclipse.jetty.plus.jndi.Resource">
<Arg>
<Ref id="Server" />
</Arg>
<Arg>jdbc/demodb</Arg>
<Arg>
<New class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<Set name="driverClassName">com.mysql.jdbc.Driver</Set>
<Set name="url">jdbc:mysql://localhost:3306/demodb</Set>
<Set name="username">{userid}</Set>
<Set name="password">{password}</Set>
</New>
</Arg>
</New>
</Configure>

and in web.xml , need configure <resource-ref> element as follows,


<resource-ref>
<description>JNDI config to access MySQL Database</description>
<res-ref-name>jdbc/demodb</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>



Tuesday, September 18, 2012

clear statement cache in weblogic


This case confused me for hours today. I have a web service app hosted by weblogic v10.3.

A tester used an appId which is a part of input to call a web service. The server need to query database based on the appId passing from web service client. So it is ok system returned null because there was no data about the appId in database.
I added the appId in database, and tried to call again. It was suprised that system still returned null, while there was something returned if I tested it via junit class.

The only possibility is that server, whatever weblogic server or database server, returns historical data from cache.By default, weblogic set statement cache type as LRU and cache size as 10, refer to http://docs.oracle.com/cd/E12839_01/web.1111/e13737/jdbc_datasources.htm#JDBCA171 and oracle jdbc implemention caches the test result,refer to http://docs.oracle.com/cd/B10501_01/java.920/a96654/stmtcach.htm, so that is why I got unexpected result before clearing the statement cache in weblogic. (how to clear statement cache, refer to http://docs.oracle.com/cd/E16764_01/apirefs.1111/e13952/taskhelp/jdbc/jdbc_datasources/ClearStatementCache.html)