PDA

View Full Version : Combining dependency injection with hibernate.



TimVD
Aug 14th, 2011, 11:14 AM
Hi I'm trying to build a Spring MVC application with Hibernate.

for now my DAO looks like this:

import java.util.ArrayList;

import javax.annotation.Resource;

import org.goldendragon.yahtzee.model.Player;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.User nameNotFoundException;
import org.springframework.stereotype.Repository;

@Repository("playerDAO")
public class PlayerDAO {
@Resource(name="sessionFactory")
private SessionFactory sessionFactory;

private static final Logger logger = LoggerFactory.getLogger(PlayerDAO.class);

public Player getPlayerById( Integer id ) {
Session session = sessionFactory.getCurrentSession();
Player player = (Player) session.get(Player.class, id);
return player;
}

public Player getPlayerByUserName(String username) throws UsernameNotFoundException, Exception{
Player player = null;
try{
Session session = sessionFactory.getCurrentSession();

Query query = session.createQuery("FROM Player WHERE username = :un ");
query.setParameter("un", username);

@SuppressWarnings("unchecked")
ArrayList<Player> list = (ArrayList<Player>) query.list();

player = (Player) list.get(0);
}catch(IndexOutOfBoundsException iob){
throw new UsernameNotFoundException("Username not found!");
}catch(Exception e){
throw new Exception("Error in retrieving user");
}
return player;
}

public void addPlayer(Player player){
logger.info("Registration of new player: " + player.getUsername());
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.save(player);
}
}

I have added

@Repository("playerDAO")

so I can inject the DAO in other classes like this:



@Autowired
private PlayerDAO playerDAO;

However this results in the error message "No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here" when calling the method addPlayer from my controller.

After reading some topics I discovered I had to put "@Transactional" at the top of my DAO-class. But when I do this the project fails to build. And I get this output:

[INFO] Scanning for projects...
[INFO] ------------------------------------------------------------------------
[INFO] Building abc
[INFO] task-segment: [tomcat:run]
[INFO] ------------------------------------------------------------------------
[INFO] Preparing tomcat:run
[INFO] [aspectj:compile {execution: default}]
[ERROR] The attribute value is undefined for the annotation type Repository
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD ERROR
[INFO] ------------------------------------------------------------------------
[INFO] Compiler errors :
error at @Repository("playerDAO")
^^^^^^^^^^
/home/tim/Desktop/Yahtzee-Workspace/src/main/java/org/goldendragon/yahtzee/dao/PlayerDAO.java:17:0::0 The attribute value is undefined for the annotation type Repository

[INFO] ------------------------------------------------------------------------
[INFO] For more information, run Maven with the -e switch
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 7 seconds
[INFO] Finished at: Sun Aug 14 18:12:58 CEST 2011
[INFO] Final Memory: 15M/127M
[INFO] ------------------------------------------------------------------------


This is my hibernate-context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
">

<!-- Enable annotation style of managing transactions -->
<tx:annotation-driven transaction-manager="transactionManager" />

<!-- Declare the Hibernate SessionFactory for retrieving Hibernate sessions -->
<!-- See http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/orm/hibernate3/annotation/AnnotationSessionFactoryBean.html -->
<!-- See http://docs.jboss.org/hibernate/stable/core/api/index.html?org/hibernate/SessionFactory.html -->
<!-- See http://docs.jboss.org/hibernate/stable/core/api/index.html?org/hibernate/Session.html -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.Anno tationSessionFactoryBean"
p:dataSource-ref="dataSource"
p:configLocation="/WEB-INF/config/hibernate.cfg.xml"
p:packagesToScan="org.goldendragon.yahtzee"/>

<!-- Declare a datasource that has pooling capabilities-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="com.mysql.jdbc.Driver"
p:jdbcUrl="jdbc:mysql://localhost/yahtzee"
p:user="yahtzee"
p:password="paswoord"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />

<!-- Declare a transaction manager-->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransa ctionManager" p:sessionFactory-ref="sessionFactory" />

</beans>

and my hibernate.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>

<property name="show_sql">false</property>

<property name="hbm2ddl.auto">create</property>
</session-factory>
</hibernate-configuration>

if anyone can point out what I'm doing wrong I would greatly appreciate it

dr_pompeii
Aug 14th, 2011, 12:18 PM
Hello

Just some observations

1) Spring when work with Transactions use AOP, ver closely with the Proxy Pattern, your DAO class is not implementing none interface, create one by yourself

2) Just playing, change @Resource by @Autowired and create a setter for your



After reading some topics I discovered I had to put "@Transactional" at the top of my DAO-class. But when I do this the project fails to build. And I get this output:
Is mandatory has Transactions in the BO/Service classes and DAO classes to let Spring has and handle the both *layers* within the same Transaction Control

3) about your configuration you need the follow

1) scan the annotated @Repository classes, you only scanning the @Entity classes

2) Add the follow


<bean id="hibernateInterceptor"
class="org.springframework.orm.hibernate3.HibernateInterc eptor">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean class="org.springframework.dao.annotation.PersistenceExce ptionTranslationPostProcessor"/>

Let me know your advance

TimVD
Aug 14th, 2011, 01:04 PM
1) scan the annotated @Repository classes, you only scanning the @Entity classes
org.goldendragon.yahtzee is my basepackage, so both subpackages org.goldendragon.yahtzee.dao (@Repository classes) and org.goldendragon.yahtzee.model (@Entity classes) are scanned, no?

2) Add the follow
after adding that I get following error message while attempting a maven-build:

Aug 14, 2011 8:01:51 PM org.apache.catalina.core.StandardContext listenerStart
SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListe ner
org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'org.springframework.web.servlet.mvc.annotation.De faultAnnotationHandlerMapping#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'playerController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Could not autowire field: private org.goldendragon.yahtzee.dao.PlayerDAO org.goldendragon.yahtzee.controllers.PlayerControl ler.playerDAO; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'playerDAO' defined in file [/home/tim/Desktop/Yahtzee-Workspace/target/classes/org/goldendragon/yahtzee/dao/PlayerDAO.class]: Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: net.sf.cglib.proxy.Enhancer.setInterceptDuringCons truction(Z)V
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.doCreateBean(AbstractAu towireCapableBeanFactory.java:527)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.createBean(AbstractAuto wireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.Abstract BeanFactory$1.getObject(AbstractBeanFactory.java:2 91)
at org.springframework.beans.factory.support.DefaultS ingletonBeanRegistry.getSingleton(DefaultSingleton BeanRegistry.java:222)
at org.springframework.beans.factory.support.Abstract BeanFactory.doGetBean(AbstractBeanFactory.java:288 )
at org.springframework.beans.factory.support.Abstract BeanFactory.getBean(AbstractBeanFactory.java:190)
at org.springframework.beans.factory.support.DefaultL istableBeanFactory.preInstantiateSingletons(Defaul tListableBeanFactory.java:580)
at org.springframework.context.support.AbstractApplic ationContext.finishBeanFactoryInitialization(Abstr actApplicationContext.java:895)
at org.springframework.context.support.AbstractApplic ationContext.refresh(AbstractApplicationContext.ja va:425)
at org.springframework.web.context.ContextLoader.crea teWebApplicationContext(ContextLoader.java:276)
at org.springframework.web.context.ContextLoader.init WebApplicationContext(ContextLoader.java:197)
at org.springframework.web.context.ContextLoaderListe ner.contextInitialized(ContextLoaderListener.java: 47)
at org.apache.catalina.core.StandardContext.listenerS tart(StandardContext.java:3843)
at org.apache.catalina.core.StandardContext.start(Sta ndardContext.java:4350)
at org.apache.catalina.core.ContainerBase.start(Conta inerBase.java:1045)
at org.apache.catalina.core.StandardHost.start(Standa rdHost.java:719)
at org.apache.catalina.core.ContainerBase.start(Conta inerBase.java:1045)
at org.apache.catalina.core.StandardEngine.start(Stan dardEngine.java:443)
at org.apache.catalina.startup.Embedded.start(Embedde d.java:825)
at org.codehaus.mojo.tomcat.AbstractRunMojo.startCont ainer(AbstractRunMojo.java:385)
at org.codehaus.mojo.tomcat.AbstractRunMojo.execute(A bstractRunMojo.java:144)
at org.apache.maven.plugin.DefaultPluginManager.execu teMojo(DefaultPluginManager.java:490)
at org.apache.maven.lifecycle.DefaultLifecycleExecuto r.executeGoals(DefaultLifecycleExecutor.java:694)
at org.apache.maven.lifecycle.DefaultLifecycleExecuto r.executeStandaloneGoal(DefaultLifecycleExecutor.j ava:569)
at org.apache.maven.lifecycle.DefaultLifecycleExecuto r.executeGoal(DefaultLifecycleExecutor.java:539)
at org.apache.maven.lifecycle.DefaultLifecycleExecuto r.executeGoalAndHandleFailures(DefaultLifecycleExe cutor.java:387)
at org.apache.maven.lifecycle.DefaultLifecycleExecuto r.executeTaskSegments(DefaultLifecycleExecutor.jav a:348)
at org.apache.maven.lifecycle.DefaultLifecycleExecuto r.execute(DefaultLifecycleExecutor.java:180)
at org.apache.maven.DefaultMaven.doExecute(DefaultMav en.java:328)
at org.apache.maven.DefaultMaven.execute(DefaultMaven .java:138)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:3 62)
at org.apache.maven.cli.compat.CompatibleMain.main(Co mpatibleMain.java:60)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Native MethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(De legatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at org.codehaus.classworlds.Launcher.launchEnhanced(L auncher.java:315)
at org.codehaus.classworlds.Launcher.launch(Launcher. java:255)
at org.codehaus.classworlds.Launcher.mainWithExitCode (Launcher.java:430)
at org.codehaus.classworlds.Launcher.main(Launcher.ja va:375)

it's all getting a bit confusing

dr_pompeii
Aug 14th, 2011, 01:21 PM
are scanned, no?
No

You need something like this, I am copying my code


<tx:annotation-driven/>
<context:annotation-config />
<context:component-scan base-package="org.goldendragon.yahtzee" />
The others two bold are needed too

You have

p:packagesToScan="org.goldendragon.yahtzee"
And is used to scan only the @Entity classes, the unique concern about Hibernate

If you did all my new suggestions above and you get the exception below


org.springframework.beans.factory.BeanCreationExce ption:
Error creating bean with name 'org.springframework.web.servlet.mvc.annotation.De faultAnnotationHandlerMapping#0':Initialization of bean failed; nested exception is
org.springframework.beans.factory.BeanCreationExce ption:
Error creating bean with name 'playerController':
Injection of autowired dependencies failed; nested exception is
org.springframework.beans.factory.BeanCreationExce ption:
Could not autowire field:
private org.goldendragon.yahtzee.dao.PlayerDAO org.goldendragon.yahtzee.controllers.PlayerControl ler.playerDAO;
nested exception is
org.springframework.beans.factory.BeanCreationExce ption:
Error creating bean with name 'playerDAO' defined in file
[/home/tim/Desktop/Yahtzee-Workspace/target/classes/org/goldendragon/yahtzee/dao/PlayerDAO.class]:
Initialization of bean failed;
nested exception is java.lang.NoSuchMethodError: net.sf.cglib.proxy.Enhancer.setInterceptDuringCons truction(Z)V
Could be due a CGLib version conflicts with other dependencies

Let me know your advance

TimVD
Aug 14th, 2011, 02:04 PM
well, I'm one step closer now I think, but now I'm getting an error about the auto-wiring


SEVERE: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListe ner
org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'org.springframework.web.servlet.mvc.annotation.De faultAnnotationHandlerMapping#0': Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'playerController': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Could not autowire field: private org.goldendragon.yahtzee.dao.PlayerDAO org.goldendragon.yahtzee.controllers.PlayerControl ler.playerDAO; nested exception is org.springframework.beans.factory.BeanCreationExce ption: Error creating bean with name 'playerDAO' defined in file [/home/tim/Desktop/Yahtzee-Workspace/target/classes/org/goldendragon/yahtzee/dao/PlayerDAO.class]: Initialization of bean failed; nested exception is java.lang.NoSuchMethodError: net.sf.cglib.proxy.Enhancer.setInterceptDuringCons truction(Z)V
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.doCreateBean(AbstractAu towireCapableBeanFactory.java:527)
at org.springframework.beans.factory.support.Abstract AutowireCapableBeanFactory.createBean(AbstractAuto wireCapableBeanFactory.java:456)

the class in which I'm using this autowiring looks like this:

package org.goldendragon.yahtzee.controllers;

import org.goldendragon.yahtzee.dao.PlayerDAO;
import org.goldendragon.yahtzee.model.Player;
import org.springframework.beans.factory.annotation.Autow ired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttri bute;
import org.springframework.web.bind.annotation.RequestMap ping;
import org.springframework.web.bind.annotation.RequestMet hod;
import org.springframework.validation.Validator;

@Controller
@RequestMapping("/player")
public class PlayerController {
@Autowired
private PlayerDAO playerDAO;

public void setPlayerDAO(PlayerDAO playerDAO){
this.playerDAO = playerDAO;
}

@Autowired
private Validator validator;

public void setValidator(Validator validator) {
this.validator = validator;
}

@RequestMapping(value = "/create", method = RequestMethod.GET)
public String getAdd(Model model) {
model.addAttribute("playerAttribute", new Player());
return "player/create";
}

@RequestMapping(value = "/create", method = RequestMethod.POST)
public String add(@ModelAttribute("playerAttribute") Player player, BindingResult result) {
validator.validate(player, result);
if (result.hasErrors()) { return "player/create"; }

playerDAO.addPlayer(player);
return "player/created";
}
}



btw, I would already like to thank you for your help, I really appreciate this! this project is a very important one that I have to make for school, so I was getting a bit stressed

TimVD
Aug 14th, 2011, 03:55 PM
You were right! It was a problem with CGLIB!!!!

I had to replace

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-full</artifactId>
<version>2.0.2</version>
</dependency>
with

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
</dependency>

thanx for the support!

dr_pompeii
Aug 14th, 2011, 04:01 PM
Hello

1) Be sure your PlayerDAO class implements an Interface, like PlayerDAOService


public class PlayerDAO implements PlayerDAOService

and define and inject the interface like a variable type instead of the class type, recall Spring use the Proxy Pattern



@Autowired
private PlayerDAOService playerDAOService;

public void setPlayerDAOService(PlayerDAOService playerDAOService){
this.playerDAOService = playerDAOService;
}

2) Is bad practice inject a DAO into a controller, it breaks the MVC pattern, you must use instead


Controller->BO/Service->DAO



btw, I would already like to thank you for your help, I really appreciate this!
You're welcome, time ago when I was rookie many people helped me a lot
(Thanks To Karl Moore and Marten Medium), now I try to return the favor to new people

TimVD
Aug 14th, 2011, 04:34 PM
Yes I was planning to put a "Service" class between my DAO and Controller
I'll also try to do the things with the interfaces you where talking about.

Thanks for the advice!

dr_pompeii
Aug 14th, 2011, 04:44 PM
Yes I was planning to put a "Service" class between my DAO and Controller
Yes, you must to do that


I'll also try to do the things with the interfaces you where talking about.
It is in some way mandatory since Spring work with AOP and with the proxy pattern, it is important for example when you work with Transactions


Thanks for the advice!
You're welcome


You were right! It was a problem with CGLIB!!!!
Yes, I recall my when my brain was burning to find a solution about that, In Google I found out the solution, therefore when I see in the error stack trace something with (Z)V I know inmediately about version errors conflicts

Best Regards