0

I know it's far not a new question. For example lots of recipies can be found here both in question and replies. However, I didn't manage to solve mine.

I user Hibernate 4 + Spring 4 and PostgreSQL. I have DAO and DAOImpl marked @Transactional for the whole class:

package org.app.server.dao.impl;

@Repository
@Transactional
public class UserDAOImpl implements UserDAO {

  @Autowired
  private SessionFactory sessionFactory;

  public User getUser(String login) {
    User user =
            (User) sessionFactory.getCurrentSession().createQuery("from User p where p.login=:login")
                    .setParameter("login", login).uniqueResult();
    return user;
  }

  @Override
  public void addUser(User user) {
    sessionFactory.getCurrentSession().save(user);
  }
}

User is an @Entity:

package org.app.model;

@Entity
@Table(name = "person")
public class User implements Serializable {
  private static final long serialVersionUID = 1L;

  public User() {
  }

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(columnDefinition = "serial")
  private Integer id;

  @NotNull
  private String login;

  @NotNull
  private String password;

  private String email;

 // getters and setters
}

Now I sign up a User from th form. Controller catches the data:

package org.app.controller;

@Controller
@Scope("prototype")
public class MyController {

@Autowired
private UserOperations userOperations;

  @ModelAttribute("user")
  public User getUserObject() {
    return new User();
  }

  @RequestMapping(value="/newuser", method = RequestMethod.POST)
  public ModelAndView newUser(@ModelAttribute("user") User user, BindingResult result) {
    userOperations.signup(user);
    return new ModelAndView("redirect:/somepage");
  }
}

And sends it to UserOperations @Component in order to check if such login has already existed and insert a new User. Obviously, that must be done in one transaction:

package org.app.server;

@Component
@Scope("prototype")
public class UserOperations {

  @Autowired
  private UserDAO userDAO;

  @Transactional
  public void signup(User user) {
    if (userDAO.getUser(user.getLogin()) == null) {
      userDAO.addUser(user);
    }    
  }
}

So whenn I run it, I have the following:

org.hibernate.HibernateException: Could not obtain transaction-synchronized Session for current thread
    at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:134)
    at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1014)
    at org.app.server.dao.impl.UserDAOImpl.getUser(UserDAOImpl.java:30)
    at org.app.server.UserOperations.signup(UserOperations.java:25)
    at org.app.controller.StartPageController.newUser(StartPageController.java:56)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

Here are my xml configs: applicationContext.xml

<context:component-scan base-package="org.app.server" />
<context:annotation-config />

<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager"
    class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource"
    p:driverClassName="${jdbc.driverClassName}" p:url="${jdbc.databaseurl}"
    p:username="${jdbc.username}" p:password="${jdbc.password}" />

<bean id="flyway" class="org.flywaydb.core.Flyway" init-method="migrate">
    <property name="dataSource" ref="dataSource"/>
</bean>

<bean id="sessionFactory"
    class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" depends-on="flyway">
    <property name="dataSource" ref="dataSource" />
    <property name="packagesToScan">
         <array>
              <value>org.app.server</value>
              <value>org.app.model</value>
         </array>
    </property> 
    <property name="hibernateProperties">
        <props>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.dialect">${jdbc.dialect}</prop>
            <prop key="hibernate.connection.charSet">UTF-8</prop>
            <prop key="hibernate.hbm2ddl.auto">${jdbc.hbm2ddl}</prop>
        </props>
    </property>
</bean>

And springmvc-servlet.xml:

<context:component-scan base-package="org.app.controller, org.app.server" />
<mvc:annotation-driven />
<mvc:default-servlet-handler />

One more interesting thing is that when I use getUser and addUser (signup) separately, directly from Controller, for example, it works fine.

Community
  • 1
  • 1
Dmitrii Bocharov
  • 872
  • 6
  • 21
  • Are you `@Autowiring` the `UserOperation` in the `StartPageController`? Spring is not wrapping the `UserOperation` with it's AOP black majick for the transaction managment. Either the `` post-processor not picking it up or you're not getting the object instance from Spring context. – André Feb 11 '15 at 12:34
  • Yes, I do. I understand it. I've updated the question. – Dmitrii Bocharov Feb 11 '15 at 13:25
  • It may be because your transaction management is only within the application context, not within the servlet context. Maybe you can try adding the `org.app.controller` package to the `applicationContext.xml` to the `` tag. Not sure, I don't have much experience with Spring and it's XML configuration, but I think it worth trying. – André Feb 11 '15 at 13:29
  • **If** you ever solved this please say how. I'm stuck on the same problem, and nothing I've found and tried works yet. I'm nearly sure it's a config issue, but no idea what. – MerreM Apr 13 '15 at 16:06
  • 1
    My problem, (might be unrelated to any other issue) was that the dispath-servlet context-annotation overlapped scanning for annotations with hibernate. That appears to have been the root of the issue. Making the dispatch servlet only scan @Controllers appears to have fixed it. – MerreM Apr 15 '15 at 13:25

3 Answers3

0

You must openSession before your call

User user = (User)sessionFactory.getCurrentSession().createQuery("from User p where p.login=:login").setParameter("login", login).uniqueResult();

So you must do :

Session session = sessionFactory.openSession();
User user = (User) session.createQuery("from User p where p.login=:login").setParameter("login", login).uniqueResult();
  • Every time opening session will impact performance , any better solution and save will not be consistent – rinilnath Aug 14 '20 at 11:19
0

Adding @transactional annotation before UserDao.getUser(String login) method should fix this.

mambo
  • 9
  • 1
  • 2
0

In my case, there was no tx:annotation-driven after the component scan for the specific package.

Kuppusamy
  • 65
  • 1
  • 6