我的目标实际上是将数据库的所有数据转储到XML文件中。数据库不是很大,大约300MB。问题是我的内存限制只有256MB(在JVM中)。所以显然我不能把所有内容都读到内存中。
我设法使用iBatis解决了这个问题(是的,我的意思是iBatis,而不是myBatis),多次调用它getList(... int skip, int max)
,增加skip
。这确实解决了我的记忆问题,但我并没有对速度印象深刻。变量名称表明该方法的作用是读取整个结果集跳过然后指定的记录。这对我来说听起来很多余(我不是说这就是方法正在做的事情,我只是根据变量名来猜测。)
现在,我切换到myBatis 3以获取我的应用程序的下一个版本。我的问题是:有没有更好的方法来处理myBatis中的大块数据块?反正是为了让myBatis处理前N个记录,将它们返回给调用者,同时保持结果集连接打开,这样下次用户调用getList(...)时它将开始从N + 1记录读取而不做任何记录“跳过”?
答案 0 :(得分:16)
myBatis CAN流结果。您需要的是自定义结果处理程序。通过这种方式,您可以单独获取每一行并将其写入XML文件。总体方案如下:
session.select(
"mappedStatementThatFindsYourObjects",
parametersForStatement,
resultHandler);
其中resultHandler是实现ResultHandler接口的类的实例。此接口只有一个方法handleResult
。此方法为您提供ResultContext对象。从这个上下文中,您可以检索当前正在读取的行并使用它执行某些操作。
handleResult(ResultContext context) {
Object result = context.getResultObject();
doSomething(result);
}
答案 1 :(得分:7)
不,mybatis没有完整的流式传输 的功能。
编辑1: 如果您不需要嵌套的结果映射,那么您可以实现自定义结果处理程序来传输结果。在当前发布的MyBatis版本上。 (3.1.1)当前限制是指您需要进行复杂的结果映射。 NestedResultSetHandler不允许自定义结果处理程序。有一个修复程序,它看起来像3.2目前的目标。请参阅Issue 577。
总之,要使用MyBatis流式传输大型结果集,您需要。
答案 2 :(得分:2)
handleResult接收与查询一样多的记录,没有暂停。
当处理的记录太多时,我使用了sqlSessionFactory.getSession()。getConnection()。 然后,正常的JDBC,获取一个Statement,获取Resultset,并逐个处理记录。别忘了关闭会议。
答案 3 :(得分:1)
我已成功将MyBatis流与Cursor一起使用。此PR的MyBatis上已实现了Cursor。
在documentation中被描述为
游标提供与列表相同的结果,除了它获取数据外 懒惰地使用Iterator。
游标非常适合处理数以百万计的项目查询 通常不会容纳在内存中。
这是我已完成并可以成功使用的实现示例:
import org.mybatis.spring.SqlSessionFactoryBean;
// You have your SqlSessionFactory somehow, if using Spring you can use
SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
然后,您使用SQL查询定义映射器,例如UserMapper
,该查询返回目标对象(而不是列表)的游标。整个想法是不将所有元素存储在内存中。
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.cursor.Cursor;
public interface UserMapper {
@Select(
"SELECT * FROM users"
)
Cursor<User> getAll();
}
然后,编写该代码,该代码将使用工厂中的开放SQL会话并使用映射器进行查询:
try(SqlSession sqlSession = sqlSessionFactory.openSession()) {
Iterator<User> iterator = sqlSession.getMapper(UserMapper.class)
.getAll()
.iterator();
while (iterator.hasNext()) {
doSomethingWithUser(iterator.next());
}
}
答案 4 :(得分:0)
如果只是从表中转储没有任何排序要求的所有数据,为什么不直接在SQL中进行分页?设置查询语句的限制,其中指定不同的记录id作为偏移量,将整个表分成块,如果行限制是合理的数字,则每个块都可以直接读入内存。
sql可能是这样的:
SELECT * FROM resource
WHERE "ID" >= continuation_id LIMIT 300;
我认为这可以被视为一种替代解决方案,您可以通过块转储所有数据,摆脱mybatis或任何持久层支持中的不同功能问题。