Lately I was refactoring a application that acceses DB. Due it was using plain JDBC I decided to reimplement it using iBatis instead of Hibernate as I would do if starting from scratch.
I have used iBatis 1.x before, and now I decided to use the latest 2.0, and the Spring support.
I was keen to see that it was like using Hibernate support, very easy. In fact is easier to understand how things work than using Hibernate.
While reading the Spring reference guide I saw that the iBatis section didn’t cover most changes in
version 2, so I decided to complete it. You can read the full section here until Spring people update
it.
iBATIS
Through the org.springframework.orm.ibatis
package, Spring supports iBATIS SqlMaps 1.3.x and 2.0.x. The iBATIS
support much resembles Hibernate support in that it supports the same
template style programming and just as with Hibernate, iBatis support
works with Spring’s exception hierarchy and let’s you enjoy the all IoC
features Spring has.
Spring supports both iBATIS SqlMaps 1.3 and 2.0. First let’s have a look at the differences between the two.
The
xml config files have changed a bit, node and attribute names. Also the
Spring clases you need to extend are different, as some method names.
Table 1.1. iBATIS SqlMaps supporting classes for 1.3 and 2.0
| Feature | 1.3.x | 2.0 |
|---|---|---|
| Creation of SqlMap | SqlMapFactoryBean | SqlMapClientFactoryBean |
| Template-style helper class | SqlMapTemplate | SqlMapClientTemplate |
| Callback to use MappedStatement | SqlMapCallback | SqlMapClientCallback |
| Super class for DAOs | SqlMapDaoSupport | SqlMapClientDaoSupport |
Using
iBATIS SqlMaps involves creating SqlMap configuration files containing
statements and result maps. Spring takes care of loading those using
the SqlMapFactoryBean.
public class Account {
private String name;
private String email;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
}
Suppose
we would want to map this class. We’d have to create the following
SqlMap. Using the query, we can later on retrieve users through their
email addresses. Account.xml:
<sql-map name="Account"> <result-map name="result" class="examples.Account"> <property name="name" column="NAME" columnIndex="1"/> <property name="email" column="EMAIL" columnIndex="2"/> </result-map> <mapped-statement name="getAccountByEmail" result-map="result"> select ACCOUNT.NAME, ACCOUNT.EMAIL from ACCOUNT where ACCOUNT.EMAIL = #value# </mapped-statement> <mapped-statement name="insertAccount"> insert into ACCOUNT (NAME, EMAIL) values (#name#, #email#) </mapped-statement> </sql-map>
After having defined the Sql Map, we have to create a configuration file for iBATIS (sqlmap-config.xml):
<sql-map-config> <sql-map resource="example/Account.xml"/> </sql-map-config>
iBATIS loads resources from the classpath so be sure to add the Account.xml file to the classpath somewhere.
Using Spring, we can now very easily set up the SqlMap, using the SqlMapFactoryBean:
<bean id="sqlMap" class="org.springframework.orm.ibatis.SqlMapFactoryBean"> <property name="configLocation"><value>WEB-INF/sqlmap-config.xml</value></property> </bean>
The SqlMapDaoSupport class offers a supporting class similar to the HibernateDaoSupport and the JdbcDaoSupport types. Let’s implement a DAO:
public class SqlMapAccountDao extends SqlMapDaoSupport implements AccountDao {
public Account getAccount(String email) throws DataAccessException {
return (Account) getSqlMapTemplate().executeQueryForObject("getAccountByEmail", email);
}
public void insertAccount(Account account) throws DataAccessException {
getSqlMapTemplate().executeUpdate("insertAccount", account);
}
}
As you can see, we’re using the SqlMapTemplate to execute the
query. Spring has initialized the SqlMap for us using the
SqlMapFactoryBean and when setting up the SqlMapAccountDao as follows,
you’re all set to go:
<!-- for more information about using datasource, have a look at the JDBC chapter -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<bean id="accountDao" class="example.SqlMapAccountDao">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="sqlMap"><ref local="sqlMap"/></property>
</bean>
It’s
pretty easy to add declarative transaction management to applications
using iBATIS. Basically the only thing you need to do is adding a
transaction manager to you application context and declaratively set
your transaction boundaries using for example the TransactionProxyFactoryBean. More on this can be found in ???
TODO elaborate!
If we want to map the previous Account class with iBATIS 2 we need to create the following SqlMap Account.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE sqlMap PUBLIC "-//iBATIS.com//DTD SQL Map 2.0//EN" "http://www.ibatis.com/dtd/sql-map-2.dtd"> <sqlMap namespace="Account"> <resultMap id="result" class="examples.Account"> <result property="name" column="NAME" columnIndex="1"/> <result property="email" column="EMAIL" columnIndex="2"/> </resultMap> <select id="getAccountByEmail" resultMap="result"> select ACCOUNT.NAME, ACCOUNT.EMAIL from ACCOUNT where ACCOUNT.EMAIL = #value# </select> <insert id="insertAccount"> insert into ACCOUNT (NAME, EMAIL) values (#name#, #email#) </insert> </sqlMap>
The configuration file for iBATIS 2 changes a bit (sqlmap-config.xml):
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE sqlMapConfig PUBLIC "-//iBATIS.com//DTD SQL Map Config 2.0//EN" "http://www.ibatis.com/dtd/sql-map-config-2.dtd"> <sqlMapConfig> <sqlMap resource="example/Account.xml"/> </sqlMapConfig>
Remember that iBATIS loads resources from the classpath so be sure to add the Account.xml file to the classpath somewhere.
We can use the SqlMapClientFactoryBean in the Spring application context :
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation"><value>WEB-INF/sqlmap-config.xml</value></property> </bean>
The SqlMapClientDaoSupport class offers a supporting class similar to the SqlMapDaoSupport. We extend it to implement our DAO:
public class SqlMapAccountDao extends SqlMapClientDaoSupport implements AccountDao {
public Account getAccount(String email) throws DataAccessException {
Account acc = new Account();
acc.setEmail();
return (Account)getSqlMapClientTemplate().queryForObject("getAccountByEmail", email);
}
public void insertAccount(Account account) throws DataAccessException {
getSqlMapClientTemplate().update("insertAccount", account);
}
}
In the DAO we use the SqlMapClientTemplate to execute the
queries, after setting up the SqlMapAccountDao in the application
context:
<!-- for more information about using datasource, have a look at the JDBC chapter -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName"><value>${jdbc.driverClassName}</value></property>
<property name="url"><value>${jdbc.url}</value></property>
<property name="username"><value>${jdbc.username}</value></property>
<property name="password"><value>${jdbc.password}</value></property>
</bean>
<bean id="accountDao" class="example.SqlMapAccountDao">
<property name="dataSource"><ref local="dataSource"/></property>
<property name="sqlMapClient"><ref local="sqlMapClient"/></property>
</bean>
Spring Framework 1.1.5 released
The Spring Framework has released the 1.1.5 version, the last one before 1.2 release candidate comes out with major improvements as Hibernate 3 and JMX support. As with previous versions I have uploaded it to the maven repository at ibiblio, so maven users can download automatically.
Hi everybody,
I’m pleased to announce that Spring Framework 1.1.5 has just been released. This is the last bug fix and minor enhancement release in the 1.1.x series, featuring many minor improvements such as:
* added overloaded "reject" and "rejectValue" methods without default message to Errors interface and BindException
* added "lookup(name, requiredType)" convenience method to JndiTemplate, matching the JNDI object against the given type
* added "homeInterface" property to AbstractRemoteSlsbInvokerInterceptor, for specifying the home interface to narrow to
* introduced MailMessage interface as common interface for SimpleMailMessage and JavaMail MIME messages
* Log4jConfigurer accepts a "classpath:" URL or a "file:" URL as location too, not just a plain file path
* Log4jConfigurer accepts config files that do not reside in the file system, as long as there is no refresh interval
* added "int[] batchUpdate(String[] sql)" method to JdbcTemplate, for executing a group of SQL statements as a batch
* added C3P0NativeJdbcExtractor for C3P0 0.8.5 or later (for earlier C3P0 versions, use SimpleNativeJdbcExtractor)
* added "maxRows" bean property to JdbcTemplate, allowing to specify the maximum number of rows to be fetched
* added "fetchSize" and "maxRows" bean properties to RdbmsOperation, passing the values to the internal JdbcTemplate
* added ClobStringTypeHandler, BlobByteArrayTypeHandler and BlobSerializableTypeHandler for iBATIS SQL Maps 2.0.9
* ResourceHolderSupport throws TransactionTimedOutException if no time-to-live left (before attempting an operation)
* TransactionSynchronization objects can influence their execution order through implementing the Ordered interface
* JtaTransactionManager is able to work with a JTA TransactionManager only (i.e. without a UserTransaction handle)
* upgraded MockHttpServletRequest to Servlet API 2.4 (added getRemotePort, getLocalName, getLocalAddr, getLocalPort)
* upgraded MockPageContext to JSP API 2.0 (added getExpressionEvaluator, getVariableResolver, overloaded include)
* added "contextOverride" option to ServletContextPropertyPlaceholderConfigurer, letting web.xml override local settings
* added "searchContextAttributes" option to ServletContextPropertyPlaceholderConfigurer, resolving context attributes
* added "clear" and "isEmpty" methods to ModelAndView, allowing to clear the view of a given ModelAndView object
* added JasperReportsMultiFormatView, allowing to specify the output format dynamically via a discriminator in the model
* JSP EL expressions in Spring’s JSP tags will be parsed with JSP 2.0 ExpressionEvaluator on JSP 2.0 (Jakarta JSTL else)
* changed "spring:transform" tag’s "value" attribute from String to Object, to allow for expressions resolved by JSP 2.0
See the changelog for details.
Our next milestone is 1.2 RC1, which we intend to release as soon as possible: with Hibernate3 support, JMX support and further major new features. Nightly 1.2-dev snapshots with Hibernate3 support and JMX support will be available within a few days, so feel free to give 1.2 an early try 🙂
Cheers,
Juergen
Acegi Security: reducing configuration in web.xml
Until now, to use Acegi Security System for Spring in your web application you needed to add at least three filters and filtermappings to your web.xml, eg. to secure an application using form based authentication these lines had to be present in every web.xml:
<filter>
<filter-name>Acegi Authentication Processing Filter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.ui.webapp.AuthenticationProcessingFilter</param-value>
</init-param>
</filter>
<filter>
<filter-name>Acegi Security System for Spring Http Session Integration Filter</filter-name>
<filter-class>net.sf.acegisecurity.ui.webapp.HttpSessionIntegrationFilter</filter-class>
</filter>
<filter>
<filter-name>Acegi HTTP Request Security Filter</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.intercept.web.SecurityEnforcementFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi Authentication Processing Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Acegi Security System for Spring Http Session Integration Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
filter-mapping>
<filter-name>Acegi HTTP Request Security Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
With the latest changes in CVS (thanks Ben) you only need to add one filter and filter mapping to web.xml:
<filter>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<filter-class>net.sf.acegisecurity.util.FilterToBeanProxy</filter-class>
<init-param>
<param-name>targetClass</param-name>
<param-value>net.sf.acegisecurity.util.FilterChainProxy</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And a bean definition to the Spring application context, specifying the actual filters and the urls to map.
<bean id="filterChainProxy"
class="net.sf.acegisecurity.util.FilterChainProxy">
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
PATTERN_TYPE_APACHE_ANT
/**=authenticationProcessingFilter,httpSessionIntegrationFilter,securityEnforcementFilter
</value>
</property>
</bean>
This approach allow you to reuse the bean across all your applications, as it won’t change if you’re using the same authentication schema (eg. form based). As a sideeffect also allows using ant patterns or regular expresions in the url mappings.
Hibernate vs JDBC == Maven vs Ant
While coding with JDBC directly provides powerful posibilities I
think no one could argue that it’s better coding at such low level in
the vast majority of the cases.
I think Maven does the same to
build systems. Maven doesn’t substitute Ant, abstracts and simplifies
it. For some cases you’ll still need to write Ant build files inside
Maven, as you can write SQL inside hibernate, but you will have the
power from both worlds.
Maven has made a risky bet, as Hibernate
has done, but fortunately both achieved a growing community. Currently
there are too much people asking for features compared to those
implementing them, so I encourage any of you to take the bull by the
horns and become actively involved.
And you should never forget
that behind high level tools and technologies there’re always low level
ones (Maven – Ant, Hibernate – JDBC, Struts – Servlets,…)
Five maven features you must love
For those Maven haters out there there are five features that they must
love. Sure they may not like the implementation, but it can be
improved, the most important thing is the idea.
- Automatic downloading of dependencies.
- A consistent and standarized directory layout
- A consistent naming of goals (targets): war, jar, javadoc,…
- AOP like chain of goals (before invocation and after invocation pointcuts)
- A declarative descriptor of dependencies and project settings
They can be implemented using Ant, but why reinvent the wheel?
Spring Framework 1.1.4 released
Juergen Hoeller has announced the release of Spring Framework 1.1.4
Among the new features are:
* added LazyInitTargetSource, lazily accessing a singleton from a BeanFactory (lazily initializing on first call)
* added ServiceLocatorFactoryBean, allowing to map custom service locator interface methods to BeanFactory.getBean calls
* reworked ResourcePatternResolver to extend ResourceLoader, for ResourcePatternResolver checks in ResourceLoaderAware
* made BindException serializable, provided that the contained target object is serializable
* added LazyConnectionDataSourceProxy, for lazily fetching JDBC Connections with native JDBC or Hibernate transactions
* added "Sybase-jConnect" to default sql-error-codes.xml file, for database product name "Adaptive Server Enterprise"
* added overloaded "queryForList"/"queryForObject"/"queryForLong"/"queryForInt" methods with arg types to JdbcTemplate
* added "alwaysUseNewSession" flag to HibernateTemplate, enforcing a new Session even in case of a pre-bound Session
* HibernateTemplate proxies exposed Sessions by default, applying query cache settings and transaction timeouts
* added "isConnectFailure(RemoteException)" hook to AbstractRemoteSlsbInvokerInterceptor, for customized failure checks
* added "isConnectFailure(RemoteException)" hook to (Jndi)RmiClientInterceptor, for customized connect failure checks
* added JaxRpcServicePostProcessor interface, intended for reusable custom type mappings etc for a JAX-RPC service
* added "servicePostProcessors" property to LocalJaxRpcServiceFactory and subclasses (incl. JaxRpcPortProxyFactoryBean)
* added "messageIdEnabled" and "messageTimestampEnabled" properties to JmsTemplate, to disable id/timestamp on producer
* added "pubSubNoLocal" property to JmsTemplate, leading to the NoLocal flag being specified on MessageConsumer creation
* added "receiveSelected" and "receivedSelectedAndConvert" methods to JmsTemplate, accepting JMS message selectors
* added "schedulerListeners", "(global)JobListeners", "(global)TriggerListeners" bean properties to SchedulerFactoryBean
* added "jobListenerNames"/"triggerListenerNames" property to JobDetailBean, CronTriggerBean, SimpleTriggerBean (resp.)
* added ServletContextAttributeFactoryBean, exposing an existing ServletContext attribute for bean references
* added ServletContextAttributeExporter, taking Spring-defined objects and exposing them as ServletContext attributes
* added ServletContextPropertyPlaceholderConfigurer, a subclass that falls back to web.xml context-param entries
* added "publishEvents" init-param to FrameworkServlet, allowing to turn off the publishing of RequestHandledEvents
* Spring JSP tags work outside DispatcherServlet too, falling back to root WebApplicationContext and JSTL/request locale
Acegi Security System for Spring benchmark
I’ve recently used Acegi for a simple application and as I wanted to know how
much it loads the webapp I used JMeter to benchmark it. I’ve just spent less
than 30 min. to setup everything, so yake it as is, just a small idea of what
involves adding the Acegi filters to process the requests.
What I have used:
- Tomcat 4.0.6
- Acegi 0.7.0
- JMeter 2.0.2
Acegi filters defined in web.xml:
- A ProcessingFilter made by myself
- HttpSessionIntegrationFilter
- SecurityEnforcementFilter
Here there are the results of three tries, each one with the number of threads,
the ramp-up (delay between threads in seconds), and the loops (number of times
the test was executed). The url tested was not secured.
| threads | ramp-up | loop | ||
| 50 | 1 | 10 | ||
| average (ms.) | deviation (ms.) | |||
| Using Acegi | 107 | 47 | 15% | |
| Not using Acegi | 93 | 82 | ||
| threads | ramp-up | loop | |
| 50 | 1 | 100 | |
| average (ms.) | deviation (ms.) | ||
| Using Acegi | 233 | 137 | 13% |
| Not using Acegi | 206 | 125 | |
| threads | ramp-up | loop | |
| 5 | 1 | 1000 | |
| average (ms.) | deviation (ms.) | ||
| Using Acegi | 15 | 12 | 67% |
| Not using Acegi | 9 | 12 | |
You can see that the overhead of using Acegi is about 14% increase of access
time in every page when the load is high. When it’s low, the overhead is
higher, but not relevant.
ONess 0.5 bundled with Tomcat
After noticing that the compressed file was corrupt I’ve make available again the latest ONess version 0.5 bundled with Tomcat 5.0.30.
As I’ve said in a previous entry, it’s ready to run in less than a minute and uses hsqldb to avoid the need of setting up a database.
You can download from Sourceforge, uncompress, go to the “bin” dir and run startup.bat or startup.sh. After tomcat is up and running you can go to http://localhost:8080/ to check the web interface.
Acegi Security release 0.7.0 is out
The long awaited 0.7.0 relase of Acegi Security System for Spring is out. I’m happy to have contributed (just a bit) to this great project. And it is built with maven!!
Dear Spring Community
I’m pleased to announce the Acegi Security System for Spring release 0.7.0 is now available from http://acegisecurity.sourceforge.net. The project provides comprehensive security services for The Spring Framework. You can read about the features in detail at http://acegisecurity.sourceforge.net.
There are many changes, improvements and fixes in release 0.7.0 (as listed at http://acegisecurity.sourceforge.net/changes-report.html). The major new feature areas are:
* Significant improvements to ACL security services
* AspectJ support (useful for instance-level security)
* Refactoring of ObjectDefinitionSources (especially useful for web URI security)
* Automatic propagation of security identity via RMI and HttpInvoker
* Integration with Servlet Spec’s getRemoteUser()
* Refactoring of Contacts sample to use the new ACL security services
* Additional event publishing (now includes authorisation, not just authentication)
* CVS restructure to use Maven as the build system
* A new project web site with FAQs, links to external articles etc
The new ACL security services deserve special mention, as they make it possible to develop applications that require complex instance-based security without any custom code. The entire configuration of such applications can be declared in the IoC container using standard Acegi Security services, so this should help significantly improve architecture and development time.
As per the Apache APR project versioning guidelines, this is a major release. We expect the next major release will be 1.0.0, although release 0.7.0 should be considered stable enough for most projects to use. There are detailed upgrade instructions included in the release ZIP and on the Acegi Security home page.
For Maven users, Acegi Security’s latest JARs are available from http://acegisecurity.sourceforge.net/maven/acegisecurity/jars. We will also be adding release 0.7.0 and above to iBiblio.
We hope you find this new release useful in your projects.
Best regards
Ben
An evening with Googles Marissa Mayer
Alan Williamson posts a very interesting entry about a presentation from Marissa Mayer (Product Manager for Google).
- The prime reason the Google home page is so bare is due to the fact that the founders didn’t know HTML and just wanted a quick interface. Infact it was noted that the submit button was a long time coming and hitting the RETURN key was the only way to burst Google into life.
- Due to the sparseness of the homepage, in early user tests they noted people just sitting looking at the screen. After a minute of nothingness, the tester intervened and asked ‘Whats up?’ to which they replied "We are waiting for the rest of it". To solve that particular problem the Google Copyright message was inserted to act as a crude end of page marker.
- One of the biggest leap in search usage came about when they introduced their much improved spell checker giving birth to the "Did you mean…" feature. This instantly doubled their traffic, but they had some interesting discussions on how best to place that information, as most people simply tuned that out. But they discovered the placement at the bottom of the results was the most effective area.
- The infamous "I feel lucky" is nearly never used. However, in trials it was found that removing it would somehow reduce the Google experience. Users wanted it kept. It was a comfort button.
- Orkut is very popular in Brazil. Orkut was the brainchild of a very intelligent Google engineer who was pretty much given free reign to run with it, without having to go through the normal Google UI procedures, hence the reason it doesn’t look or feel like a Google application. They are looking at improving Orkut to cope with the loads it places on the system.
- Google makes changes small-and-often. They will sometimes trial a particular feature with a set of users from a given network subnet; for example Excite@Home users often get to see new features. They aren’t told of this, just presented with the new UI and observed how they use it.
- Google has the largest network of translators in the world
- They use the 20% / 5% rules. If at least 20% of people use a feature, then it will be included. At least 5% of people need to use a particular search preference before it will make it into the ‘Advanced Preferences’.
- They have found in user testing, that a small number of people are very typical of the larger user base. They run labs continually and always monitoring how people use a page of results.
- The name ‘Google’ was an accident. A spelling mistake made by the original founders who thought they were going for ‘Googol’
- Gmail was used internally for nearly 2years prior to launch to the public. They discovered there was approximately 6 types of email users, and Gmail has been designed to accommodate these 6.
- They listen to feedback actively. Emailing Google isn’t emailing a blackhole.
- Employees are encouraged to use 20% of their time working on their own projects. Google News, Orkut are both examples of projects that grew from this working model.
- This wasn’t a technical talk so no information regarding any infrastructure was presented however they did note that they have a mantra of aiming to give back each page with in 500ms, rendered.
- Quote: Give Users What They Want When They Want It
- Quote: Integrate Sensibly