I want to manage my transactions with Spring #Transactional and support nested transaction by following this example in order to do so.
My problem is if I call UserService.addAdmin() that SpringTransactionProvider.begin() and SpringTransactionProvider.commit() are not getting called for some reason which indicates that it is not working the way I'm doing it ..
I'm having my service implemented:
/*
*
*/
public class UserService {
private final static Logger LOGGER = Logger.getLogger(UserService.class.getName());
private AdminRepository adminRepository;
public UserService(DSLContext ctx) {
this.adminRepository = new AdminRepository(ctx);
}
#Transactional
public void addAdmin(String userId) {
DSLContext ctx = adminRepository.getCtx();
ctx.insertInto(Admin.ADMIN)
.set(Admin.ADMIN.USER_NAME, userId)
.execute();
}
}
As well as defined my configuration file servlet-context.xml
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.postgresql.Driver" />
<property name="url" value="jdbc:postgresql://localhost:5432/mz_db" />
<property name="username" value="postgres" />
<property name="password" value="huehuehue" />
</bean>
<!-- Configure Spring's transaction manager to use a DataSource -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- Configure jOOQ's TransactionProvider as a proxy to Spring's transaction manager -->
<bean id="transactionProvider"
class="com.mz.server.web.SpringTransactionProvider">
</bean>
<!-- Configure jOOQ's ConnectionProvider to use Spring's TransactionAwareDataSourceProxy,
which can dynamically discover the transaction context -->
<bean id="transactionAwareDataSource"
class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<constructor-arg ref="dataSource" />
</bean>
<bean class="org.jooq.impl.DataSourceConnectionProvider" name="connectionProvider">
<constructor-arg ref="transactionAwareDataSource" />
</bean>
<!-- Configure the DSL object, optionally overriding jOOQ Exceptions with Spring Exceptions -->
<bean id="dsl" class="org.jooq.impl.DefaultDSLContext">
<constructor-arg ref="config" />
</bean>
<!-- Invoking an internal, package-private constructor for the example
Implement your own Configuration for more reliable behaviour -->
<bean class="org.jooq.impl.DefaultConfiguration" name="config">
<property name="SQLDialect"><value type="org.jooq.SQLDialect">POSTGRES_9_4</value></property>
<property name="connectionProvider" ref="connectionProvider" />
<property name="transactionProvider" ref="transactionProvider" />
</bean>
<!-- BEGIN Services -->
<bean id="userService" class="com.mz.server.web.service.UserService">
<constructor-arg>
<ref bean="dsl" />
</constructor-arg>
</bean>
and basically a copy of SpringTransactionProvider:
public class SpringTransactionProvider implements TransactionProvider {
private final static Logger LOGGER = Logger.getLogger(SpringTransactionProvider.class);
#Autowired
DataSourceTransactionManager txMgr;
public SpringTransactionProvider() {
LOGGER.info("Ctor()");
}
#Override
public void begin(TransactionContext ctx) {
LOGGER.info("##### begin #####");
TransactionStatus tx = txMgr.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_NESTED));
ctx.transaction(new SpringTransaction(tx));
}
#Override
public void commit(TransactionContext ctx) {
LOGGER.info("##### commit #####");
txMgr.commit(((SpringTransaction) ctx.transaction()).tx);
}
#Override
public void rollback(TransactionContext ctx) {
LOGGER.info("##### rollback #####");
txMgr.rollback(((SpringTransaction) ctx.transaction()).tx);
}
}
I would have expected to see
INFO com.mz.server.web.SpringTransactionProvider - Ctor()
DEBUG com.mz.server.web.servlet.UserServletImpl - Login request by userId: username
INFO com.mz.server.web.SpringTransactionProvider - #### begin ####
INFO com.mz.server.web.service.UserService - Yay!
INFO com.mz.server.web.SpringTransactionProvider - #### commit ####
but I'm only getting
INFO com.mz.server.web.SpringTransactionProvider - Ctor()
DEBUG com.mz.server.web.servlet.UserServletImpl - Login request by userId: username
INFO com.mz.server.web.service.UserService - Yay!
Why does the SpringTrancationProvider not getting utilized?
jOOQ's TransactionProvider provides an implementation for the explicit jOOQ transaction API, wiring it to Spring, which you don't seem to be using. An example addAdmin() method that uses the explicit jOOQ transaction API would be this one:
// no #Transactional - no need for declarative transaction management
public void addAdmin(String userId) {
adminRepository.getCtx().transaction(configuration -> {
DSL.using(configuration)
.insertInto(Admin.ADMIN)
.set(Admin.ADMIN.USER_NAME, userId)
.execute();
});
}
The TransactionProvider is not involved if you use Spring's declarative #Transaction API. In other words, you don't need it.
Related
I've got a problem in my standalone Java application. The thing is that I'm trying to Autowire both my Services and my DAOs, but I get a NullPointerException when I invoke service methods from the UI since dependency injection is not working properly. I've tried a lot of things, many of them from similar questions, but the problem is still there. I'm using Spring 4.0.6.RELEASE and Hibernate 4.3.11.Final. Here is my code:
1 - Invocation of service:
public class LoginView {
#Autowired
private UsuarioService usuarioService;
...
...
JButton btnAutenticarse = new JButton("Autenticar");
btnAutenticarse.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
Usuario usuario = usuarioService.login(textField.getText(),
String.valueOf(passwordField.getPassword()), false); // NullPointerException
} catch (InstanceNotFoundException e1) {
...
2 - Definition of service:
#Service("usuarioService")
public class UsuarioServiceImpl implements UsuarioService {
#Autowired
private UsuarioDao usuarioDao;
...
3 - Definition of DAO:
#Repository("usuarioDao")
public class UsuarioDaoHibernate extends GenericDaoHibernate <Usuario, Long>
implements UsuarioDao {
...
4 - Definition of GenericDAO:
public class GenericDaoHibernate<E, PK extends Serializable> implements
GenericDao<E, PK> {
#Autowired
private SessionFactory sessionFactory;
....
5 - AppConfig.java:
#Configuration
#ComponentScan(basePackages = "org.example.model")
public class AppConfig {
#Bean(name = "usuarioService")
public UsuarioService usuarioService() {
return new UsuarioServiceImpl();
}
#Bean(name = "usuarioDao")
public UsuarioDao usuarioDao() {
return new UsuarioDaoHibernate();
}
6 - spring-config.xml:
<!-- Enable usage of #Autowired. -->
<context:annotation-config/>
<!-- Enable component scanning for defining beans with annotations. -->
<context:component-scan base-package="org.example.model"/>
<!-- For translating native persistence exceptions to Spring's
DataAccessException hierarchy. -->
<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/appdb" />
<property name="username" value="username" />
<property name="password" value="password"/>
</bean>
<bean id="dataSourceProxy" class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"
p:targetDataSource-ref="dataSource"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" >
<property name="dataSource" ref="dataSource"/>
<property name="packagesToScan">
<list>
<value>org.example.model</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">false</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="persistenceExceptionTranslationPostProcessor"
class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>
<!-- Enable the configuration of transactional behavior based on
annotations. -->
<tx:annotation-driven transaction-manager="transactionManager" />
Spring will only inject Autowired fields in Spring managed beans. AKA if you are using new LoginView() Spring cannot inject dependencies.
public class LoginView {
#Autowired
private UsuarioService usuarioService;
}
should be
#Component
public class LoginView {
#Autowired
private UsuarioService usuarioService;
}
If you can't let Spring manage that class, you'll need to design it a different way.
I'd recommend you use constructor injection instead of field injection, by the way.
What I might do is make another class to be a Spring managed bean and do something like this:
#Component
public class InitClass{
private UsarioService usarioService;
#Autowired
public InitClass(UsarioService usarioService){
this.usarioService = usarioService;
}
#PostConstruct
public void init(){
new LoginView(usarioService);
}
}
Then this class will handle all the initialization you're doing now in #PostConstruct. It may be required to do this in #PostConstruct as Spring beans may not be fully initialized until then.
However, without seeing how everything is initialized, it's hard to tell what the best strategy would be.
Make explicit component scan in the application context file. I did like this and it worked for me.
<context:component-scan base-package="com.example" />
You can see more information in the link here
I'm new with Spring and I'm trying to configure it by using the transaction manager of jboss. Indeed, i receive this exception:
Caused by: org.hibernate.resource.transaction.backend.jta.internal.JtaPlatformInaccessibleException: Unable to access TransactionManager or UserTransaction to make physical transaction delegate
This is my config.xml:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:jboss/datasources/mydb"/>
</bean>
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
<property name="persistenceUnitName" value="ufirst" />
</bean>
<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:jboss/TransactionManager" />
<property name="userTransactionName" value="java:jboss/UserTransaction" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />
<tx:jta-transaction-manager />
And this is my repository, that return the error:
#Repository
#Transactional(readOnly = true)
public class RoleRepositoryimpl implements RoleRepository {
private static final Logger logger = LoggerFactory.getLogger(RoleRepositoryJpa.class);
#PersistenceContext(unitName="entityManagerFactory")
private EntityManager em;
#Transactional
public Role find(String role){
logger.info("in repository");
return em.find(Role.class, role);
}
Thanks in advance for your reply.
I have a web application (JSF and Managedbeans) using Mybatis without Spring and I want to use Spring in this web application and integrate mybatis with spring.
I have my mybatis-config.xml and the mappers.
I have mappers in xml and interfaces with the same name of the mappers un xml.
Then the classes that implements the interfaces are something like that.
public class LocalidadPersistence implements LocalidadMapper {
#Override
public List<Localidad> getLocalidades() throws SQLException {
List<Localidad> listaLocalidades = null;
SqlSession session = new MyBatisUtil().getSession();
if (session != null) {
try {
listaLocalidades = session.selectList("com.mybatis.mappers.localidad.getLocalidades");
} catch (Exception ex) {
throw new SQLException("Error obteniendo listado Localidades");
} finally {
session.close();
}
} else {
throw new SqlSessionException("Sesion no encontrada");
}
return listaLocalidades;
}}
MyBatisUtil code is this.
public class MyBatisUtil {
private String resource = "com/mybatis/mybatis-config.xml";
private SqlSession session = null;
public SqlSession getSession(){
try{
Reader reader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader);
session = sqlMapper.openSession();
}catch (IOException ex){
ex.printStackTrace();
}
return session;
}}
I have been reading something about Mybatis spring integration but I don't understand all very well. I know that my spring xml must be something like that. I have not put the mappersLocation property because I have read that this is not neccesary since i have defined my mappers on mybatis-config.xml.
<context:annotation-config />
<context:component-scan base-package="com" />
<tx:annotation-driven />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/DB" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/com/mybatis/mybatis-config.xml" />
<property name="transactionFactory" ref="springManagedTransactionFactory" />
</bean>
<bean id="springManagedTransactionFactory" class="org.mybatis.spring.transaction.SpringManagedTransactionFactory">
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<bean id="registroClimaMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="com.mybatis.dao.RegistroClimaMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
Also I have no idea about how to use this inside my application after it has been configured with spring. What happen with MyBatisUtil class and the implementation of the interfaces?
When you use mybatis-spring, you will get an implementation of mapper interface automatically from the mybatis. So, you can safely use the interface with #Autowired inside any of your spring beans. That's it. Where there is a spring, there a DI. And yes, with mybatis-spring you don't need MybatisUtil at all as all the task are managed in your spring xml .
I have a webapp using Hibernate 4.1 and Spring 3.1 and JSF 1.2 (myFaces).
I have this "LazyInitializationException" each time I try to access one of my pages
Caused by: org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at £org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:149)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:195)
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185)
at foo.data.bo.implementations.EOServiceType_$$_javassist_10.getTechKey(EOServiceType_$$_javassist_10.java)
at foo.converter.EOServiceTypeConverter.getAsString(EOServiceTypeConverter.java:36)
at org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils.getConvertedStringValue(RendererUtils.java:648)
at org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils.getSubmittedOrSelectedValuesAsSet(HtmlRendererUtils.java:362)
at org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils.internalRenderSelect(HtmlRendererUtils.java:337)
at org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils.renderMenu(HtmlRendererUtils.java:288)
at org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlMenuRendererBase.encodeEnd(HtmlMenuRendererBase.java:57)
at org.apache.myfaces.renderkit.html.ext.HtmlMenuRenderer.encodeEnd(HtmlMenuRenderer.java:70)
at javax.faces.component.UIComponentBase.encodeEnd(UIComponentBase.java:649)
... 50 more
I think I missunderstood something because I don't know How to give the "session" to my classes.
For info, here are some of my configuration files :
spring-config.xml:
<context:annotation-config />
<context:component-scan base-package="foo" />
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation">
<value>classpath:hibernate.cfg.xml</value>
</property>
<property name="namingStrategy">
<ref bean="oracleNamingStrategy" />
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<alias name="jndiDataSource" alias="dataSource" />
<bean name="oracleNamingStrategy"
class="org.hibernate.cfg.ImprovedNamingStrategy">
</bean>
<bean name="jndiDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/fooDS</value>
</property>
</bean>
My java class EOServiceType
#Entity
#Table(name="EOSERVICETYPE")
public class EOServiceType implements IEOServiceType {
#Id
#Column(name="EOSERVICETYPE_ID")
private long techKey;
#Column(name="H_PROPERTY")
private String property;
#Column(name="H_DESCRIPTION")
private String description;
//... + all getters and setters
}
My DAO implementation for Hibernate EOServiceTypeDaoHibernateImpl
#Repository("eOServiceTypeDao")
public class EOServiceTypeDaoHibernateImpl implements IEOServiceTypeDao {
#Autowired
private SessionFactory sessionFactory;
public void save(IEOServiceType serviceType) {
sessionFactory.getCurrentSession().save(serviceType);
}
public void update(IEOServiceType serviceType) {
sessionFactory.getCurrentSession().update(serviceType);
}
//... and some other CRUD operations...
}
My POJO Service implementation for Hibernate EOWebStaffServicesImpl
#Service
public class EOWebStaffServicesImpl implements IEOWebStaffServices {
#Autowired
private SessionFactory sessionFactory;
//...
#Autowired
private IEOServiceTypeDao eoServiceTypeDao;
public void saveOrUpdateEOServiceType(IEOServiceType eoServiceType) {
try {
eoServiceTypeDao.saveOrUpdate(eoServiceType);
} catch (DataIntegrityViolationException e) {
DuplicateKeyException exception= new DuplicateKeyException("Duplicate business key for " + eoServiceType,e);
throw exception;
}
}
public void deleteEOServiceType(IEOServiceType eoServiceType) {
eoServiceTypeDao.delete(eoServiceType);
}
My Hibernate Config file :
<hibernate-configuration>
<session-factory>
<property name="hibernate.mapping.precedence">hbm, class</property>
<property name="show_sql">false</property>
<property name="format_sql">true</property>
<property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
<property name="jdbc.batch_size">20</property>
<mapping class="foo.data.bo.implementations.EOServiceType"/>
<!-- ... and other mappings -->
</session-factory>
</hibernate-configuration>
Does any body have a tip to help me ? I read some articles and post but did not really find a solution to my problem.
Best regards,
Kamran
I had the same kind of problem several weeks ago. I obviously forgot to annotate my method which is interacting with Hibernate.
I recommend you the
#Transactional
annotation. It should fix your problem.
Otherwise here is the related Hibernate documentation:
Sessions and Transactions
I think this happens because you don't use transactions in your DAO classes. Hibernate will not work with Spring outside of the transaction. You can define declarative transactions with Spring (annotate necessary classes with #Transactional annotation). Here is a link to Spring reference documentation about Transactions.
Also you should inject SessionFactory to your bean before using it:
#autowired
private SessionFactory sessionFactory;
What are you planing to archive with the SessionFactory in your service class?
Anyway, i think the problem is that you are trying to access a detached object with lazy properties. (That's usually the case when you see that exception)
Is that all the code there is of your POJOs?
My service makes use of a generic DAO (which explicitly uses Hibernate session factory). I have spent some time before I discovered this error
org.hibernate.HibernateException: No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
I annotated my service and all works perfectly. Now I want to use context path scanning, and remove this line from my configuration:
<bean id="societaService" class="it.trew.prove.services.SocietaService" />
So.... here's my final version:
#Service
#Transactional(readOnly = true)
public class SocietaService {
private Dao societaDao;
#Autowired
public void setSocietaDao(Dao societaDao) {
this.societaDao = societaDao;
}
public void salvaSocieta(Societa s) {
societaDao.save(s);
}
public List<Societa> listAll() {
return societaDao.getAll(Societa.class);
}
public void deleteById(Long id) {
societaDao.delete(getSocieta(id));
}
public Societa getSocieta(String id) {
return getSocieta(Long.parseLong(id));
}
public Societa getSocieta(Long id) {
return societaDao.get(Societa.class, id);
}
}
Adding #Service annotation makes my app give the awful hibernate error above. Why?
Removing #Service and configuring the service bean via xml = it works. WHY??
In addition:
does transactional annotating my service class makes all its methods to be executing in a transaction?
EDIT
Here's my context xml:
<context:annotation-config />
<context:component-scan base-package="it.trew.prove" />
<!-- Hibernate -->
<bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.google.appengine.api.rdbms.AppEngineDriver" />
<property name="url" value="jdbc:google:rdbms://xxx:xxx/xxx" />
</bean>
<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="annotatedClasses">
<list>
<value>it.trew.prove.model.beans.Scadenza</value>
<value>it.trew.prove.model.beans.Fornitore</value>
<value>it.trew.prove.model.beans.Societa</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<!-- <prop key="hibernate.hbm2ddl.import_files">/setup.sql</prop> -->
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="mySessionFactory" />
</bean>
Did you enabled annotations ?
<context:annotation-config />
and
<context:component-scan base-package="org.example"/>
this will enable annotations like #Service, #Component and #Repository
First of all, you need to tell spring to enable Transactional annotation with something like that:
<tx:annotation-driven transaction-manager="transactionManager" />
Second, if you use #Transactional at class level, any call to a method of your Service will be transactional. Whether it starts a transaction or not depends on the "propagation" attribute. The default is start one if none started in this session (Propagation.REQUIRED). If you use #Transactional only at class level, all of your methods will inherit its attributes, i.e., if you set "readOnly = true", all of yours methods will be read only, thus, updates/saves/delete won't work. I would recommend you to read more the Spring Transaction Management Doc