我只需要使用Hibernate读取MySQL数据库中表中的每一行,并根据它编写一个文件.但是有9000万行,它们非常大.所以看起来以下是合适的:
ScrollableResults results = session.createQuery("SELECT person FROM Person person")
.setReadOnly(true).setCacheable(false).scroll(ScrollMode.FORWARD_ONLY);
while (results.next())
storeInFile(results.get()[0]);
Run Code Online (Sandbox Code Playgroud)
问题是上面将尝试将所有9000万行加载到RAM中,然后再转到while循环...这将使OutOfMemoryError消除我的内存:Java堆空间异常:(.
所以我猜ScrollableResults不是我想要的?处理这个问题的正确方法是什么?我不介意这个while循环需要几天(好吧我不喜欢它).
我想处理这个问题的另一种方法是使用setFirstResult和setMaxResults迭代结果,只使用常规的Hibernate结果而不是ScrollableResults.这感觉就像它效率低下一样,当我在8900万行中调用setFirstResult时,它将开始花费一段可笑的时间......
更新:setFirstResult/setMaxResults不起作用,事实证明需要花费相当长的时间才能达到我所担心的偏移量.这里一定有解决方案!这不是一个很标准的程序吗?我愿意放弃Hibernate并使用JDBC或其他任何东西.
更新2:我提出的解决方案哪个工作正常,不是很好,基本上是以下形式:
select * from person where id > <offset> and <other_conditions> limit 1
Run Code Online (Sandbox Code Playgroud)
由于我有其他条件,即使是索引中的所有条件,它仍然没有我想要的那么快......所以仍然可以提供其他建议..
我有一个使用Hibernate(通过JPA)的长期运行(但相当简单)的应用程序.它经历了相当剧烈的放缓.我已经能够缩小到需要偶尔entityManager.clear()打电话.当Hibernate的实体管理器跟踪100,000个实体时,它比仅跟踪几个实体的速度慢约100倍(见下面的结果). 我的问题是:为什么 Hiberate在追踪很多实体的时候会这么慢?还有其他方法吗?
!更新:我已经能够将其缩小到Hibernate的自动刷新代码.!
具体到org.hibernate.event.internal.AbstractFlushingEventListener's flushEntities()方法(至少在Hibernate 4.1.1.Final中).在其中有一个循环遍历持久化上下文中的所有实体,执行一些广泛的检查来清除它们中的每一个(即使在我的示例中已经刷新了所有实体!).
因此,部分回答我的问题的第二部分,可以通过FlushModeType.COMMIT在查询上设置刷新模式来解决性能问题(请参阅下面的更新结果).例如
Place place = em.createQuery("from Place where name = :name", Place.class)
.setParameter("name", name)
.setFlushMode(FlushModeType.COMMIT) // <-- yay!
.getSingleResult();
Run Code Online (Sandbox Code Playgroud)
...但这似乎是一个相当丑陋的解决方案 - 传递责任,知道是否将事物刷新到查询方法而不是将其保留在更新方法中.它也意味着我要么必须在所有查询方法上将flush模式设置为COMMIT,要么更有可能在EntityManager上设置它.
这让我想知道:这是预期的行为吗?我是否在刷新或者如何定义实体时出错?或者这是Hibernate的限制(或可能是错误)?
我用来隔离问题的示例代码如下:
@Entity @Table(name="place") @Immutable
public class Place {
private Long _id;
private String _name;
@Id @GeneratedValue
public Long getId() { return _id; }
public void setId(Long id) { _id = id; }
@Basic(optional=false) @Column(name="name", length=700,
updatable=false, nullable=false, unique=true, …Run Code Online (Sandbox Code Playgroud) 我需要让我的网络应用程序使用非常庞大的数据集.目前我得到的是OutOfMemoryException或输出1-2分钟.
让我们把它简单,假设我们有在DB 2个表:Worker并WorkLog在第一个约1000列,并在第二个10个000 000行.后期表有几个字段,包括'workerId'和'hoursWorked'字段等.我们需要的是:
计算每个用户的总工作小时数;
每个用户的工作时间列表.
纯SQL中每个任务最直接的方法(IMO)是:
1)
select Worker.name, sum(hoursWorked) from Worker, WorkLog
where Worker.id = WorkLog.workerId
group by Worker.name;
//results of this query should be transformed to Multimap<Worker, Long>
Run Code Online (Sandbox Code Playgroud)
2)
select Worker.name, WorkLog.start, WorkLog.hoursWorked from Worker, WorkLog
where Worker.id = WorkLog.workerId;
//results of this query should be transformed to Multimap<Worker, Period>
//if it was JDBC then it would be vitally
//to set resultSet.setFetchSize (someSmallNumber), ~100
Run Code Online (Sandbox Code Playgroud)
所以,我有两个问题:
我看到很多JDBC/MySQL的"最佳实践"指南告诉我指定setFetchSize().
但是,我不知道何时指定,以及要指定的内容(语句,结果集).
Statement.setFetchSize() or PreparedStatement.setFetchSize()
ResultSet.setFetchSize()
Run Code Online (Sandbox Code Playgroud)
的Javadoc
默认值由创建结果集的Statement对象设置.可以随时更改提取大小.
Oracle Doc
生成结果集后对语句对象的提取大小所做的更改将不会影响该结果集.
如果我错了,请纠正我.这是否意味着setFetchSize在执行查询之前只是Affective?(因此,ResultSet上的setFetchSize是无用的?但是恰好"可以随时更改获取大小"?)
我目前在我的项目中设置了hibernate.它适用于大多数事情.但是今天我需要从表中返回几十万行查询.它是表中总行数的约2/3s.问题是查询需要大约7分钟.使用直接JDBC并执行我假设的相同查询,它需要<20秒.因此,我认为我做的事情完全错了.我将在下面列出一些代码.
DetachedCriteria criteria =DetachedCriteria.forlass(MyObject.class);
criteria.add(Restrictions.eq("booleanFlag", false));
List<MyObject> list = getHibernateTemplate().findByCriteria(criteria);
Run Code Online (Sandbox Code Playgroud)
关于它为什么会变慢和/或我可以做些什么来改变它的想法?