2015-07-15 2 views
0

Прямо сейчас я прошу this question на стеке переполнение и that one на codereview.Я использую Hibernate-Sessions неправильно?

Это заставляет меня думать, если я понял, как использовать сеансы Hibernate. Я постараюсь сделать это короткое:

Предположим, у вас есть инструменты и описания инструментов на нескольких языках. Поэтому у вас есть Tool, ToolDescription и Language столов. Теперь предположим, что вы даете клиенту возможность собрать набор инструментов, но не только для существующих, он также может создавать новые, просто изобретая новое имя. Когда клиент отправляет данные на сервер, он пытается сохранить набор инструментов, но сначала должен сохранить новые. Это будет выглядеть следующим образом:

Клиент посылает:

ToolSet 
    id:1 
    Tool1 
     id:2 
     en:"My Hammer" 
     de:"Mein Hammer" 
    Tool2 
     id:null 
     en:"My new Tool" 
     de:"Mein neues Werkzeug" 

Чтобы сохранить это можно было бы сделать что-то вроде этого: (обратите внимание, что один из инструментов не имеет идентификатора).

toolSet = getToolSet(toolSetDto.getId()); 

if(toolSetDto.tool1 != null) { 
    tool1Dto = toolDescriptionRepository.updateOrSaveNewTool(toolSetDto.getTool1()); 
} 
if(toolSetDto.tool2 != null) { 
    tool2Dto = toolDescriptionRepository.updateOrSaveNewTool(toolSetDto.getTool2()); 
} 

// New session .. 
session = Hibernate.openSession(); 
session.beginTransaction(); 
tool1 = session.get(Tool.class, tool1Dto.getId()); 
tool2 = session.get(Tool.class, tool2Dto.getId()); 
toolSet.setTool1(tool1); 
toolSet.setTool2(tool2); 
session.getTransaction().commit(); 
session.close(); 

и updateOrSaveNewTool(ToolDTO toolDto) мы бы что-то вроде:

Session session = Hibernate.openSession(); 
Tool tool = null; 

if(toolDto.getId() != null) { 
    // Tool does already exist.. 
    tool = session.get(Tool.class, toolDto.getId()); 
} else { 
    // Store a new tool .. 
    session.beginTransaction(); 
    tool = new Tool(); 
    session.save(tool); 
    ToolDescription toolDescription = new ToolDescription(tool, getDefaultLanguage()); 
    session.save(toolDescription); 
    session.getTransaction().commit(); 
} 

session.close(); 

Это откроет несколько сеансов в одном RPC вызов, например, Из-за моих проблем у меня есть here Я не уверен, что я должен это сделать. Могу ли я получить проблемы с кэшированием, например?

Может ли кто-нибудь объяснить мне, как именно использовать сеансы Hibernate? Я знаю, они должны выполнять/обрабатывать единицу работы, но это не объясняет, нужно ли мне открывать несколько сеансов для каждого запроса.

Я мог бы, конечно, un капсулы updateOrSaveNewTool() и работать, например. с сырыми сущностей-объектов и не придется выполнять другую get(Tool.class, tool1Dto.getId()) после updateOrSaveNewTool(), но это не будет очень объектно-ориентированный язык ..

ответ

0

Я рекомендую вам прекратить писать свой собственный код сеанса и транзакции управления, и вместо того, чтобы использовать стандартные решения, как Весна, которая сделает все это для вас.

Подробнее о поддержке Spring Hibernate here. Дополнительная информация о Spring management management here.

При настройке, ваш код будет выглядеть следующим образом:

@Autowired 
private SessionFactory sessionFactory; 

@Transactional 
public void foo() { 
    tool1 = sessionFactory.getCurrentSession().get(Tool.class, tool1Dto.getId()); 
    toolSet.setTool1(tool1); 
    sessionFactory.getCurrentSession().update(tool1); 
} 
+0

Это хороший намек, но, к сожалению, я действительно не отвечаю на мой вопрос. Как это будет выглядеть в моем сценарии, когда я должен «предварительно хранить» некоторые объекты, прежде чем сохранять свой «ToolSet». Я просто не знаю, как я должен это делать. – displayname

+0

Хорошо согласен. Hibernate.openSession() всегда будет открывать новый сеанс, так как вы указываете, что это может быть непрактичным, если вы работаете с одними и теми же объектами в двух разных сеансах. Вам лучше использовать sessionFactory.getCurrentSession(), который возвращает сессию, связанную с контекстом (и ее не нужно закрывать). По умолчанию контекст привязан к текущему потоку, поэтому обычно это означает, что сеанс будет использоваться повторно во всех ваших методах. Вот почему я предложил использовать Spring с sessionFactory.getCurrentSession() - он будет автоматически повторно использовать сеанс для всех вызовов в запросе. – lars

1

Hibernate сессии следует рассматривать как «единица работы». Если ваша единица работы связана с созданием некоторых объектов и последующим их потреблением, было бы лучше использовать один сеанс для всех этих операций. Более того, вы должны иметь это в одной транзакции, так что если один шаг не удастся, предыдущие шаги будут отброшены назад. Обычно, единица работы - это то, что происходит внутри одного запроса.

То, что вы видите, является общей проблемой, которая решается по-разному в зависимости от вашего технологического стека.

Если вы находитесь в контейнере Java EE, например Wildfly, вы можете позволить контейнеру управлять менеджером сущностей для вас.Таким образом, вы просто вводите EntityManager, который готов к использованию (то есть: «сеанс» уже открыт для вас и закроется, когда вам это больше не понадобится).

Другим распространенным подходом является использование одного сеанса для каждого запроса, управляемого самим собой: у вас будет фильтр (или что-то подобное), который откроет сеанс после получения запроса и закроется, когда ответ будет готов. Вы сохранили бы это в потоке локально, чтобы каждый запрос имел свой сеанс.

И, наконец, если вы используете Spring, вы можете делегировать ему поддержку EntityManager, как и в случае с контейнером Java EE.

+0

Так что это значит, что я не должен был инкапсулировать сохранение объекта Tool и ToolDescription и повторно использовать его позже в другом сеансе в том же запросе, но вместо этого сделать все это только в * one * request/session, правильно? Потому что будет возможность передать открытый сеанс каждому вызову функции репозитория ([шаблон репозитория] (http://www.codeproject.com/Articles/526874/Repositorypluspattern-cplusdoneplusright)), чтобы гарантировать, что все действия произойдут только в одном предварительно открытом сеансе. – displayname

+0

Вопрос в том случае, если шаблон-репозиторий как таковой в порядке с этим - я не уверен в этом, но, возможно, это другой вопрос. – displayname

+0

Да, вы * можете *, но я не думаю, что вы захотите это сделать. Я совершенно уверен, что любая среда, в которой вы развертываете приложение, может уже иметь хорошее решение для вашей проблемы. Исключение будет «автономным» клиентом, то есть не развертывается в каком-либо контейнере. – jpkrohling

Смежные вопросы