您的位置:首页 > 其它

ibatis

2016-02-25 16:08 302 查看
配置

applicationContext.xml

<bean id = "sqlmapClientBuilder" class = "SqlMapClientFactoryBean">
<property name="dataSource" ref="mysqldbDataSource"/>
<property name="sqlMapConfigResource" value="sqlmapConfig.xml"></property>
</bean>
//处理FactoryBean
<bean id="mysqldbSqlMapClient" factory-bean="sqlmapClientBuilder" factory-method="get">


sqlmapConfig.xml

<sqlMapConfig>
<setting  cacheModelsEnabled="true" statementCachingEnabled = "true" classInfoCacheEnabled="true" />
<sqlMap resource="com/xianling/bus/id/entity/sqlmap/IdGen.xml"/>
</sqlMapConfig>


SqlMapClientFactoryBean 初始化

因为他是个FactoryBean,所以真正的类型在get(sqlMapClient)方法中

他又实现InitializingBean

因此,实例化这个对象后,初始化afterPropertiesSet()方法

主要是创建 SqlMapClient 和事务(当没有指定什么事务时,就会创建一个空的事务,里面的方法都是空的)

SqlMapClient sqlMapClient;
Class transactionConfigClass = ExternalTransactionConfig.class;
public void afterPropertiesSet() {
sqlMapClient = buildSqlMapClient(…);//解析xml.
if(dataSource != null) {
TransactionConfig config = transactionConfigClass.newInstance(); //
DataSource toUse = new TransactionAwareDataSourceProxy (dataSource);
config.setDataSource(toUse);
config.initialize(transactionConfigProperties);            Config.setMaximunConcurrentTransactions(sqlMapClient.getDelegate().getMaximunConcurrentTransactions());
sqlMapClient.getDelegate().setTxManager(new TransactionManager(config) );
}
protected SqlMapClient buildSqlMapClient(
Resource[] configLocations,Properties properties){
SqlMapClient client = null;
SqlMapConfigParser configParser = new SqlMapConfigParser();
for (int i = 0; i < configLocations.length; i++) {
InputStream is = configLocations[i].getInputStream();
client = configParser.parse(is, properties);
}
return
4000
client;
}


TransactionAwareDataSourceProxy 这个主要是getConnection()中,创建代理对象

拦截的是

1) close() :DataSourceUtils.doReleaseConnection(con,dataSource);

2) getTargetConnection() : DataSourceUtils.doGetConnection(dataSource) ;

3) 别的方法: 这个方法执行后,DataSourceUtils. doReleaseConnection(con,dataSource)

以下是重要类的属性和流程图



下面是解析的流程图



介绍下解析类NodeletParser

这里主要存放一些处理节点的接口

这个process里面就是处理节点的

public interface Nodelet {
void process (Node node) throws Exception;
}


NodeletParser {
Map<String,Nodelet> letMap = new HashMap();
public void addNodelet(String xpath, Nodelet nodelet) {
letMap.put(xpath, nodelet);
}
public void parse(Reader reader) throws NodeletException {
parse(createDocument(reader).getLastChild());
//遍历他的字节点,根据节点路径找到Nodelet来调用他的process(node)
}
}
}


简介SqlMapConfigParser解析sqlConfig.xml文件

1) delegate = new SqlMapExecutorDelegate()

2)mapClient = new SqlMapClientImpl()

3) 处理xml中的各个节点

下面来简述下 ibatis的缓存

<cacheModel id="user-cache" type="LRU" readOnly="false" serialize="true">                                         <flushInterval hours="24" />
<flushOnExecute statement="getUser" />
</cacheModel>


当在解析语句的时候insert, update sql等时,当这个语句是有缓存的时候就把他

Public MappedStatement SqlStatement.parseGenernalStatement(Node,GeneralStatement) {
cacheName = node.getAttribute(“cacheMode”);
….
If(cacheName != null)
Return new CachingStatement(statement, degate.getCacheMode(cacheName));
Return statement;
}


ibatis缓存

下面来介绍下普通的

1) GenernalStatement 如果增删改的时候,和这实体类有关的缓存都清除

Public int executeUpdate(RequestScope, Transaction, parameter) {
Request.getSession().setCommitRequired(true);//这个事务是可以提交的。
//如果有一起批量更新的话,就加入到批量更新中;否则直接更新
If(request.getSession().isInBatch())
sqlExecutor.excuteUpdate(request,con,sql,para);
else
sqlExecutor.addBatch request,con,sql,para);
sql.cleanup(request);
notifyListeners();//通知清理缓存
}
Public void notifyListeners(){
for(CacleModel model:executeListemers) {
Model.onExecuteStatement(this); //缓存的清理cacheControll.flush()
}
}


2) CachingStatement :缓存语句:在查询的时候将查询结果放入进去

MappedStatement statement,CacheModel model;
Public int executeUpdate(…) {return statement.executeUpdate(…);}
Public executeQueryForList (…) {
CacheKey cacheKey = get CacheKey(…);
Object list = model.getOject(cacheKey);
if(list == null)
list = statement. executeQueryForList(…); model.putObject(cacheKey,list);
return list;
}


这个model放进去的时候有两种情况

//这里的type有FIFO、LRU、MEMORY、OSCACHE
<cacheModel id="user-cache" type="LRU" readOnly="false" serialize="true">
<flushInterval hours="24" />
<flushOnExecute statement="getUser" />
<property value="600" name="size" />                                                         </cacheModel〉
parser.addNodelet(“/sqlMapConfig/end()”,new Nodelet(){
Public void process(Node n){
For(CacheModel model : delegate.getCacheModes().valueSet()){
For(String key:model.getFlushTriggerStatementNames()) {
MappedStatement statement = delegate.get MappedStatement(key);
Statement.addExecuteListener(model);
}
}}}


总结: 在增删改时,会清理缓存 ; 查询的时候,插入到缓存中

下面重点来说下缓存


<sqlMap>
<cacheModel id="user-cache" type ="LRU" readOnly="true" serialize="false">
<flushInterval hours="24"/>
<flushOnExecute statement="updateUser"/>
<flushOnExecute statement="insertUser"/>
<flushOnExecute statement="deleteUser"/>
<property value="500" name="size"/>
</cacheModel>
<statement id="getProductList" cacheModel=" user-cache " >
select * from PRODUCT
</statement>
<statement id="getProduct" parameterClass="int" cacheModel=" user-cache" >
select * from PRODUCT where id = #value#
</statement>
</sqlMap>


观察者模式 ,即 语句是发布者,cachemodel是观察者。当语句被更改的时候,通知cachemodel来清理缓存中的数据(当这个实体类中的数据被修改时,对于这个实体类的缓存数据全部被删除;解析的时候语句封装成CachingStatement ,查询的时候直接从缓存中取出,如果没有数据再从数据库取出)

介绍下CacheModel:

CacheModel implements ExecuteListener{
CacheController controller;
//初始化的设置属性参数
Public void configure (props) { controller.configure(props);}
//清理缓存
Public void flush (){ Controller.flush(this); lastFlush = System.currentTime();}
Public Object getObject (CacheKey key) {
Object value = null;
Synchronized(this) {
If(System.currentTime() – latFlush > flushInterval)  flush() ;
Value = controller.getObject(key);
If(serialize && !readOnly) {
Value = serializeOut(value) ;
}
}
Return value;
}
Public void putObject (CacheKey key,Object value) {
Synchronized(this) {
If(serialize && !readOnly) {
Value = serializeIn(value) ;
}
Controller.putObject(this,key,value);
}}

}


从缓存中获取数据时,超过清理时间就清理缓存。对于要序列化对象,在获取和插入时都序列化下.具体的怎么插入什么的还是要看是什么策略

缓存策略:

1) FIFO – FifoCacheController :这个遵循先进去缓存先被删除(短时间内使用过就不再使用情况下,建议使用)

插入:判断是否已经达到缓存中的最大值,就删除最早放进去的;插入到缓存中

获取:直接从数据中获取

Map cache = Collections.synchronizedMap(new HashMap();
int cacheSize = 100; List keys = Collections.syn..(new ..());
Public void configure (props) {
String size = props.getProperty(“cache-szie”);
Size = size != null? Size : props.getProperty(“size”);
cacheSize =  size != null? Size : cacheSize;
}
Public void putObject(CacheModel cachemodel, key, value) {
Cache.put(key, value); keys.add(key);
if(cache.szie() > cacheSize) {
String old = key.remove(key);
cache.remove(old);
}
}
Public Object getObject(CacheModel cachemodel, key) {
return cache.get(key);
}


2) LRU – LruCacheController :这个遵循近期使用最少的被删除,一个对象总是被使用,被删除的几率很少(长期时间内,用户经常使用这个对象 ,比如查询关键字翻页)

插入:判断是否已经达到缓存中的最大值,就删除最早放进去的;插入到缓存中

获取:放在最近的位置上,再获取

LruCacheController .. {
Configure和putObject 和FIFO的一样..
Public Object getObject(CacheModel cachemodel, key) {
Object value = cache.get(key);
keyList.remove(key); key.add(key);
return value;
}
}


3) MEMORY – MemoryController:

这个再等等 ,要看jvm java回收机制

根据reference-type来创建

SoftReference:内存不足时删除

WeakReference:java垃圾回收时,就删除

StrongReference

4) OSCACHE – OSCaheContoller :他是一个插件。以后再将

总结ibatis缓存: 1)在解析完xml后,把缓存
d790
放入到Statement的List中 2)如果这条语句是有缓存的话,那就包装成CachingStatement ,在查询后把结果放入到缓存中。增删改时,通知缓存去清理数据 3)缓存插入时,时会记录刷新的时间,如果这个是不可读时先序列化一下再放入缓存中。取出时,如果超过时间了,就清理缓存。是不可读时饭序列化一下 4) 缓存4大策略: I)fifo :先进先被删除 II) lru 最近很少被使用的就被删除 III) memory 引用策略 SoftReference:内存不足时删除,WeakReference:java垃圾回收时,就删除 IIII) 插件OSCache


数据源 ## 当然也可以不用这个自带的,直接在SqlMapClientFactoryBean的属性dataSource上

< transactionManager type ="JDBC" ><!-- 定义了ibatis的事务管理器有3中(JDBC,JTA,EXTERNAL) -->
< dataSource type ="SIMPLE" ><!-- type属性指定了数据源的链接类型,也有3种类型(SIMPLE,DBCP,JNDI) -->
< propertyname ="JDBC.Driver" value="" .... "/>
<!--  Pool.MaximumActiveConnections 连接池维持的最大容量 Pool.MaximumIdleConnections 连接   池允许挂起的最大连接 Pool.MaximumCheckoutTime 连接被某个任务所允许占用的最大时间 TimeToWait 线程允许等待的最大时间-->
</ dataSource >
</ transactionManager >


数据源有3种情况:SIMPLE,DBCP,JNDI

解析xml时,是根据DataSourceFactory来获取的

基本接口

DataSourceFactory{
Public void initialize(Map) ; public DataSource getDataSource();
}


1)JNDI :这个是搜索上下文来寻找的

属性名也可以是DBIndiContext、DBFullJndiContext

JndiDataSourceFactory {
DataSource dataSource;
public void initialize(Map){
IntialContxt c = new IntialContxt(map);
dataSource = c.lookup(map.get(“DataSource”)) ;
}
…
}


2)DBCP :BasicDataSource

DbcpDataSourceFactory{
DataSource dataSource;
public void initialize(Map){
DbcpConfiguration dbcp = new DbcpConfiguration (map);
dataSource = dbcp.getDataSource();
}
// 就是这个
//BasicDataSource bds = new ..();
//.. 设置属性
//Reurn dbs;
}


3) SimpleDataSourceFactory

SimpleDataSourceFactory {
DataSource dataSource;
public void initialize(Map){
dataSource = new SimpleDataSource(map);
}
}


这里的就大体上看下

   这里的dataSource.getConnection() 返回的是代理对象(因为他只能确定基类是Connection,不能确定客户到底使用的是哪个连接(驱动)) 。只拦截Connection的方法

   1)close() : datasource.pushConnection(this)将连接返回到连接池中

   2)其余方法则放过

使用的时候从方法池中取出,关闭的时候放入到连接池中。

   getConnection() 时(获取连接): 从连接池中取出

     一) 当在连接池中有空闲连接时, 直接从连接池中取出

     二) 创建连接

         I) 当使用中的连接<最大数时(还可以再创建时) ,直接去创建新的连接

         II) 取出最早的连接

          a) 当这个连接被占用的时间超时时,就从使用的连接池中删除,并包装成新的连接

          b) 线程等一会(等到有线程将连接放入到连接池中被唤醒)

   当这个连接可以使用时,就放入到使用的线程池中;否则计入到错误数中

   当这个错误数很大时,就抛异常。

   把这个连接放入到连接池中

      当这个连接可用

          如果连接池中的连接数 < 允许最大空闲数 重新包装 放入连接池

          否则 关闭连接

ibatis 事务

一般不用这个事务,因为这边的是一个insert一个事务,他没办法保证多个insert是在一个事务里。一般都用spring 事务DataSourceTransactionManager


- 初始化

   启动事务由2种

   1) 是不用ibatis推荐的三种dataSource

     直接在SqlMapClientFactoryBean中的afterProperties中

       如果 dataSource不为空时 

   2) 用自带的

      就在解析完sqlConfig.xml中中

      

TransactionConfig config = Class.forName(type ).newInstance();
Config.setDataSource(dataSource);config.setMaxImumConcurrentTransactions(..);
Config.initialize(txtProps);
TransactionManager m = new TransactionManager(config); m.setForceCommit(…))
Delegate.setTxManager(m)


  在介绍事务之前,先讲下Throttle

     

就是达到一定数量之后线程就开始等待,直到有线程把他唤醒

Throttle {
Int count,limit;
 final Object LOCK = new Object();
public void increment() {
synchronized(LOCK) {
if(count >= limit)
LOCK.wait();
Count++;
}
}
Public void decrement() {
synchronized(LOCK) {
LOCK.notify();Count --;
}
}}


TransactionManager :

简述下就是开始一个事务的时候(当事务数超出时,这个线程就一直在等待;直到有事务结束后将线程唤醒。事务结束时,当没有被提交,那么就回滚,关闭连接)

TransactionManager {
TransactionConfig config,Throttle t, Boolean forceCommit;
Public TransactionManager(config) {
…,t = new Throttle (config.getMaximumConcurrentTransactions());
}
// 开启事务
Public void begin (SessionScope session,int isolation) {
If(session.getTransactionState() == started || == user_provided) throw new ..
Transaction tran = null;
t.increment();
try{
tran = config.newTransaction(isolation); session.setCommitRequired(false);
}catch(…){
T.decrement();
}
Session.setTransaction(tran); Session.setTransactionState(started);
}
// 提交事务
Public void commit(SessionScope session) {
If(state != started && state != committed) throw new ..
If(session.isCommitRequired()  || forceCommit){
Session.getTransaction().commit(); session.setCommitRequired(false);
}
Session.setTransactionState(committed);
}
//结束事务
Public void end (SessionScope session) {
Transaction tran = session.getTransaction();
Try{ if(tran != null) {
If(state != committed) {
If(session.isCommitRequired() || forceCommiit)
tran.rollback(); session.setCommitRequired(false);
}
}

}finally {
Tran.close(); if(state != ended) t.decrement();
}
Session.setTransaction(null);
Session.setTransactionState(ended);
}


事务的三种方式:

1. jdbc – JdbcTransactionConfig: 就是在获取连接的时候将con.setAutoCommit(false);提交回滚都用con的

2. JTA – JtaTransactionConfig 用的是UserTransaction第三方插件

3. EXTERNAL –ExternalTransactionConfig (默认 ) 里面的提交回滚什么的都不操作

具体实现执行sql 

以下是执行sql示意图



总结: insert 流程  1. ThreadLocal 这个是一个线程一个变量 确保不会出现并发的行为 (只有在这个线程的SessionImpl被关闭了或者null才创建) 2.从线程池中取出Session ,然后开启一个事务 ,具体的和jdbc操作的交给别的 。提交事务 然后关闭事务,把这个session放入到线程池中,将这个线程的SessionImpl关闭

以下是简单的代码.

- SqlMapClientImpl

SqlMapClientImpl {
ThreadLocal<SqlMapSessionImpl> session = new ..();
Public Object insert (String id,Object param) {
Return getLocalSqlMapSession() .insert(id,param);
}
Public int update(String id,Object param) {
Return getLocalSqlMapSession().update(id,param);
}…….所有的都交给了SqlMapSessionImpl处理
Public void endTransaction() {
getLocalSqlMapSession().endTransaction();
getLocalSqlMapSession().close(); -- session回到池中
}
//这个是每个线程一个session变量,线程安全 。 当sessionImpl==null或者关闭时,创建
SqlMapSessionImpl  getLocalSqlMapSession() {
SqlMapSessionImpl sessionImpl = session.get();
if(sessionImpl == null || sessionImpl.isClosed()) {
sessionImpl = new .. (this);
session.set(sessionImpl);
}
return sessionImpl;
}

}
}


SqlMapSessionImpl ..{
SessionScope session ,SqlMapExecutorDelegate delegate,Boolean closed;
Public SqlMapSessionImpl(SqlMapClient client) {
Delegate = client.getDelegate(); session = delegate.popSession(); ..
}
Public Object insert(String id, param) {
Return delegate.insert(session,id,param);
}
Public void close()  {
delegate.pushSession(session);
Session = null; delefate = null; closed = true;
}
Public void startBatch(){ delegate.startBatch();}
}

}


SqlMapExecutorDelegate {
ThrottledPool  sessionPool = new ..(RequestScope.class, 128);
ThrottledPool requestPool = new ..(SessuinScope.class,512);

Public Object insert(SessionScope session,id ,param) {
Boolean autoStart = false;
Transanction tran =  getTransaction(session, autoStart);
MappedStatement ms = mappedStatements.get(id);
RequestScope request = popReuest (session,ms);
Ms.executeUpdate (request,tran,param);
pushRequest(request);
if(autoStart ) {
session.getSqlMapClient().commitTransaction();
session.getSqlMapClient().endTransaction();
}
}
Public void startBatch(SessionScope ) {
session.setInBatch(true);
}

Public void commitTransaction (SessionScope) {
If(session.isInBatch()) executeBatch(session);
sqlExecutor.cleanup(session); txManager.commit(session);
}
Public void endTransaction (SessionScope) {
sqlExecutor.cleanup(session); txManager.end(session);
}
Public void executeBatch(SessionScope) {
Session.setInBatch(false); sqlExecutor.executeBatch(session);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: