On this example, im trying to set mybatis default isolation level, but before i go further let me explain a little why we need to setup an isolation level.
In a typical application, multiple transactions run concurrently, often working with the same data to get their job done. Concurrency, while necessary, can lead to the following problems:
Dirty read—Dirty reads occur when one transaction reads data that has been written but not yet committed by another transaction. If the changes are later rolled back, the data obtained by the first transaction will be invalid.
Nonrepeatable read—Nonrepeatable reads happen when a transaction performs the same query two or more times and each time the data is different. This is usually due to another concurrent transaction updating the data between the queries.
Phantom reads—Phantom reads are similar to nonrepeatable reads. These occur when a transaction (T1) reads several rows, and then a concurrent transaction (T2) inserts rows. Upon subsequent queries, the first transaction (T1) finds additional rows that were not there before.
That’s why on some mission critical application, i always set Isolation Level on Serializable. Why i use Serializable because this fully ACID-compliant isolation level ensures that dirty reads, nonrepeatable reads, and phantom reads are all prevented. This is the slowest of all isolation levels because it is typically accomplished by doing full table locks on the tables involved in the transaction.
Okay, here is how to achieve Serializable Isolation Level using MyBatis,
import com.edw.mybatis.bean.Testing;
import com.edw.mybatis.config.MyBatisSqlSessionFactory;
import com.edw.mybatis.mapper.TestingMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.log4j.Logger;
public class Main {
private static Logger logger = Logger.getLogger(Main.class);
public static void main(String[] args) {
SqlSessionFactory sqlSessionFactory = MyBatisSqlSessionFactory.getSqlSessionFactory();
SqlSession sqlSession = sqlSessionFactory.openSession(TransactionIsolationLevel.SERIALIZABLE);
TestingMapper testingMapper = sqlSession.getMapper(TestingMapper.class);
try {
Testing testing = new Testing();
testing.setName("name 1");
testing.setAddress("address 1");
int success = testingMapper.insert(testing);
logger.debug(success);
sqlSession.commit();
} catch (Exception e) {
logger.error(e, e);
sqlSession.rollback();
} finally {
sqlSession.close();
}
}
}
Hope this piece of code could help others, have fun with other TransactionIsolationLevel parameters, such as READ_COMMITTED or REPEATABLE_READ.
Perhaps there would be some people wondering, why should i use expiry date on my static files? Well for a first-time visitor to your page may have to make several HTTP requests to load all your web page’s content, but by using the Expires header you make those components cacheable. This avoids unnecessary HTTP requests on subsequent page views. Expires headers are most often used with images, but they should be used on all components including scripts, stylesheets, and Flash components.
But dont forget, using a far future Expires header affects page views only after a user has already visited your site. It has no effect on the number of HTTP requests when a user visits your site for the first time and the browser’s cache is empty. Therefore the impact of this performance improvement depends on how often users hit your pages with a primed cache.
Okay, so basically i use a simple ServletFilter to add Expiry header on each static contents, on this example would be .css, .png and .gif files. So here is my code,
package com.edw.fw.server.filter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
public class ExpiryFilter implements Filter {
// add a five years expiry
private Integer years = 5;
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
if (years > -1) {
Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.add(Calendar.YEAR, years);
// HTTP header date format: Thu, 01 Dec 1994 16:00:00 GMT
String o = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss zzz")
.format(c.getTime());
((HttpServletResponse) response).setHeader("Expires", o);
}
chain.doFilter(request, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
And dont forget to register your Filter to your web.xml file
<!-- expiration date filter --> <filter> <description>Set cache expiry for static content</description> <filter-name>ExpiryFilter</filter-name> <filter-class>com.edw.fw.server.filter.ExpiryFilter</filter-class> </filter> <filter-mapping> <filter-name>ExpiryFilter</filter-name> <url-pattern>*.css</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping> <filter-mapping> <filter-name>ExpiryFilter</filter-name> <url-pattern>*.png</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping> <filter-mapping> <filter-name>ExpiryFilter</filter-name> <url-pattern>*.gif</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping>
This is what it looks like on my browser’s http log,

as you can see, my http request get 304 not modified header, due to accessing my browser’s cache instead of the targetted web page.
Okay, i hope this helped other. Have Fun
Another exception happened to me today while im deploying my application to Tomcat 7, somehow the error (again) never happen on my IDE. The error is related to Spring MVC’s @PathVariable. Below is the complete stacktrace for the error.
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: Name for argument type 1 not available, and parameter name information not found in class file either. org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894) org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778) javax.servlet.http.HttpServlet.service(HttpServlet.java:621) javax.servlet.http.HttpServlet.service(HttpServlet.java:722) com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119) com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55) org.springframework.security.web.FilterChainProxy$VirtualFilterChain .doFilter(FilterChainProxy.java:330) org.springframework.security.web.access.intercept.FilterSecurityInterceptor .invoke(FilterSecurityInterceptor.java:118) org.springframework.security.web.access.intercept.FilterSecurityInterceptor .doFilter(FilterSecurityInterceptor.java:84)
And this is the suspected method which raise exception,
@RequestMapping(value ="/baculsoft/news/view/{id}", method = RequestMethod.GET)
public String newsView(ModelMap modelMap,
@PathVariable String id) {
modelMap.put("news", newsService.get(id);
return "news/view";
}
The workaround is actually simple, below is how to deal with it,
@RequestMapping(value ="/baculsoft/news/view/{id}", method = RequestMethod.GET)
public String newsView(ModelMap modelMap,
@PathVariable("id") String id) {
modelMap.put("news", newsService.get(id);
return "news/view";
}
Actually very simple workaround, but since i have hundreds of methods using @PathVariable, it’s not so simple anymore. And the weird thing is, the .war exported from Eclipse IDE run perfectly, only .war created from Netbeans that raise exception. I dont know why, but somehow Netbeans’ ant create a different war compared to Eclipse’s war file.
After sometimes googling, i found out that it happens due to javac’s debug parameter on Netbeans’ ant, changing “debug” default value into “on” on Netbeans’ build-impl.xml makes my war file run perfectly on Tomcat 7. Well i hope it hepled others, cheers
Today i found an exception while deploying my .war application to Tomcat web server for production usage, very weird because i never had this exception on my IDE before. This is the complete exception stacktrace,
Caused by: java.lang.IllegalArgumentException: Filter mapping specifies an unknown filter name FilterName at org.apache.catalina.core.StandardContext.addFilterMap(StandardContext.java:2506) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source)
below is my web.xml content,
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="crm" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_4.xsd">
<display-name>edw</display-name>
<context-param>
<param-name>edw</param-name>
<param-value>edw</param-value>
</context-param>
<filter-mapping>
<filter-name>FilterName</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>FilterName</filter-name>
<filter-class>com.edw.filter.FilterName</filter-class>
</filter>
</web-app>
How to fix it is actually very simple, i only need to swap between
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="crm" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_4.xsd">
<display-name>edw</display-name>
<context-param>
<param-name>edw</param-name>
<param-value>edw</param-value>
</context-param>
<filter>
<filter-name>FilterName</filter-name>
<filter-class>com.edw.filter.FilterName</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterName</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Weird eh