I am at my wits end trying to debug this issue. Basically, I have a mvc application where I have a BusinessManagerImpl class which has 2 DAOs (UserDao and ProductDao) and am using JDBC with a connection pool instead of an ORM. Database is mySQL with InnoDb engine. RestUserController is the calling class of BusinessManagerImpl.
BusinessManagerImpl.addUser() has been annotated with #Transactional annotation. I have also tried annotating #Transactional at the class level but doesn't seem to make a difference. Both DAOs are also annotated as such.
BusinessManagerImpl.addUser() uses UserDao to insert a user but a subsequent call to ProductDao.getAllProducts() throws a RuntimeException on purpose to cause the transaction to rollback. My expectation is that the user should not have been inserted as a RuntimeException has occurred and the transaction would have been rolledback but I have checked my database and the new user is inserted.
I have tried with throwing a checked exception and using the "rollback for" parameter of #Transactional annotation but it doesn't work. I have also tried different values of propagation like Propagation.Required but doesn't seem to have an effect on rolling back the transaction. I have tried searching on stackoverflow and google but came up with nothing that could help. Can someone please shed some light on what I am doing wrong or missing something? Thank you. Below is my setup:
application-context.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
<context:component-scan base-package="someproject" />
<!-- <context:annotation-config /> -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/somedb" />
<property name="username" value="xxx" />
<property name="password" value="yyy" />
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
BusinessManagerImpl class
#Service
public class BusinessManagerImpl implements BusinessManager{
#Autowired
private UserDao userDao;
#Autowired
private ProductDao productDao;
....
#Override
#Transactional(propagation=Propagation.REQUIRED)
public User addUser(User user) throws Exception {
// TODO Auto-generated method stub
User tempUser = userDao.addUser(user);
productDao.getAllProducts();
return tempUser;
}
UserDaoImpl class
#Service
public class UserDaoImpl implements UserDao {
private DataSource dataSource;
#Autowired
public UserDaoImpl(DataSource dataSource) {
super();
setDataSource(dataSource);
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
private JdbcTemplate getJdbcTemplate(){
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
return jdbcTemplate;
}
...
#Override
#Transactional(propagation=Propagation.MANDATORY)
public User addUser(final User user) {
KeyHolder holder = new GeneratedKeyHolder();
final String sql = "insert into user (username, password) "
+ " VALUES (?, ?)";
getJdbcTemplate().update(new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement ps = connection.prepareStatement(sql.toString(), Statement.RETURN_GENERATED_KEYS);
int index = 1;
ps.setString(index++, user.getUsername());
ps.setString(index++, user.getPassword());
return ps;
}
}, holder);
int seq = holder.getKey().intValue();
user.setSeq(seq);
return user;
}
ProductDaoImpl class
#Service
public class ProductDaoImpl implements ProductDao {
private DataSource dataSource;
#Autowired
public ProductDaoImpl(DataSource dataSource) {
super();
setDataSource(dataSource);
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
#Transactional(propagation=Propagation.MANDATORY)
public List<Product> getAllProducts() throws Exception {
if(true)
throw new RuntimeException("on purpose");
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
List<Product> products = jdbcTemplate.query(
"select * from product",
new ProductRowMapper());
return products;
}
RestUserController class
#RestController
public class RestUserController {
private static Logger logger = LoggerFactory.getLogger(RestUserController.class);
#Autowired
private BusinessManager businessManager;
#RequestMapping(value = "/adduser", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> createEmployee(#RequestBody User user)
{
logger.debug("adding user:"+user);
User addedUser=null;
try {
addedUser = businessManager.addUser(user);
return new ResponseEntity(addedUser, HttpStatus.CREATED);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return new ResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR);
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<display-name>Spring3 MVC Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/application-context.xml
</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>spring-web</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-web</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
spring-web-servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:component-scan base-package="someproject" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:annotation-driven />
</beans>
I find use of #Service annotation on Dao Implementation class weird. Try replacing them with #Repository and adding rollbackFor = {Exception.class} to all of your transactional annotations.
For your 2 application contexts configs, you have to specify the base-package differently. Do not let the web application context scan DAO package.
Related
So, I am pretty new to Spring MVC, and I think I'm having problems with configuration and I'm going crazy.
I've tried the solution here but either I'm unable to make it work or my mistake is something different
The error I'm getting is:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'servicioPersonalUrgenciasImplementacion': Unsatisfied dependency expressed through field 'dao'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.modelo.dao.PersonalUrgenciasDAO' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
My components are:
Controller. in com.controladores package
#Controller
public class LoginController {
private ServicioPersonalUrgenciasLogin service;
#Autowired
public void setServicioPersonalUrgenciasLogin( ServicioPersonalUrgenciasLogin service) {
this.service = service;
}
#GetMapping("login")
public String login(Model model) {
PersonalUrgenciasLogin login = new PersonalUrgenciasLogin();
//Add customers to the model
model.addAttribute("login", login);
return "login";
}
...
}
Service interface and Service implementation, both in com.modelo.servicios
#Service
public interface ServicioPersonalUrgenciasLogin {
public boolean login(String username, String pwd) throws NoSuchAlgorithmException, UnsupportedEncodingException;
public void cambiarContrasenia(String username, String newpwd);
}
#Service
public class ServicioPersonalUrgenciasLoginImplementacion implements ServicioPersonalUrgenciasLogin {
#Autowired
private PersonalUrgenciasLoginDAO dao;
#Override
#Transactional
public boolean login(String username, String pwd) throws NoSuchAlgorithmException, UnsupportedEncodingException {
return dao.login(username, pwd);
}
#Override
#Transactional
public void cambiarContrasenia(String username, String newpwd) {
// I have to implement this,
}
}
DAO Interface and Implementation, both in com.modelo.dao package
I know this is not the way to implement a login, but I wanted something fast
public interface PersonalUrgenciasLoginDAO {
public boolean login(String username, String pwd) throws NoSuchAlgorithmException, UnsupportedEncodingException;
public void cambiarContrasenia(String username, String newpwd);
}
#Repository
public class PersonalUrgenciasLoginDAOImplementacion implements PersonalUrgenciasLoginDAO{
#Autowired
private SessionFactory factoria;
#Override
public boolean login(String username, String pwd) throws NoSuchAlgorithmException, UnsupportedEncodingException {
Session sesion = factoria.getCurrentSession();
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] hashInOne = md5.digest(pwd.getBytes("UTF-8"));
String hashMD5String = getString(hashInOne);
Query<String> query2 = sesion.createQuery(
"SELECT usuario FROM PersonalUrgencias WHERE contrasenia=: pwd AND usuario =:user", String.class);
query2.setParameter("user", username);
query2.setParameter("pwd", hashMD5String);
List<String> haLogueado = query2.getResultList();
return !haLogueado.isEmpty();
}
#Override
public void cambiarContrasenia(String username, String newpwd) {
// TODO Auto-generated method stub
}
private static String getString( byte[] bytes )
{
StringBuffer sb = new StringBuffer();
for( int i=0; i<bytes.length; i++ )
{
byte b = bytes[ i ];
String hex = Integer.toHexString((int) 0x00FF & b);
if (hex.length() == 1)
{
sb.append("0");
}
sb.append( hex );
}
return sb.toString();
}
My Entity. I know it is not wired, but I don't want it to be
And then, my web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
<display-name>triaje</display-name>
<absolute-ordering />
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
And this is my applicationContext.xml
I've tried just writting com in <context:component-scan...> too
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- Add support for component scanning -->
<context:component-scan base-package="com, com.controladores, com.modelo.dao, com.modelo.entidades, com.modelo.servicios" />
<!-- Add support for conversion, formatting and validation support -->
<mvc:annotation-driven/>
<!-- Define Spring MVC view resolver -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/view/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- Step 1: Define Database DataSource / connection pool -->
<bean id="myDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3316/trj?useSSL=false&serverTimezone=UTC" />
<property name="user" value="root" />
<property name="password" value="root" />
<!-- these are connection pool properties for C3P0 -->
<property name="minPoolSize" value="5" />
<property name="maxPoolSize" value="20" />
<property name="maxIdleTime" value="30000" />
</bean>
<!-- Step 2: Setup Hibernate session factory -->
<bean id="factoria"
class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
<property name="dataSource" ref="myDataSource" />
<property name="packagesToScan" value="com.modelo.entidades" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<!-- Step 3: Setup Hibernate transaction manager -->
<bean id="myTransactionManager"
class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<property name="sessionFactory" ref="factoria"/>
</bean>
<!-- Step 4: Enable configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="myTransactionManager" />
<!-- Add support for reading web resources: css, images, js... -->
<mvc:resources location="/resources/" mapping="/resources/**"></mvc:resources>
</beans>
I'm both grateful and sorry for anybody who has read through all of this
I have a Global Filter with an injected attribute.
public class AuthenticationGlobalFilter implements GlobalFilter, Ordered {
#Autowired
private Permission permission;
#Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long serviceId = 0;
try {
ServerHttpRequest request = exchange.getRequest();
HttpHeaders headers = request.getHeaders();
String fromKey = headers.getFirst("x-api-key");
String fromIp = headers.getFirst("x-forwarded-for");
String fromSubject = headers.getFirst("******");
URI rqUrl = request.getURI();
String path = rqUrl.getPath();
serviceId = Long.parseLong(path.split("/")[6]);
permission.check(permission.identity(fromKey, fromSubject, fromIp), serviceId);
return chain.filter(exchange);
} catch (PermissionException e) {
exchange.getResponse().setStatusCode(HttpStatus.FORBIDDEN);
} catch (IllegalArgumentException e) {
exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
} catch (Exception e) {
exchange.getResponse().setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
}
return Mono.empty();
}
}
Injection works perfectly when I run the application
#EnableDiscoveryClient
#SpringBootApplication
#EnableConfigurationProperties(ApiGatewayProperties.class)
#Import({ PermitAllSecurityConfiguration.class, CustomAttributesConfiguration.class })
#ImportResource(value = { "classpath:datasource.xml", "classpath:api-gateway-unico-beans.xml" })
public class ApiGatewayUnicoApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayUnicoApplication.class, args);
}
}
With this configuration file:
<?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:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:cache="http://www.springframework.org/schema/cache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/cache
http://www.springframework.org/schema/cache/spring-cache.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<cache:annotation-driven
cache-manager="cacheManager" />
<context:annotation-config />
<context:component-scan
base-package="*****.apigatewayunico" />
<bean
class="*****.apigatewayunico.filters.AuthenticationGlobalFilter" />
<bean class="*****.apigatewayunico.Persistence"
p:sql="SELECT rep.url FROM RestService rs, RestEndpoint rep WHERE rep.id = rs.endpoint_id AND rs.id = ? AND rs.available = true" />
<bean id="permission"
class=*****.permission.Permission" />
The problem is that when I make a unit test, the property is injected in the unit test by not in the filter being tested, giving me errors about not being able to locate a bean that can satisfy this dependency.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = RANDOM_PORT)
#DirtiesContext
public class AuthenticationGlobalFilterIntegrationTests extends BaseWebClientTests {
#Autowired
private Permission permission;
#Before
public void setup() {
super.setup();
reset(permission);
permission.check(null, 123456L);
}
#Test
public void runSucessTest() {
expectLastCall();
replay(permission);
testClient.get().uri("/status/200").exchange().expectStatus().isEqualTo(HttpStatus.OK);
}
#Test
public void runPermissionExceptionTest() throws Exception {
expectLastCall().andThrow(new PermissionException(""));
replay(permission);
testClient.get().uri("/status/200").exchange().expectStatus().isEqualTo(HttpStatus.FORBIDDEN);
}
#EnableAutoConfiguration
#SpringBootConfiguration
#Import(DefaultTestConfig.class)
#ImportResource(value = "classpath:authentication-filter-test-beans.xml")
public static class TestConfig {
}
}
authentication-filter-test-beans.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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:api-gateway-unico-test-beans.xml" />
<bean id="authenticationFilter"
class="*****.apigatewayunico.filters.AuthenticationGlobalFilter">
</bean>
</beans>
api-gateway-unico-test-beans.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:c="http://www.springframework.org/schema/c"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-lazy-init="false">
<bean id="mockSupport" class="org.easymock.EasyMockSupport" />
<bean id="pp" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="*****.permission.PermissionPersistence" />
<bean id="permission" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="*****.permission.Permission" />
<bean id="jdbc" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="org.springframework.jdbc.core.JdbcTemplate" />
<bean id="descoveryClienteMock" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="org.springframework.cloud.client.discovery.DiscoveryClient" />
<bean id="serviceInstanceMock" factory-bean="mockSupport"
factory-method="createNiceMock"
c:_0="org.springframework.cloud.client.ServiceInstance" />
</beans>
Now when I try to add the unit test's configuration xml to the #ConextConfiguration annotation in the unit test, I get a test related to Reactor dependencies!
org.springframework.context.ApplicationContextException: Unable to
start reactive web server
Solved by annotating the dependency with #Lazy
I am using spring declarative Transaction features. Something like this
XML file for spring configuration..
<?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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:annotation-config/>
<context:component-scan base-package="com" />
<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/test" />
<property name="username" value="root" />
<property name="password" value="password" />
</bean>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver" >
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<!-- Add this tag to enable annotations transactions -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
Dao Layer code
package com.dao;
#Repository("commonDao")
public class CommonDaoImpl implements CommonDao {
private static Logger logger = Logger.getLogger(CommonDaoImpl.class);
#Autowired
private DataSource dataSource;
private JdbcTemplate jdbcTemplate;
#Autowired
public void setSessionFactory(){
jdbcTemplate = new JdbcTemplate(dataSource);
}
#Override
public Object makePayment(Object e) {
String sql = "insert into payment (id, name, amount) values('abc', 100)";
try{
return jdbcTemplate.update(sql);
}catch(DataAccessException ex){
throw ex;
}
}
#Override
public Object signUp(Object e) {
String sql = "insert into login (userid, password) values('naveen', 'password')";
return jdbcTemplate.update(sql);
}
}
Service Layer code
#Service
public class CommonServiceImpl implements CommonService {
#Autowired
private CommonDao commonDao;
// #Transactional I tried both of them one by one but not worked
#Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
#Override
public boolean makePayment() {
try{
commonDao.signUp(new Object());
commonDao.makePayment(new Object());
} catch(DataAccessException ex){
return false;
}
return true;
}
}
but when i send call makePayment() method via controller then it save the record into login table but failed when it move to insert into payment table because i write query so that it can through an exception. I do not understand why transaction is not working. because #Transactional annotation is on makePayment method so not operation should happen in db.
Please tell what's wrong in this code.
Your SQL query in in makePayment method is :
insert into payment (id, name, amount) values('abc', 100)
You are asking the query to insert id, name and amount in the database table, but providing only 2 values i.e. abc and 100.
If id column in the table is autoincrement id then you don't need to mention it in the SQL query. Your SQL query in the makePayment method should be like this:
insert into payment (name, amount) values('abc', 100)
I have this test:
#ContextConfiguration(locations = {"classpath:/test/BeanConfig.xml"})
public class CandidateServiceTest extends AbstractTransactionalJUnit4SpringContextTests{
#Autowired
CandidateService candidateService;
#BeforeClass
public static void initialize() throws Exception{
UtilMethods.createTestDb();
}
#Before
public void setup() {
TestingAuthenticationToken testToken = new TestingAuthenticationToken("testUser", "");
SecurityContextHolder.getContext().setAuthentication(testToken);
}
#After
public void cleanUp() {
SecurityContextHolder.clearContext();
}
#Test
public void add(){
Candidate candidate = new Candidate();
candidate.setName("testUser");
candidate.setPhone("88888");
candidateService.add(candidate);//here I should add data to database
List<Candidate> candidates = candidateService.findByName(candidate.getName());
Assert.assertNotNull(candidates);
Assert.assertEquals("88888", candidates.get(0).getPhone());
}
}
add function:
#Transactional
#Service("candidateService")
public class CandidateService {
public void add(Candidate candidate) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String login = auth.getName();
User user = utilService.getOrSaveUser(login);
candidate.setAuthor(user);
candidateDao.add(candidate);
}
...
}
add function in dao:
public Integer add(Candidate candidate) throws HibernateException{
Session session = sessionFactory.getCurrentSession();
if (candidate == null) {
return null;
}
Integer id = (Integer) session.save(candidate);
return id;
}
Usually candidateService.add(candidate) adds to the database normally, but in test it doesn't add to database. I checked the database after test to see it.
What could be the problem?
UPDATE
configuration:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang"
xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<!-- Настраивает управление транзакциями с помощью аннотации #Transactional -->
<tx:annotation-driven transaction-manager="transactionManager" />
<!-- Менеджер транзакций -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- Непосредственно бин dataSource -->
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
p:driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
p:url="jdbc:sqlserver://10.16.9.52:1433;databaseName=hhsystemTest;"
p:username="userNew"
p:password="Pass12345" />
<!-- Настройки фабрики сессий Хибернейта -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation">
<value>classpath:test/hibernate.cfg.xml</value>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
<prop key="hibernate.connection.charSet">UTF-8</prop>
<!-- <prop key="hibernate.hbm2ddl.auto">create-drop</prop> -->
</props>
</property>
</bean>
</beans>
Try adding the #Transactional annotation around the method in your test class.
This annotation creates a session that can interact with the database.
Issue a flush after the candidateService.add. There is a transaction around the test method, a commit (and flush) is only happening after the method execution. You have to 'fake' the commit by flushing the session. Simply inject the SessionFactory in your test class and call flush on the current session.
#ContextConfiguration(locations = {"classpath:/test/BeanConfig.xml"})
public class CandidateServiceTest extends AbstractTransactionalJUnit4SpringContextTests{
#Autowired
private SessionFactory sessionFactory;
#Test
public void add(){
Candidate candidate = new Candidate();
candidate.setName("testUser");
candidate.setPhone("88888");
candidateService.add(candidate);//here I should add data to database
sessionFactory.getCurrentSession().flush(); //execute queries to database
List<Candidate> candidates = candidateService.findByName(candidate.getName());
Assert.assertNotNull(candidates);
Assert.assertEquals("88888", candidates.get(0).getPhone());
}
}
And ofcourse make sure that your findByName method is also implemented correctly and using the same hibernate session!
You must auto wire your sessionDactory instance in the DAO using #Autowired And I would place the #Transactional annotation within each method of the DAO, rather than before he class definition of a service class. This is normally the way that it is done in annotation-driven Spring application.
I'm having trouble doing a Spring (using 3.0.5.RELEASE) mapping. I want to map the URL http://mydomain/context-path/user/registrationform.jsp to my JSP page at
/WEB-INF/views/user/registrationform.jsp
but I'm getting a 404. I have my controller setup like so …
#Controller
#RequestMapping("registrationform.jsp")
public class RegistrationController {
private static Logger LOG = Logger.getLogger(RegistrationController.class);
…
public void setRegistrationValidation(
RegistrationValidation registrationValidation) {
this.registrationValidation = registrationValidation;
}
// Display the form on the get request
#RequestMapping(method = RequestMethod.GET)
public String showRegistration(Map model) {
final Registration registration = new Registration();
model.put("registration", registration);
return "user/registrationform";
}
here is my dispatcher-servlet.xml …
<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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
<!-- Enable annotation driven controllers, validation etc... -->
<mvc:annotation-driven />
<context:component-scan base-package="com.burrobuie.eventmaven" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<property name="basename" value="/messages" />
</bean>
</beans>
and here is my web.xml …
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
What else do I need to configure (or configure differently) to make this mapping work? This is harder than - Dave
#Controller
#RequestMapping("registrationform.jsp")
public class RegistrationController {
The RequestMapping annotation at class level should be use for a common url pattern like "item/*" and all the links that contains "/item" followed by other pattern would be mapped it to the controller. "user/" in your case
The RequestMapping annotation at method level is used for mapping the sub URL like "item/add" or "item/delete", "registrationform.jsp' in your case
So try this:
#Controller
#RequestMapping("/user")
public class RegistrationController {
private static Logger LOG = Logger.getLogger(RegistrationController.class);
…
public void setRegistrationValidation(
RegistrationValidation registrationValidation) {
this.registrationValidation = registrationValidation;
}
// Display the form on the get request
#RequestMapping(value="/registrationform.jsp",method = RequestMethod.GET)
public String showRegistration(Map model) {
final Registration registration = new Registration();
model.put("registration", registration);
return "user/registrationform";
}
This will map /user/registrationform.jsp
Change the RequestMapping to:
#RequestMapping("/user/registrationform.jsp")