【发布时间】:2015-07-26 17:49:37
【问题描述】:
我们有一个带有 jersey REST 接口的 java、spring、osgi 应用程序。
在该设置中,我需要将数据发送到运行 winCE CF 3.5 的移动设备,因此每个进程只有 4 MiB RAM。这意味着即使是一千个数据对象也会让 MDE 内存溢出。
为了解决这个问题,想法是拆分请求。
- 查询数据的初始请求,将其拆分为片段,将它们编码为 JSON,将它们存储在 HDD 上并返回有多少片段以及参考
- 后续请求将一次检索一个片段,其中片段大小仅为 100 KiB
即使连接非常糟糕,这也应该可以工作,并允许 MDE 尽可能慢地请求片段,并且尽可能频繁地请求片段,同时仍然获得相同的数据快照。
我对第一个请求的想法是,将具有休眠条件的数据查询为可滚动结果,滚动到最后,以这种方式从行号获取总结果并创建对初始请求的答案,而在后台滚动回到开始并将结果处理成硬盘上的json文件。
整个休眠过程应该发生在一个后台线程中,该线程通知 REST-request-thread 一次以提供有关结果的数据,但不提供实际结果数据。
现在我已经读到休眠和多线程是一个非常危险的组合。但在我看来,如果 Hibernate 相关的所有内容都在后台线程中发生,我可以避免麻烦。
@Path("/1/mde/articles/")
@Component
@Scope("prototype")
@Transactional
public class RestMdeArticleController
{
private static final Logger LOG = LoggerFactory.getLogger(RestMdeArticleController.class);
@Context
private HttpServletRequest request;
@Autowired
private IResultFragmentationController resultFragmentationController;
@Autowired
private IFindMasterDataStrategy masterDataFinder;
@Autowired
private SessionFactory sessionFactory;
@Path("fragment")
@GET
@Produces(MediaType.APPLICATION_JSON)
public FragmentedResultInfo getAllMdeArticlesFragmentedThreaded()
{
final FragmentedResultInfo info = new FragmentedResultInfo();
Thread worker = new Thread((new Runnable() {
private FragmentedResultInfo info = null;
public Runnable setInfo(FragmentedResultInfo info)
{
this.info = info;
return this;
}
@Override
public void run()
{
try
{
HibernateTemplate template = new HibernateTemplate(sessionFactory, true);
template.execute((new HibernateCallback<Object>()
{
private FragmentedResultInfo info = null; ;
public HibernateCallback<Object> setInfo(FragmentedResultInfo info)
{
this.info = info;
return this;
}
@Override
public Object doInHibernate(Session session) throws HibernateException, SQLException
{
ScrollableResults results = masterDataFinder.findArticles(null, new ArticleFilterOptions<Article>(), ScrollMode.SCROLL_INSENSITIVE);
resultFragmentationController.createFragments(this.info, results, Article.class, MdeTransferArticle.class);
return null;
}
}).setInfo(info));
}
catch (Exception e)
{
LogUtil.error(LOG, e, "Thread failed with {1}", e.getClass().getSimpleName());
}
finally
{
synchronized (info)
{
info.notify();
}
}
}
}).setInfo(info));
worker.start();
synchronized (info)
{
try
{
// wait for the worker to notify (that it updated the values in info)
info.wait();
}
catch (InterruptedException e)
{
LogUtil.warn(LOG, e, "waiting for thread '{0}' failed with InterruptedException", worker.getName());
}
}
return info;
}
}
问题是我仍然没有在新线程中获得休眠会话。
我想知道如何在子线程中获得休眠会话,以及整个设置是否有机会工作。有没有我还没有考虑过的陷阱,我应该注意这些? 如果您对如何处理所有问题(拆分数据)有更好的了解,我也会对此持开放态度。
编辑 1:
我得到一个:
org.springframework.orm.hibernate3.HibernateSystemException: No Hibernate Session bound to thread,并且配置不允许在这里创建非事务性会话; 嵌套异常是 org.hibernate.HibernateException: No Hibernate Session bound to thread,并且配置不允许在此处创建非事务性会话
在masterDatafinder深度的这一行:
getSessionFactory()
.getCurrentSession()
.createCriteria(getEntityName(entityClass, storageMode))
.setComment(StringUtil.normalize(comment))
.setResultTransformer(DistinctRootEntityResultTransformer.INSTANCE);
在我看来,我遗漏了一些重要信息。这是为了以离线/批量方式将主数据同步到移动设备。移动设备大部分时间都没有连接。
虽然移动设备上的数据大部分时间都会过时/同步,但至少应该始终如此。独立检索页面将为我提供属于每个请求的不同时间点的数据。 如果数据在几毫秒内发生变化,在检索实际数据之前执行 SELECT COUNT(*) 也可能会给我不同的结果。
我从一篇据说来自 Hibernate 书籍的帖子中得到了结果滚动的想法。数据实际上并没有被检索到。这是 ResultFragmentationController 的开始:
results.last();
totalResults = results.getRowNumber() + 1;
totalFragments = BigDecimal.valueOf(totalResults).divide(BigDecimal.valueOf(fragmentSize), 0, RoundingMode.UP).intValue();
info.setTotalFragments(totalFragments);
info.setTotalResults(totalResults);
info.setFragmentSize(fragmentSize);
info.setStatus(EState.IN_PROGRESS);
synchronized (info)
{
info.notify();
}
// reset the results pointer to the initial position
results.beforeFirst();
while (results.next())
{
...
【问题讨论】:
-
“我没有获得休眠会话”是什么意思?你尝试了什么,发生了什么。例外?在哪里?
-
另外:不要为了计算元素而滚动结果集。改为执行带有计数的选择。不要将结果存储在后端,而是按需执行分页查询。你真的需要第一个请求,还是可以从一个请求开始,比如:给我前 100 个“随便”,如果还有更多,请告诉我,这样可以节省一次往返,并且可能会限制存储在客户端上所需的信息量
-
@Jens:感谢您的想法,但我认为在这种情况下他们不会给我我需要的东西 - 请参阅编辑 1
标签: java multithreading spring hibernate transactions