1)Ioc
2)AOP
3)BeanFactory, DI
4)DispatcherServlet: handlerMapping,return ModelAndView,viewResolver
Struts:
1)ActionServlet
2)Struts-config.xml: FormBean,ActionMapping
3)Action.execute(),return ActionForward.
ibatis:
<beans> <bean id="sqlMapTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <bean id="sqlMapTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="sqlMapTransactionManager"/> </bean> <!--sql map --> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="configLocation" value="com/mydomain/data/SqlMapConfig.xml"/> <property name="dataSource" ref="dataSource"/> </bean> <bean id="dataSource" name="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@10.1.5.11:1521:XE"/> <property name="username" value="junshan"/> <property name="password" value="junshan"/> <property name="maxActive" value="20"/> </bean> <bean id="accountDAO" class="com.mydomain.AccountDAO"> <property name="sqlMapClient" ref="sqlMapClient"/> <property name="sqlMapTransactionTemplate" ref="sqlMapTransactionTemplate"/> </bean> </beans>
public class SimpleTest { public static void main(String[] args) { ApplicationContext factory = new ClassPathXmlApplicationContext("/com/mydomain/data/applicationContext.xml"); final AccountDAO accountDAO = (AccountDAO) factory.getBean("accountDAO"); final Account account = new Account(); account.setId(1); account.setFirstName("tao"); account.setLastName("bao"); account.setEmailAddress("[email protected]"); account.setDate(new Date()); try { accountDAO.getSqlMapTransactionTemplate().execute(new TransactionCallback(){ public Object doInTransaction(TransactionStatus status){ try{ accountDAO.deleteAccount(account.getId()); accountDAO.insertAccount(account); //account.setLastName("bobo"); //accountDAO.updateAccount(account); Account result = accountDAO.selectAccount(account); System.out.println(result); return null; } catch (Exception e) { status.setRollbackOnly(); return false; } } }); //accountDAO.getSqlMapClient().commitTransaction(); } catch (Exception e) { e.printStackTrace(); } } }
关于spring+ibatis sharding方案的实现:
1)根据ID和路由规则确定TargetDataSource
因为一般DaoImpl会下面这样实现,因此可以写一个类ShardSqlMapClientDaoSupport继承SqlMapClientDaoSupport,然后根据ID和路由规则确定TargetDataSource,调用setDataSource(TargetDataSource)方法。这样数据源的路由问题(就是分库)就解决了。
2)接下来看怎么实现sql中的分表。
a 首先根据id和路由规则确定表名table
b 因为如下面代码所示SqlMapClientTemplate的insert和queryForObject等方法都是通过callback来做的,这里只传一个statementName过去,具体的跟statement替换变量相关的代码都在ibatis包里,所以这里替换表名的工作还不是很好做。
c 那怎么办呢?通过搜索看到ibatis从3.0开始提供了Plugin接口,可以对执行的sql进行拦截,这样就可以把表名根据规则替换上去了。但是由于我们使用的是ibatis早期版本,升级的代价比较大,因此最后只在某些新项目使用了这个分表逻辑。
关于Plugin怎么使用看我的收藏夹最新文章。
3)如果查询条件中不含id,比如根据name查或动态条件查,特别是可能还需要分页,排序,group by,count等会比较麻烦,这种情况只能把sql发送到各个dataSource然后在应用端进行Merge。 没办法,Sharding是有代价的。
package com.lanp.dao; import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport; import com.lanp.beans.Student; public class StudentDaoImpl extends SqlMapClientDaoSupport implements StudentDao { @Override public Student getStudent(String name) { try{ return (Student)getSqlMapClientTemplate().queryForObject("queryStudentById", name); } catch(Exception e) { e.printStackTrace(); } return null; } }
public Object queryForObject(final String statementName, final Object parameterObject) throws DataAccessException { return execute(new SqlMapClientCallback() { public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { return executor.queryForObject(statementName, parameterObject); } }); }
public Object insert(final String statementName, final Object parameterObject) throws DataAccessException { return execute(new SqlMapClientCallback() { public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException { return executor.insert(statementName, parameterObject); } }); }