[原]Jackrabbit使用介绍

spring modules jcr

spring-modules-jcr 是jackrabbit与spring集成的第三方模块,可以简化JSR-170 API使用。连接:http://www.infoq.com/cn/articles/spring-modules-jcr

spring-modules-jcr主要是通过JcrTemplate中的execute方法操作jackrabbit的读写。在没有事务的环境下,每一个execute都使用独立的session,方法执行完后立即释放。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
* @see org.springmodules.jcr.JcrOperations#execute(org.springmodules.jcr.JcrCallback,
* boolean)
*/

public Object execute(JcrCallback action, boolean exposeNativeSession) throws DataAccessException {
Session session = getSession();
//判断当前是否在事务下
boolean existingTransaction = SessionFactoryUtils.isSessionThreadBound(session, getSessionFactory());
if (existingTransaction) {
logger.debug("Found thread-bound Session for JcrTemplate");
}

try {
Session sessionToExpose = (exposeNativeSession ? session : createSessionProxy(session));
Object result = action.doInJcr(sessionToExpose);
// TODO: does flushing (session.refresh) should work here?
// flushIfNecessary(session, existingTransaction);
return result;
}
catch (RepositoryException ex) {
throw convertJcrAccessException(ex);
// IOException are not converted here
}
catch (IOException ex) {
// use method to decouple the static call
throw convertJcrAccessException(ex);
}
catch (RuntimeException ex) {
// Callback code threw application exception...
throw convertJcrAccessException(ex);
}
finally {
if (existingTransaction) {
logger.debug("Not closing pre-bound Jcr Session after JcrTemplate");
}
else {
//不在事务环境下,立即释放session
SessionFactoryUtils.releaseSession(session, getSessionFactory());

}
}
}
~

测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public void executeTask()
{

logger.debug("get the Gadget class Bean,DagetDao={}", dao);
//添加节点
dao.addNodeIfAbsent("Gadget");
//查询节点
boolean result = dao.nodeExists("Gadget");
logger.debug("add Node success or not ,{}",result);
try{
//删除节点
dao.deleteNodebyName("Gadget");

}
catch (Exception e)
{
e.printStackTrace();
}
//再次查询节点
logger.debug("Node exist or not ,{}",dao.nodeExists("Gadget"));
}

上面测试代码总共4次操作jackrabbit,应该使用4个session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2015-08-20 20:24:42 [main] DEBUG org.springmodules.jcr.JcrSessionFactory -no session holder provider manager set; using the default one
2015-08-20 20:24:42 [thread-0] DEBUG com.huawei.jackrabbit.test.service.GadgetnoTransactionService -get the Gadget class Bean,DagetDao=com.huawei.jackrabbit.test.DAO.GadgetDao@1e22c75
2015-08-20 20:24:42 [thread-0] DEBUG org.springmodules.jcr.SessionFactoryUtils -Opening JCR Session
2015-08-20 20:24:42 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -Starting from root node. node=node /
2015-08-20 20:24:43 [thread-0] INFO com.huawei.jackrabbit.test.DAO.GadgetDao -Saved node. node=node /Gadget
2015-08-20 20:24:43 [thread-0] DEBUG org.springmodules.jcr.SessionFactoryUtils -Closing JCR Session
2015-08-20 20:24:43 [thread-0] DEBUG org.springmodules.jcr.SessionFactoryUtils -Opening JCR Session
2015-08-20 20:24:43 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -Starting from root node. node=node /
2015-08-20 20:24:43 [thread-0] DEBUG org.springmodules.jcr.SessionFactoryUtils -Closing JCR Session
2015-08-20 20:24:43 [thread-0] DEBUG com.huawei.jackrabbit.test.service.GadgetnoTransactionService -add Node success or not ,true
2015-08-20 20:24:43 [thread-0] DEBUG org.springmodules.jcr.SessionFactoryUtils -Opening JCR Session
2015-08-20 20:24:43 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -delete the node name =Gadget
2015-08-20 20:24:43 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -the node exist or not ,false
2015-08-20 20:24:43 [thread-0] DEBUG org.springmodules.jcr.SessionFactoryUtils -Closing JCR Session
2015-08-20 20:24:43 [thread-0] DEBUG org.springmodules.jcr.SessionFactoryUtils -Opening JCR Session
2015-08-20 20:24:43 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -Starting from root node. node=node /
2015-08-20 20:24:43 [thread-0] DEBUG org.springmodules.jcr.SessionFactoryUtils -Closing JCR Session
2015-08-20 20:24:43 [thread-0] DEBUG com.huawei.jackrabbit.test.service.GadgetnoTransactionService -Node exist or not ,false

打印日志可以看出4次打开和关闭session

在事务环境下,每个事务都独立使用一个session,事务提交之后立即释放。所以上面的测试代码在事务下只使用一个session

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2015-08-21 09:16:06 [main] DEBUG org.springmodules.jcr.JcrSessionFactory -no session holder provider manager set; using the default one
2015-08-21 09:16:06 [thread-0] DEBUG org.springmodules.jcr.jackrabbit.LocalTransactionManager -Creating new transaction with name [com.huawei.jackrabbit.test.service.GadgetService.executeTask]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
2015-08-21 09:16:06 [thread-0] DEBUG org.springmodules.jcr.jackrabbit.LocalTransactionManager -Opened new session [session-admin-3] for JCR transaction
2015-08-21 09:16:06 [thread-0] DEBUG com.huawei.jackrabbit.test.service.GadgetService -get the Gadget class Bean,DagetDao=com.huawei.jackrabbit.test.DAO.GadgetDao@115470e
2015-08-21 09:16:06 [thread-0] DEBUG org.springmodules.jcr.JcrTemplate -Found thread-bound Session for JcrTemplate
2015-08-21 09:16:07 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -Starting from root node. node=node /
2015-08-21 09:16:07 [thread-0] INFO com.huawei.jackrabbit.test.DAO.GadgetDao -Saved node. node=node /Gadget
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.JcrTemplate -Not closing pre-bound Jcr Session after JcrTemplate
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.JcrTemplate -Found thread-bound Session for JcrTemplate
2015-08-21 09:16:07 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -Starting from root node. node=node /
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.JcrTemplate -Not closing pre-bound Jcr Session after JcrTemplate
2015-08-21 09:16:07 [thread-0] DEBUG com.huawei.jackrabbit.test.service.GadgetService -add Node success or not ,true
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.JcrTemplate -Found thread-bound Session for JcrTemplate
2015-08-21 09:16:07 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -delete the node name =Gadget
2015-08-21 09:16:07 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -the node exist or not ,false
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.JcrTemplate -Not closing pre-bound Jcr Session after JcrTemplate
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.JcrTemplate -Found thread-bound Session for JcrTemplate
2015-08-21 09:16:07 [thread-0] DEBUG com.huawei.jackrabbit.test.DAO.GadgetDao -Starting from root node. node=node /
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.JcrTemplate -Not closing pre-bound Jcr Session after JcrTemplate
2015-08-21 09:16:07 [thread-0] DEBUG com.huawei.jackrabbit.test.service.GadgetService -Node exist or not ,false
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.jackrabbit.LocalTransactionManager -Initiating transaction commit
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.jackrabbit.LocalTransactionManager -Committing JCR transaction on session [session-admin-3]
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.jackrabbit.LocalTransactionManager -Closing JCR session [session-admin-3] after transaction
2015-08-21 09:16:07 [thread-0] DEBUG org.springmodules.jcr.SessionFactoryUtils -Closing JCR Session

上面的日志显示本次操作只使用了[session-admin-3] session

嵌套事务使用
spring中默认配置事务传播机制为PROPAGATION_REQUIRED

如果当前处在事务中调用另一事务方法,会被合成一个事务。图中事务方法2发生异常,进行事务回滚后,事务方法1结束后会提交事务,事务已经回滚再次提交会报异常

1
2
3
encountered an error.org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:252)
at java.util.concurrent.FutureTask.get(FutureTask.java:111)

在实际使用中需要注意嵌套事务的使用。

事务和非事务性能对比

根据节点名来查询jackrabbit节点是否存在,比较在事务和非事务下的性能差异

在事务和非事务下使用50线程分别查询节点并统计查询消耗的平均时间、最大和最大时间
非事务下又分为使用单一session和多session场景,下面是数据统计:

  1. 使用事务,50个线程有50个事务

    1
    -average time:88(ms), max time :94(ms), min time :86(ms)
  2. 非事务,每次查询都使用新session

    1
    -average time:44(ms), max time :54(ms), min time :39(ms)
  3. 非事务,每次查询使用同一session

    1
    -average time:12(ms), max time :23(ms), min time :8(ms)

对比使用单一session耗时最小,性能最佳,使用事务性能最差,但是从事务的ACID特性考虑,使用单一session和多个session不满足原子性要求
上面测试代码executeTask方法在多线程下使用单一session操作会报 javax.jcr.InvalidItemStateException异常,使用多session操作 报javax.jcr.ItemNotFoundException异常。而在事务环境下可以正常完成。
是否使用事务需要从业务的使用场景考虑,如果业务场景就满足原子性操作,或者异常有处理措施保证数据一致性,那么不使用事务也可以,但是这种往往比较复杂对session使用也需要谨慎考虑。
考虑一种场景,一次性添加多个节点,要不全部成功要不全部失败,如果中途失败,前面添加的节点都需要删除,在事务下可以回滚状态,在非事务下就必须代码删除,这样处理很复杂而且容易出错