I use JHipster to make a simple app and write a service, code as follows:
#Service
#Transactional
public class OperateQueueActionService {
#Transactional(rollbackFor = Throwable.class)
public OperateQueueDTO apply(OperateQueueDTO operateQueueDTO, QueueEventType queueEventType, String deskNo) {
StateMachine<QueueStatus, QueueEventType> stateMachine = operateQueueActionMachineService.getStateMachine();
try { QueueEventDTO operateQueueEventDTO = operateQueueUtils.saveQueueEvent(operateQueueDTO, queueEventType, deskNo);
......in process will throw RuntimeException.
} finally {
stateMachine.stop();
}
}
}
I want the transaction to rollback and not save queueEvent, but the record is saved in the database. This is the transaction log:
[2018-07-24 12:04:51.861] [XNIO-2 task-6] WARN
o.s.s.l.CompositeStateMachineListener -Error during stateContext
java.lang.RuntimeException: 无效状态 at
com.higoee.queue.state.utils.OperateQueueStateMachineLogListener.stateContext(OperateQueueStateMachineLogListener.java:58)
at java.lang.Thread.run(Thread.java:745) [2018-07-24 12:04:51.871]
[XNIO-2 task-6] INFO c.h.q.s.u.OperateQueueStateMachineLogListener
-4d667539-3316-491d-a0db-240e29b0fcae状态机状态为:STATEMACHINE_STOP [2018-07-24 12:04:51.872] [XNIO-2 task-6] DEBUG
o.s.orm.jpa.JpaTransactionManager -Initiating transaction commit
[2018-07-24 12:04:51.874] [XNIO-2 task-6] DEBUG
o.s.orm.jpa.JpaTransactionManager -Committing JPA transaction on
EntityManager
JPA config have not problem,spring state machine had been catch the exception which was thrown in StateMachineListenerAdapter.
Related
I'm using Spring Webflux and as I understand it, by using this, the thread used for receiving request and the one used for response should be different. However, whether I use netty or undertow, I end up using the same thread.
My app is a simple crud app with MySQL DB. I'm not using r2dbc but a jdbc coupled with Executor and Scheduler.
As shown in the log below, request is handled by thread [ XNIO-1 I/O-6] and the response is given by the same one.
By this, I'm assuming the thread is blocked until db operation has finished. How can I fix this?
Here's the log
2019-07-23 17:49:10.051 INFO 132 --- [ main] org.xnio : XNIO version 3.3.8.Final
2019-07-23 17:49:10.059 INFO 132 --- [ main] org.xnio.nio : XNIO NIO Implementation Version 3.3.8.Final
2019-07-23 17:49:10.114 INFO 132 --- [ main] o.s.b.w.e.undertow.UndertowWebServer : Undertow started on port(s) 8080 (http)
2019-07-23 17:49:10.116 INFO 132 --- [ main] c.n.webflux.demo.WebfluxFunctionalApp : Started WebfluxFunctionalApp in 1.262 seconds (JVM running for 2.668)
2019-07-23 17:49:10.302 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.s.adapter.HttpWebHandlerAdapter : [4c85975] HTTP GET "/api/findall"
2019-07-23 17:49:10.322 DEBUG 132 --- [ XNIO-1 I/O-6] s.w.r.r.m.a.RequestMappingHandlerMapping : [4c85975] Mapped to public reactor.core.publisher.Mono<java.util.List<com.webflux.demo.model.TypeStatus>> com.webflux.demo.controller.MonitoringController.findAll()
2019-07-23 17:49:10.337 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.r.r.m.a.ResponseBodyResultHandler : Using 'application/json;charset=UTF-8' given [*/*] and supported [application/json;charset=UTF-8, application/*+json;charset=UTF-8, text/event-stream]
2019-07-23 17:49:10.338 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.r.r.m.a.ResponseBodyResultHandler : [4c85975] 0..1 [java.util.List<com.webflux.demo.model.TypeStatus>]
2019-07-23 17:49:10.347 INFO 132 --- [pool-1-thread-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-07-23 17:49:10.785 INFO 132 --- [pool-1-thread-1] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-07-23 17:49:10.838 DEBUG 132 --- [pool-1-thread-1] org.springframework.web.HttpLogging : [4c85975] Encoding [[com.webflux.demo.model.TypeStatus#7b4509cb, com.webflux.demo.model.TypeStatus#22676ebe, (truncated)...]
2019-07-23 17:49:10.949 DEBUG 132 --- [ XNIO-1 I/O-6] o.s.w.s.adapter.HttpWebHandlerAdapter : [4c85975] Completed 200 OK
Also my dao is
#Repository
public class TypeStatusJdbcTemplate {
private JdbcTemplate jdbcTemplate;
public TypeStatusJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private final static String SQL_FIND_ALL = "select * from `monitoring`.`type_status` limit 3";
public List<TypeStatus> findAll() {
return jdbcTemplate.query(SQL_FIND_ALL,
new TypeStatusMapper());
}
}
service is
#Service
public class MonitoringService {
private final Scheduler scheduler;
private TypeStatusJdbcTemplate repository;
public MonitoringService(Scheduler scheduler, TypeStatusJdbcTemplate repository) {
this.scheduler = scheduler;
this.repository = repository;
}
public Mono<List<TypeStatus>> findAll() {
return Mono.fromCallable(repository::findAll).subscribeOn(scheduler);
}
}
controller is
#RestController
#RequestMapping("/api")
public class MonitoringController {
private final MonitoringService monitoringService;
private static final Logger logger = LoggerFactory.getLogger(MonitoringController.class);
public MonitoringController(MonitoringService monitoringService) {
this.monitoringService = monitoringService;
}
#GetMapping(value="/findall")
public Mono<List<TypeStatus>> findAll() {
return monitoringService.findAll();
}
}
main file (showing scheduler)
#SpringBootApplication
public class WebfluxFunctionalApp {
public static void main(String[] args){
SpringApplication.run(WebfluxFunctionalApp.class, args);
}
#PostConstruct
public void init(){
// Setting Spring Boot SetTimeZone
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
}
#Bean
public Scheduler jdbcScheduler() {
return Schedulers.fromExecutor(Executors.newFixedThreadPool(30));
}
}
Thread execution does not always have to be different threads. Taken from the Reactive documentation:
Reactive Schedulers
Obtaining a Flux or a Mono doesn’t necessarily mean it will run in a dedicated Thread. Instead, most operators continue working in the Thread on which the previous operator executed. Unless specified, the topmost operator (the source) itself runs on the Thread in which the subscribe() call was made.
So there is nothing that says that it has to be a new thread.
Why even if exception is thrown data is not rolledback from database
I set rollbackFor = Exception.class and it still doesn't rollback data when second visit is put into databse.
#EnableTransactionManagement
#Service
public class VisitService {
private final static Logger logger = LoggerFactory.getLogger(VisitService.class);
private final VisitRepository visitRepository;
public VisitService(VisitRepository visitRepository) {
this.visitRepository = visitRepository;
}
public List<Visit> findAllByDate(LocalDateTime date){
return visitRepository.findAllByDate(date);
}
#Transactional(isolation = Isolation.READ_COMMITTED, rollbackFor = RuntimeException.class, propagation = Propagation.REQUIRED)
public Visit save(#Valid Visit visit) {
logger.info("Inside visitService");
visit.setPayed(false);
if (visit.getPrice().intValue() > 1000)
visit.setDiscount(new BigDecimal(0.05));
else visit.setDiscount(new BigDecimal(0.00));
visitRepository.save(visit);
if(findAllByDate(visit.getDate()).size()>1)
throw new RuntimeException();
return null;
}
}
tried with configuration to transaction
#Configuration
#ConditionalOnClass({PlatformTransactionManager.class})
#AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
#EnableConfigurationProperties({TransactionProperties.class})
public class TransactionAutoConfiguration {
}
part of stack trace
2018-12-08 23:16:23.653 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
Hibernate: insert into visits (visit_date, description, discount, payed, pet_id, price, vet_id) values (?, ?, ?, ?, ?, ?, ?)
2018-12-08 23:16:23.663 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.save]
2018-12-08 23:16:23.664 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor : Getting transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAllByDate]
Hibernate: select visit0_.id as id1_6_, visit0_.visit_date as visit_da2_6_, visit0_.description as descript3_6_, visit0_.discount as discount4_6_, visit0_.payed as payed5_6_, visit0_.pet_id as pet_id6_6_, visit0_.price as price7_6_, visit0_.vet_id as vet_id8_6_ from visits visit0_ where visit0_.visit_date=?
2018-12-08 23:16:23.685 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.data.jpa.repository.support.SimpleJpaRepository.findAllByDate]
2018-12-08 23:16:23.685 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.TransactionInterceptor : Completing transaction for [org.springframework.samples.petclinic.services.VisitService.save] after exception: java.lang.RuntimeException
2018-12-08 23:16:23.685 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.RuleBasedTransactionAttribute : Applying rules to determine whether transaction should rollback on java.lang.RuntimeException
2018-12-08 23:16:23.685 TRACE 3360 --- [nio-8080-exec-2] o.s.t.i.RuleBasedTransactionAttribute : Winning rollback rule is: RollbackRuleAttribute with pattern [java.lang.RuntimeException]
2018-12-08 23:16:23.690 DEBUG 3360 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Failed to complete request: java.lang.RuntimeException
2018-12-08 23:16:23.698 ERROR 3360 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.RuntimeException] with root cause
java.lang.RuntimeException: null
There is visitRepository with default annnotations
VisitRepository :
public interface VisitRepository extends Repository<Visit, Integer> {
Visit save(Visit visit) throws DataAccessException;
List<Visit> findByPetId(Integer petId);
Visit findById(Integer visitId);
#Transactional(readOnly = true)
List<Visit> findAllByDate(LocalDateTime date);
Visit findByDate(LocalDateTime date);
}
In my spring-boot application I have problem with rollback in transaction method. For test purpose I have this example:
#Transactional(rollbackFor = Exception.class)
public void insertPersonSaveLog() {
refService.insertPerson();
String a = null;
a.getBytes();
auditService.editRequest();
}
in log I can see that rollback should be perform but person is still saved in my db. Maybe I am missing some annotation. This is Main Class:
#SpringBootApplication
#EnableScheduling
public class MyApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) {
new SpringApplicationBuilder(MyApplication.class).run(args);
}
}
and stacktrace:
149254 DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction commit
149254 DEBUG org.springframework.transaction.jta.JtaTransactionManager - Creating new transaction with name [..]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; '',-java.lang.Exception
149254 DEBUG org.springframework.transaction.jta.JtaTransactionManager - Participating in existing transaction
149258 DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL query
149258 DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement
149258 DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Fetching JDBC Connection from DataSource
149258 DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Registering transaction synchronization for JDBC Connection
149260 DEBUG org.springframework.jdbc.core.JdbcTemplate - Executing prepared SQL statement
149262 DEBUG org.springframework.jdbc.datasource.DataSourceUtils - Returning JDBC Connection to DataSource
149262 DEBUG org.springframework.transaction.jta.JtaTransactionManager - Initiating transaction rollback
java.lang.NullPointerException: null
at org.my.service.InserPersonService.insertPersonSaveLog(InserPersonService.java:77)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:720)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:655)
DbConfig:
#Bean
#ConfigurationProperties(prefix = "datasource.ref")
#Primary
public DataSource refDataSource() {
return new DataSource();
}
#Bean
public JdbcTemplate refJdbcTemplate() {
return new JdbcTemplate(refDataSource());
}
#Bean
#ConfigurationProperties(prefix = "datasource.log")
public DataSource logDataSource() {
return new DataSource();
}
#Bean
public JdbcTemplate logJdbcTemplate() {
return new JdbcTemplate(logDataSource());
}
and application.properties:
datasource.ref.url=jdbc:oracle:thin:#something:1521:ora11g
datasource.ref.username=...
datasource.ref.password=...
datasource.ref.driver-class-name=oracle.jdbc.OracleDriver
datasource.log.url=jdbc:oracle:thin:#something:1521:ora11g
datasource.log.username=...
datasource.log.password=...
datasource.log.driver-class-name=oracle.jdbc.OracleDriver
I am participating into a code swap type challenge, for Java and Spring, and I am having some issues adding in a Search feature for this shopping list app. Before I get into depth of code, by search feature I mean search the local database, not something like google. Now I'l show the code and then explain what I'm trying to do underneath it all.
This is the error message:
2016-12-14 10:00:40.476 WARN 10452 --- [ main] o.h.b.i.SessionFactoryBuilderImpl : Unrecognized hbm2ddl_auto value : auto. Supported values include create, create-drop, update, and validate. Ignoring
2016-12-14 10:00:42.300 INFO 10452 --- [ main] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2016-12-14 10:00:46.009 WARN 10452 --- [ main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'shoppingListController': Unsatisfied dependency expressed through field 'shoppingListRepo'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shoppingListRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property category found for type ShoppingList!
2016-12-14 10:00:46.014 INFO 10452 --- [ main] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2016-12-14 10:00:46.071 INFO 10452 --- [ main] o.apache.catalina.core.StandardService : Stopping service Tomcat
2016-12-14 10:00:46.393 INFO 10452 --- [ main] utoConfigurationReportLoggingInitializer :
Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2016-12-14 10:00:46.455 ERROR 10452 --- [ main] o.s.boot.SpringApplication : Application startup failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'shoppingListController': Unsatisfied dependency expressed through field 'shoppingListRepo'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'shoppingListRepository': Invocation of init method failed; nested exception is org.springframework.data.mapping.PropertyReferenceException: No property category found for type ShoppingList!
This is the part of the controller I am editing:
#GetMapping("/lists")
public String lists(Model model, #RequestParam(name = "srch-term", required = false) String searchTerm) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String email = auth.getName();
User u = userRepo.findOneByEmail(email);
if (searchTerm == null || "".equals(searchTerm)) {
model.addAttribute("lists", shoppingListRepo.findAllByUser(u));
} else {
ArrayList<ShoppingList> userLists = new ArrayList<ShoppingList>();
ArrayList<ShoppingList> lists = shoppingListRepo.findByCategoryContainsOrNameContainsAllIgnoreCase(searchTerm,
searchTerm);
for (ShoppingList list : lists) {
if (list.getUser() == u) {
userLists.add(list);
}
}
model.addAttribute("lists", shoppingListRepo.findAllByUser(u));
model.addAttribute("user", u);
}
return "lists";
}
This is the controller before being edited by me
#GetMapping("/lists")
public String lists(Model model) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String email = auth.getName();
User u = userRepo.findOneByEmail(email);
model.addAttribute("lists", shoppingListRepo.findAllByUser(u));
model.addAttribute("user", u);
return "lists";
}
This is my Repository
package org.elevenfifty.shoppinglist.repositories;
import java.util.ArrayList;
import org.elevenfifty.shoppinglist.beans.ShoppingList;
import org.elevenfifty.shoppinglist.beans.User;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface ShoppingListRepository extends CrudRepository<ShoppingList, Long> {
ArrayList<ShoppingList> findAllByUser(User u);
ArrayList<ShoppingList> findByCategoryContainsOrNameContainsAllIgnoreCase(String categoryPart, String namePart);
}
Now like I said I am getting a search feature working. I am not sure where the property is in my error message I am hoping some fresh eyes can help find what I am clearly missing.
Add category field and its getters and setters to the ShoppingList class.
private String category
public void setCategory(String category){
this.category = category;
}
public String getCategory(){
return category;
}
I use Kundera-Cassandra 3.2 and want to use the transaction management from Kundera.
My handling looks like this:
EntityManager manager = repo.getEntityManagerFactory().createEntityManager(CassandraRepository.getProperties());
try{
manager.getTransaction().begin();
this.repo.update(account1, manager); //calls the merge method of the Entitymanager
this.repo.save(account2, manager); //calls the persist method of the Entitymanager
manager.getTransaction().commit();
} catch(Exception e){
if(manager.getTransaction().isActive()){
manager.getTransaction().rollback();
}
} finally {
manager.clear();
manager.close();
}
When an error in the this.repo.save(account2, manager); occurs, the manager rollbacks the transaction, but does not do a update statement, he makes a delete statement for the merge method. The reason for this is, when calling the merge methode, kundera creates an insert statement and not an update. But how to say Kundera to make an update to rollback the transaction also with an update.
Logs:
12:42:41.185 [http-bio-8080-exec-3] INFO com.impetus.client.cassandra.CassandraClientBase - Returning delete query DELETE FROM "account" WHERE "id" = 'MCSP-000000000004'.
12:42:41.211 [http-bio-8080-exec-3] INFO com.impetus.client.cassandra.CassandraClientBase - Returning delete query DELETE FROM "account" WHERE "id" = 'MCSP-000000000005'.
EDIT (my repository):
public class CassandraRepository<T> {
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
public static Map<String, String> getProperties() {
final Map<String, String> properties = new HashMap<String, String>();
properties.put(CassandraConstants.CQL_VERSION, CassandraConstants.CQL_VERSION_3_0);
return properties;
}
public void update(T entity, EntityManager manager) throws Exception{
try {
manager.merge(entity);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
public void save(T entity, EntityManager manager) throws Exception{
try {
manager.persist(entity);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
According to JPA, to update an entity you have to first bring it into managed state (by fetching it)
Example:-
PersonCassandra p = entityManager.find(PersonCassandra.class, "2");
entityManager.getTransaction().begin();
p.setMonth(Month.JAN);
entityManager.merge(p);
entityManager.persist(p3);
entityManager.getTransaction().commit();
Issue is not with INSERT and UPDATE statements since both are similar for Cassandra, under the hood.