我们有一个带有泽西REST接口的java,spring,osgi应用程序。
在该设置中,我需要将数据发送到运行winCE CF 3.5的移动设备,因此每个进程只有4个MiB RAM。这意味着即使是一千个数据对象也让MDE内存过流。
要解决问题,我们的想法是拆分请求。
即使是非常糟糕的连接也应该可以工作,并允许MDE以尽可能慢的速度请求片段,同时仍然获得相同的数据快照。
我对第一个请求的想法是,将具有休眠标准的数据查询为可滚动结果,滚动到结尾,以这种方式从行号中获取总结果并创建对初始请求的答案,而在后台滚动回到开始并将结果处理到硬盘上的json文件中。
整个hibernate的东西应该发生在一个后台线程中,它通知REST请求线程一次提供有关结果但没有实际结果数据的数据。
现在我已经读过Hibernate和Multithreading是一个非常危险的组合。然而,在我看来,如果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;
}
}
问题是我仍然没有在新线程中获得Hibernate会话。
我想知道如何在子线程中获得Hibernate会话,如果整个设置有任何机会可以工作。我还有什么陷阱尚未考虑过,我应该注意哪些? 如果您更好地了解如何处理所有问题(将数据拆分),我也会对此持开放态度。
我得到了:
org.springframework.orm.hibernate3.HibernateSystemException:没有Hibernate Session绑定到线程,配置不允许在这里创建非事务性的; 嵌套异常是org.hibernate.HibernateException:没有Hibernate会话绑定到线程,并且配置不允许在这里创建非事务性的
在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())
{
...
答案 0 :(得分:1)
HibernateTemplate
应该允许您创建新的Hibernate Session
,因为当前SpringSessionContext
ThreadLocal
存储没有会话限制。
与设计相关,您应关闭ScrollableResults
并释放与数据库相关的资源(连接,光标)。
因此我会这样设计:
初始请求构建一个分配了UUID的Command,并将Command传递给ExecutorService以进行异步处理。执行结果被缓存。
任何后续请求都使用相同的UUID从Cache中获取计算结果。您可以使用Future
,以便客户端代码阻止Computation结束。
计算Result对象的异步块必须始终关闭Hibernate会话并释放数据库连接资源。