I have a simple entity - Team, with name and rating properties (or maybe more).
Suppose I need to query my teams by multiple criteria.
So instead of adding multiple methods with signature like 'findByXYZAndZYX' to my service, I'd rather add following method :
Teams findTeams()
Implementation snippet:
#Autowired private SessionFactory sessionFactory;
...
#Override
public Teams getTeams() {
return new HibernateTeams(sessionFactory);
}
Now, Teams interface:
public interface Teams extends Iterable<Team> {
Teams withNameContaining(String name);
Teams withRatingGreaterThan(Integer rating);
}
and hibernate-specific implementation:
public class HibernateTeams implements Teams {
private static final String NAME_PROPERTY = "name";
private static final String RATING_PROPERTY = "rating";
private SessionFactory sessionFactory;
private Criteria criteria;
public HibernateTeams(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
criteria = getRootCriteria();
}
private Criteria getRootCriteria() {
return getCurrentSession().createCriteria(Team.class);
}
#Override
public HibernateTeams withNameContaining(String name) {
criteria.add(Restrictions.like(NAME_PROPERTY, name));
return this;
}
#Override
public Teams withRatingGreaterThan(Integer rating) {
criteria.add(Restrictions.gt(RATING_PROPERTY, rating));
return this;
}
#Override
public Iterator<Team> iterator() {
#SuppressWarnings("unchecked")
Collection<Team> result = criteria.list();
return result.iterator();
}
private Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
}
But now, using this in client code:
teamService
.getTeams()
.withNameContaining("someTeamName")
.withRatingGreaterThan(15)
I have an SessionException
org.hibernate.SessionException: Session is closed
I suppose this happens because of passing sessionFactory to non-managed class.
So there are a couple of questions here:
1) Is it possible to do this the way I wrote it? I tried to annotate my HibernateTeams with Transactional or sth, but it didn't help.
2) If I need to make my HibernateTeams spring-managed-bean and possibly inject SessionFactory into it, how can I do that? I've already tried with
#Component #Scope("prototype")
or #Configurable
but with no luck.
Thanks,
Related
I have an app using Spring Core, Spring MVC, Hibernate.
There are a lot of DAO classes which get new Hibernate session in every method like this
#Autowired
private SessionFactory sessionFactory;
private Session session;
private void createSession() {
session = sessionFactory.openSession();
}
#Override
public List<User> listUsers() {
createSession();
List users;
users = session.createQuery("from User").list();
session.close();
return users;
}
I would like to use AOP to execute createSession method before those class methods but can't figure out how to do it.
All I have done was this aspect
#Configuration
#Aspect
public class DaoSessionLifeCycle {
#Before(value = "execution(* ua.com.alistratenko.dao.UserDaoImp.listUsers(..))")
public void openSession(JoinPoint joinPoint){
System.out.println("izi");
}
}
Because you still did not provide an MCVE or at least some more (and more realistic) sample code, I can only speculate and provide a schematic answer.
BTW, I am using AspectJ and not Spring AOP here, but it should work the same way there.
Dummy classes to make the sample code compile:
package ua.com.alistratenko.dao;
public class User {
String name;
public User(String name) {
this.name = name;
}
#Override
public String toString() {
return "User [name=" + name + "]";
}
}
package ua.com.alistratenko.dao;
import java.util.ArrayList;
import java.util.List;
public class QueryResult {
public List<User> list() {
List<User> users = new ArrayList<>();
users.add(new User("jane"));
users.add(new User("joe"));
return users;
}
}
package ua.com.alistratenko.dao;
public class Session {
public QueryResult createQuery(String string) {
return new QueryResult();
}
public void close() {}
}
package ua.com.alistratenko.dao;
public class SessionFactory {
public Session openSession() {
return new Session();
}
}
Application class:
What did I change?
In Spring you would use #Autowired instead of creating the session factory by yourself. I just did this in order to be able to run my sample code without Spring.
Added some setters/getters for the aspect to be able to access the session factory and also the session itself
Removed createSession() (code migrated to aspect, see below)
No more boilerplate clutter in listUsers()
Added main method for demo purposes
package ua.com.alistratenko.dao;
import java.util.List;
public class UserDaoImp {
private SessionFactory sessionFactory = new SessionFactory();
private Session session;
public SessionFactory getSessionFactory() { return sessionFactory; }
public Session getSession() { return session; }
public void setSession(Session session) { this.session = session; }
public List<User> listUsers() {
return session.createQuery("from User").list();
}
public static void main(String[] args) {
new UserDaoImp().listUsers();
}
}
Aspect:
In order to do something before + after the intercepted method call, please use #Around.
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
//import org.springframework.stereotype.Component;
import ua.com.alistratenko.dao.UserDaoImp;
//#Component
#Aspect
public class DaoSessionLifeCycle {
#Around("execution(public * listUsers(..)) && target(dao)")
public Object openSession(ProceedingJoinPoint thisJoinPoint, UserDaoImp dao) throws Throwable {
try {
System.out.println("Creating session");
dao.setSession(dao.getSessionFactory().openSession());
System.out.println("Calling " + thisJoinPoint.getSignature());
return thisJoinPoint.proceed();
}
finally {
try {
System.out.println("Closing session");
dao.getSession().close();
}
catch (Exception e) {}
}
}
}
Console log:
Creating session
Calling List ua.com.alistratenko.dao.UserDaoImp.listUsers()
Closing session
Now there are several open questions:
Do you only want to handle this one class or maybe all subclasses of a base class or an implemented interface common to all your DAOs? Then the pointcut would look different.
Do you only want to intercept method listUsers() or maybe also other methods? As you did not show more code, nobody except you can know. The pointcut would also look different depending on your answer.
Do you really want to do this all manually or maybe follow the other users' advice and use on-board Spring tools in order to manage your transactions? I have no idea about Spring, so I cannot tell you. I was just providing the answer to your question which was aspect-related.
In addition to what #Bogdan says, take a look at CrudRepository or JpaRepository, it will save you a lot of time. Also, in my opinion, #Aspect classes are annotated with #Component as opposed to #Configuration.
The HQL statement does not roll back when I use spring+hibernate, but session.saveOrUpdate () will;
UserService
#Service
#Transactional(rollbackFor=Exception.class)
public class UserService {
#Autowired
private BaseDao dao;
public int updateTest(){
int i = dao.updateUser();
int t = 1/0;
return i;
}
}
BaseDao
#Repository
public class BaseDao {
#Autowired
private SessionFactory sessionFactory;
private Session getSession(){
return sessionFactory.getCurrentSession();
}
public int updateUser(){
int i = 0;
/* String sql = "from Student where name = 'dengbojing'";
Query query = this.getSession().createQuery(sql);*/
Student s = new Student();
s.setId(1);
s.setAddress("1");
Query query = this.getSession().createQuery("update Student s set s.address = '1'");
query.executeUpdate();
//this.getSession().update(s);
return i;
}
}
Configuration class
#Configuration
#EnableConfigurationProperties(HibernateProperties.class)
#EnableTransactionManagement(proxyTargetClass=true)
public class HibernateConfig {
#Autowired
private HibernateProperties config;
#Bean(name="sessionFactory")
public LocalSessionFactoryBean localSessionFactoryBean(){
LocalSessionFactoryBean bean = new LocalSessionFactoryBean();
bean.setDataSource(dataSource());
bean.setHibernateProperties(config.getHibernateProperties());
bean.setPackagesToScan(config.getPackageToScan());
return bean;
}
#Bean
public DataSource dataSource(){
DruidDataSource source = new DruidDataSource();
source.setDriverClassName(config.getDatasource().getDriverClassName());
source.setUsername(config.getDatasource().getUsername());
source.setUrl(config.getDatasource().getUrl());
source.setPassword(config.getDatasource().getPassword());
return source;
}
#Bean
public HibernateTransactionManager txManager(){
HibernateTransactionManager manager = new HibernateTransactionManager();
manager.setSessionFactory(localSessionFactoryBean().getObject());
manager.setDataSource(dataSource());
return manager;
}
}
Spring transaction does not support the HQL statement, the problem plagued me for 2 days, I saw someone with similar problems, but did not solve the problem
I have made some tests with exact same versions and configuration.
I have to say that the update is never persisted in the database.
As a workaround, if you can implement your functionality in this way.. try to:
Find the desired Person entity
Update required fields
Do not invoke any other methods on the session object after that.. just leave it to the framework to update the changes on the transaction commit.
Example
Person person = session.get(Person.class, 1);
person.setAddress("1")
// other fields updated
return i;
Now, without the explicit bulk update using the executeUpate() there is no chance for an implicit commit by the provider.. This is theory so check it out.
I am developing a web application with pure JSP, Servlet and Hibernate. Last few days I was having a trouble with the SessionFactory of Hibernate, not knowing the best way of implementing it. In various places developers have claimed that there should be one SessionFactory for the application. So, I created a singleton class like below.
package dao;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class SessionFactoryBuilder
{
private static SessionFactoryBuilder instance;
private static SessionFactory sessionFactory;
private SessionFactoryBuilder()
{
buildConfig();
}
private static void buildConfig()
{
Configuration configuration = new Configuration().configure();
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder()
.applySettings(configuration.getProperties());
sessionFactory = configuration.buildSessionFactory(builder.build());
}
public static SessionFactoryBuilder getInstance()
{
if(instance == null)
{
instance = new SessionFactoryBuilder();
}
return instance;
}
public SessionFactory getSessionFactory()
{
return sessionFactory;
}
}
Below is a class which uses the SessionFactoryBuilder.
public class AgentImpl implements AgentInterface
{
SessionFactoryBuilder sessionFactory = SessionFactoryBuilder.getInstance();
#Override
public Session openCurrentSession() {
Session currentSession = sessionFactory.getSessionFactory().openSession();
return currentSession;
}
#Override
public Transaction openTransaction(Session session) {
Transaction beginTransaction = session.beginTransaction();
return beginTransaction;
}
#Override
public void save(Agent entity, Session session) {
session.save(entity);
}
#Override
public void update(Agent entity, Session session) {
session.update(entity);
}
}
Now my question is, is this is the best way of using the SessionFactory ? This might sound like a silly question but it will not when you think about multi threaded behavior in servlets, Driver#Connect error happening because of incorrect usage of session factory, various ways of implementing the singleton pattern and so on. Please provide me your advice.
I'm using hibernate 4.1.2
And I want insert some data into the table.
I know that it can be realized with sql in hibernate configuration in this way:
<property name="hibernate.hbm2ddl.import_files" value="/file1.sql,/file2.sql"/>.
But, are there any other ways insert automatically data only once in Java code after hibernate started?
I want do it like this:
public Role getRoleByName(EnumRole name)
{
return (Role) sessionFactory.getCurrentSession()
.createQuery("from Role where name = :name")
.setParameter("name", name).uniqueResult();
}
public void insertRoles(){
for(EnumRole role:EnumRole.values())
{
Role r=getRoleByName(role);
if(r==null)
{
r=new Role();
r.setName(role);
r.setDescription(role.getDescription());
sessionFactory.getCurrentSession().save(r);
}
}
EnumRole:
public enum EnumRole {
ROLE_CLIENT("РОЛЬ КЛИЕНТА"),
ROLE_ADMIN("РОЛЬ АДМИНСТРАТОРА"),
ROLE_CONSUMER("РОЛЬ КОМПАНЬОНА"),
ROLE_ANONYMOUS("НЕ АВТОРИЗОВАННЫЙ ПОЛЗОВАТЕЛЬ");
EnumRole(String descriptin)
{
this.descriptin=descriptin;
}
public String getDescription()
{
return this.descriptin;
}
private String descriptin;
}
You need to create any Spring bean with #PostConstruct annotated method and for example create transaction using PlatformTransactionManager:
#Service
public class AnyBean {
#Autowired
private PlatformTransactionManager transactionManager;
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
private void init() {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallback<Object>() {
#Override
public Object doInTransaction(TransactionStatus transactionStatus) {
// HERE YOU CODE
for(EnumRole role:EnumRole.values())
{
Role r = getRoleByName(role);
if(r==null)
{
r=new Role();
r.setName(role);
r.setDescription(role.getDescription());
sessionFactory.getCurrentSession().save(r);
}
}
return null;
}
});
}
}
I hope this helped.
Create a service with method insertRoles() marked with #Transactional and call service.insertRoles().
Or, more easy:
create a transaction
add roles
commit (or manage rollback if error occured)
Do this stuff after your application startup has been completed
I'm using Spring 3.1.0.RELEASE with Hibernate 4.0.1.Final. I want to invoke an event listener on my entity bean when it is loaded from the DB, but I can't figure out what event I should be using. I load my entities in my DAO like so
#Repository("eventFeedsDao")
public class EventFeedsDaoImpl implements EventFeedsDao {
...
#Autowired
private SessionFactory sessionFactory;
...
#Override
public EventFeed findById(final Integer id) {
EventFeed eventFeed = null;
final Session session = sessionFactory.getCurrentSession();
final Criteria crit = session.createCriteria(EventFeed.class).add(Restrictions.eq("id", id));
final List<EventFeed> results = crit.list();
if (results != null && results.size() > 0) {
eventFeed = results.get(0);
} // if
return eventFeed;
} // findById
Here is how I'm trying to set up my event wiring ...
#Component
public class HibernateEventWiring {
#Autowired
private SessionFactory sessionFactory;
#Autowired
private EventMavenLoadListener listener;
#PostConstruct
public void registerListeners() {
EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory)
.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.LOAD).appendListener(listener);
}
}
and my listener class is as follows ...
#Component
public class EventMavenLoadListener implements LoadEventListener {
...
#Override
public void onLoad(final LoadEvent event, final LoadType loadType) throws HibernateException {
if(EventFeed.class.getName().equals(event.getEntityClassName())){
EventFeed entity = (EventFeed) event.getInstanceToLoad();
entity.setNetUtilsService(netUtilsService);
} // if
}
}
but the "onLoad" event is never called. What am I doing wrong? Should I be using another event?
For some reason that still eludes me, the EventType.PRE_LOAD event type was the way to go. This worked.
#Component
public class HibernateEventWiring {
#Autowired
private SessionFactory sessionFactory;
#Autowired
private EventMavenLoadListener listener;
#PostConstruct
public void registerListeners() {
final EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory)
.getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.PRE_LOAD)
.appendListener((PreLoadEventListener) listener);
}
}
The load event is only triggered when loading an entity individually (session.get, initializing a proxy, fetching eager to-one associations).
The pre and post load events however follow JPA defined behavior and occur before and after the entity data is loaded regardless of how that "data load" occurred.
Its a case of bad name collision.
The preferred way in Hibernate 4, btw, to specify event listeners is through the use of an org.hibernate.integrator.spi.Integrator. See http://docs.jboss.org/hibernate/orm/4.1/devguide/en-US/html_single/#integrators