TL;DR:
Service method annotated with #Transactional(propagation = Propagation.NOT_SUPPORTED)
Hibernate 5.0.4.Final: everything works as expected (method is executed without transaction)
Hibernate 5.2.5.Final: javax.persistence.TransactionRequiredException: no transaction is in progress is thrown
as a testcase for this issue I created a simple maven web app and the only change made in code (copy-paste from old working project) was Hibernate version bump in pom.xml
Question:
What is the proper way to execute service methods without transaction nowadays?
Code snippets (Spring used as a main framework):
DAO:
#Repository
public class UrlDaoImpl implements UrlDao {
#Autowired
private SessionFactory sessionFactory;
#Override
public List<Url> getAllUrls() {
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("from Url");
return query.list();
}
}
Service:
#Service
public class UrlServiceImpl implements UrlService {
#Autowired
private UrlDao urlDao;
#Override
#Transactional // THIS WORKS IN NEW HIBERNATE
public List<Url> getAllUrls() {
return urlDao.getAllUrls();
}
#Override
#Transactional(propagation = Propagation.NOT_SUPPORTED) // THIS USED TO WORK BUT NOW THROWS EXCEPTION
public List<Url> getAllUrlsNoTxn() {
return urlDao.getAllUrls();
}
}
Controller:
#Controller
public class HomeController {
#Autowired
private UrlService urlService;
#RequestMapping(value = "/", method = RequestMethod.GET, produces = "text/plain")
public String entryPoint() {
urlService.getAllUrls();
System.out.println("--------------------- ok");
return "ok";
}
#RequestMapping(value = "/no-txn", method = RequestMethod.GET, produces = "text/plain")
public String entryPointNoTxn() {
// EXCEPTION WILL BE THROWN BELOW
urlService.getAllUrlsNoTxn();
System.out.println("--------------------- ok no txn");
return "ok no txn";
}
}
Stacktrace for the exception in new Hibernate:
exception
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.persistence.TransactionRequiredException: no transaction is in progress
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
root cause
javax.persistence.TransactionRequiredException: no transaction is in progress
org.hibernate.internal.SessionImpl.checkTransactionNeeded(SessionImpl.java:3439)
org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1410)
org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1406)
org.springframework.orm.hibernate5.SessionFactoryUtils.flush(SessionFactoryUtils.java:144)
org.springframework.orm.hibernate5.SpringSessionSynchronization.beforeCommit(SpringSessionSynchronization.java:95)
org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCommit(TransactionSynchronizationUtils.java:95)
org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCommit(AbstractPlatformTransactionManager.java:932)
org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:744)
org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730)
org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:504)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:292)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
com.sun.proxy.$Proxy32.getAllUrlsNT(Unknown Source)
com.example.web.controller.HomeController.entryPointNoTxn(HomeController.java:31)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:498)
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:220)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:134)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
Ok, after few hours of trying different configurations (before posting this question to SO), I finally found the solution.
For new Hibernate versions there is another required parameter that must be declared on #Transactional if you want to execute a method without a transaction: readOnly = true. So the working example of the Service part is:
#Service
public class UrlServiceImpl implements UrlService {
#Autowired
private UrlDao urlDao;
#Override
#Transactional
public List<Url> getAllUrls() {
return urlDao.getAllUrls();
}
#Override
#Transactional(readOnly = true, propagation = Propagation.NOT_SUPPORTED) // ADDED READONLY TO WORK IN NEW HIBERNATE VERSIONS
public List<Url> getAllUrlsNoTxn() {
return urlDao.getAllUrls();
}
}
I also confirmed that this works on debug by calling ((org.hibernate.engine.transaction.internal.TransactionImpl) session.getTransaction()).isActive(); which returns true for the first Service method (with transaction) and false for the second Service method (with Propagation.NOT_SUPPORTED).
Related
I need to add additional business-logic for POST method. For now, I'm going to reuse the logic from RepositoryEntityController for getting and save the needed object.
#RepositoryRestController
#RequestMapping("/customPost")
public class UserController implements ApplicationEventPublisherAware {
private final UserRepository userRepository;
private final RepositoryRestConfiguration config;
private final HttpHeadersPreparer headersPreparer;
private ApplicationEventPublisher publisher;
#Autowired
public UserController(UserRepository userRepository, RepositoryRestConfiguration config, HttpHeadersPreparer headersPreparer) {
this.userRepository = userRepository;
this.config = config;
this.headersPreparer = headersPreparer;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
#ResponseBody
#RequestMapping(
value = {"/{repository}"},
method = {RequestMethod.POST}
)
public ResponseEntity<ResourceSupport> postCollectionResource(PersistentEntityResource payload, PersistentEntityResourceAssembler assembler, #RequestHeader(value = "Accept", required = false) String acceptHeader) throws HttpRequestMethodNotSupportedException {
return this.createAndReturn(payload.getContent(), assembler, this.config.returnBodyOnCreate(acceptHeader));
}
private ResponseEntity<ResourceSupport> createAndReturn(Object domainObject, PersistentEntityResourceAssembler assembler, boolean returnBody) {
publisher.publishEvent(new BeforeCreateEvent(domainObject));
Object savedObject = userRepository.save((User) domainObject);
publisher.publishEvent(new AfterCreateEvent(savedObject));
PersistentEntityResource resource = returnBody ? assembler.toFullResource(savedObject) : null;
HttpHeaders headers = headersPreparer.prepareHeaders(resource);
addLocationHeader(headers, assembler, savedObject);
return ControllerUtils.toResponseEntity(HttpStatus.CREATED, headers, resource);
}
private void addLocationHeader(HttpHeaders headers, PersistentEntityResourceAssembler assembler, Object source) {
String selfLink = assembler.getSelfLinkFor(source).getHref();
headers.setLocation((new UriTemplate(selfLink)).expand(new Object[0]));
}
}
The code which I sent - is working ok. But the issue is that I need to add some request mapping #RequestMapping("/customPost") to controller.
Without this mapping - method will not work. I tried to have the same controller but without "/customPost" mapping. I got this exception at start of applicaton:
caused by: java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'repositoryEntityController' method
public org.springframework.http.ResponseEntity<org.springframework.hateoas.ResourceSupport> org.springframework.data.rest.webmvc.RepositoryEntityController.postCollectionResource(org.springframework.data.rest.webmvc.RootResourceInformation,org.springframework.data.rest.webmvc.PersistentEntityResource,org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler,java.lang.String) throws org.springframework.web.HttpRequestMethodNotSupportedException
to {[/{repository}],methods=[POST],produces=[application/hal+json || application/json]}: There is already 'userController' bean method
public org.springframework.http.ResponseEntity<org.springframework.hateoas.ResourceSupport> com.project.controller.UserController.postCollectionResource(org.springframework.data.rest.webmvc.PersistentEntityResource,org.springframework.data.rest.webmvc.PersistentEntityResourceAssembler,java.lang.String) throws org.springframework.web.HttpRequestMethodNotSupportedException mapped.
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.assertUniqueMethodMapping(AbstractHandlerMethodMapping.java:576) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry.register(AbstractHandlerMethodMapping.java:540) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.handler.AbstractHandlerMethodMapping.registerHandlerMethod(AbstractHandlerMethodMapping.java:264) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
I also tried to remove "/customPost" mapping from controller and change mapping in metod to "/users". But for this case, I have this exception:
java.lang.NullPointerException: null
at org.springframework.data.rest.webmvc.config.RootResourceInformationHandlerMethodArgumentResolver.resolveArgument(RootResourceInformationHandlerMethodArgumentResolver.java:86) ~[spring-data-rest-webmvc-2.6.10.RELEASE.jar:na]
at org.springframework.data.rest.webmvc.config.PersistentEntityResourceHandlerMethodArgumentResolver.resolveArgument(PersistentEntityResourceHandlerMethodArgumentResolver.java:113) ~[spring-data-rest-webmvc-2.6.10.RELEASE.jar:na]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:158) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128) ~[spring-web-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) ~[tomcat-embed-core-8.5.27.jar:8.5.27]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) ~[spring-webmvc-4.3.14.RELEASE.jar:4.3.14.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) ~[tomcat-embed-core-8.5.27.jar:8.5.27]
The question is: how can I have needed logic without adding "/customPost" mapping?
Spring Data REST emits its own events when working with entities:
BeforeCreateEvent
AfterCreateEvent
BeforeSaveEvent
AfterSaveEvent
BeforeLinkSaveEvent
AfterLinkSaveEvent
BeforeDeleteEvent
AfterDeleteEvent
You can listen to these events to add your additional business logic. For example, to perform something after the SDR creates a new User (with POST method) or saves (updates) the existent User (with PUT/PATCH methods), you can use such a handler:
#Component
#RepositoryEventHandler
public class UserEventHandler {
private final UserServie userServie;
public UserEventHandler(UserServie userServie) {
this.userServie = userServie;
}
#HandleAfterCreate
public void handleAfterCreateUser(User user) {
userService.afterCreate(user)
}
#HandleAfterSave
public void handleAfterSaveUser(User user) {
userService.afterSave(user)
}
}
P.S. If I'm not mistaken, SDR emits these events outside the current transactions, only before it is started or after it is committed. You need to take this into account when implementing your business logic...
NOTE: This may sound a duplicate but I have everything working from other threads
I am implementing my own dao classes with spring and I am not able to configure spring with hibernate properly or maybe something else is at fault.
I am using java based configuration but still getting error. Please have a look
Hibernate configuration:
#Configuration
#ComponentScan("com.caveatemptor")
#EnableJpaRepositories
#EnableTransactionManagement
public class SpringDataJPAConfiguration {
private Logger LOG = LoggerFactory.getLogger(SpringDataJPAConfiguration.class);
// #Bean(destroyMethod = "close")
// public BasicDataSource dataSource() {
// BasicDataSource basicDataSource = new BasicDataSource();
// basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
// basicDataSource.setUrl("jdbc:mysql://localhost/ce");
// basicDataSource.setUsername("root");
// basicDataSource.setPassword("root");
// basicDataSource.setInitialSize(1);
// basicDataSource.setMaxIdle(5);
// basicDataSource.setMaxOpenPreparedStatements(10);
// LOG.debug("+++++++++++++Datasource Configured");
// return basicDataSource;
// }
#Bean
public DriverManagerDataSource dataSource() {
DriverManagerDataSource basicDatasource = new DriverManagerDataSource();
basicDatasource.setDriverClassName("com.mysql.jdbc.Driver");
basicDatasource.setUrl("jdbc:mysql://localhost/ce");
basicDatasource.setUsername("root");
basicDatasource.setPassword("root");
LOG.debug("+++++++++++++Datasource Configured");
return basicDatasource;
}
// #Bean
// public LocalContainerEntityManagerFactoryBean entityManager() {
// LocalContainerEntityManagerFactoryBean bean = new
// LocalContainerEntityManagerFactoryBean();
// bean.setDataSource(this.dataSource());
// bean.setLoadTimeWeaver(this.instrumentationLoadTimeWeaver());
// bean.setJpaProperties(this.jpaProperties());
// bean.setJpaVendorAdapter(this.vendorAdapter());
// bean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
// bean.setPackagesToScan("com.caveatemptor.core");
// bean.setPersistenceUnitName("jpwh");
// LOG.info("==================[Entity Manage Configured]==================");
// return bean;
// }
#Bean
public HibernateJpaVendorAdapter vendorAdapter() {
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setShowSql(true);
adapter.setGenerateDdl(true);
LOG.info("==================[Hibernate Jpa Vendor Configured]==================");
return adapter;
}
#Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager();
jpaTransactionManager.setDataSource(this.dataSource());
jpaTransactionManager.setJpaProperties(this.jpaProperties());
// jpaTransactionManager.setEntityManagerFactory(this.entityManager().getObject());
return jpaTransactionManager;
}
#Bean
public InstrumentationLoadTimeWeaver instrumentationLoadTimeWeaver() {
return new InstrumentationLoadTimeWeaver();
}
public Properties jpaProperties() {
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "create");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
properties.setProperty("hibernate.show_sql", "true");
properties.setProperty("hibernate.format_sql", "true");
properties.setProperty("hibernate.ejb.naming_strategy", "org.hibernate.cfg.DefaultNamingStrategy");
properties.setProperty("hibernate.order_by.default_null_ordering", "first");
LOG.info("==================[JPA Properties Configured]==================");
return properties;
}
#Bean
public SessionFactory sessionFactory() {
LocalSessionFactoryBuilder bean = new LocalSessionFactoryBuilder(this.dataSource());
bean.setProperties(this.jpaProperties());
bean.addPackage("com.ceveatemptor");
LOG.info("Session Factory Configured+++++++++++++++++++");
return bean.buildSessionFactory();
}
}
This is a multimodule project using maven and therefore it is configured in web module as:
public class DispatcherServletInitConfiguration extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { SpringDataJPAConfiguration.class };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { WebServletInitConfiguration.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
Generic DAO Class:
public abstract class GenericDAOImpl<T, ID extends Serializable> implements GenericDAO<T, ID> {
#PersistenceContext
protected EntityManager em;
protected final Class<T> entityClass;
public GenericDAOImpl(Class<T> entityClass) {
this.entityClass = entityClass;
}
#Override
public T makePersistent(T entity) {
return em.merge(entity);
}
}
Since Most of threads mentioned that I need a #Transactional and here is the implementation
#Repository
#Transactional
#javax.transaction.Transactional
public class ItemDAOImpl extends GenericDAOImpl<Item, Long> implements ItemDAO {
public ItemDAOImpl() {
super(Item.class);
}
}
The Stacktrace:
Sep 02, 2017 11:30:22 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/portal] threw exception [Request processing failed; nested exception is javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'merge' call] with root cause
javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'merge' call
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:282)
at com.sun.proxy.$Proxy77.merge(Unknown Source)
at com.caveatemptor.core.dao.GenericDAOImpl.makePersistent(GenericDAOImpl.java:53)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:333)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy79.makePersistent(Unknown Source)
at com.caveatemptor.web.controllers.ItemController.saveItem(ItemController.java:38)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:624)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
This may be a typo or some major configuration error, I am not getting it.
Any Insight will be helpful.
EDIT 1: If you need any other information, Please let me know.
EDIT 2 I am open to all kind of suggestions even If it is to change entire configuration for hibernate/jpa module. But only Java/annotation based configuration
EDIT 3 I Solved it by migrating server from tomcat to wildfly and configured JNDI data source and now it is working fine.
i'm encountering an error while producing JSON from DAO, here is my code
Controller
#RequestMapping(value="/findAdpId/", method=RequestMethod.POST , consumes = "application/json", produces = "application/json")
public #ResponseBody AllCustomerHist findAdpId(#RequestBody AllCustomerHist customer){
String customerId = customer.getCustomerId();
String srctable = customer.getSrctable();
System.out.println("customer ID = "+customerId);
System.out.println("srctable = "+srctable);
List<AllCustomerHist> adpcust = allCustomerHistService.findAdpId(customerId, srctable);
return (AllCustomerHist) adpcust;
}
DAO Class
#SuppressWarnings("unchecked")
public List<AllCustomerHist> findAdpId(String customerId, String srctable) {
// TODO Auto-generated method stub
Criteria criteria = getSession().createCriteria(AllCustomerHist.class)
.setProjection(Projections.projectionList()
.add(Projections.property("adpId"), "adpId"))
.add(Restrictions.eq("customerId", customerId))
.add(Restrictions.eq("srctable", srctable));
return (List<AllCustomerHist>)criteria.list();
}
what i want is, when retrieving JSON like
{
"customerId":"11",
"srctable":"transaction"
}
my DAO class will produce
{
"adpId":["abcd123","defw123"]
}
this is the error log
Exception
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is java.lang.ClassCastException:
java.util.ArrayList cannot be cast to
com.astra.adp.model.AllCustomerHist
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Root Cause
java.lang.ClassCastException: java.util.ArrayList cannot be cast to
com.astra.adp.model.AllCustomerHist
com.astra.adp.controller.CustomerController.findAdpId(CustomerController.java:73)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
java.lang.reflect.Method.invoke(Unknown Source)
org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
Note The full stack trace of the root cause is available in the server
logs.
thank you for the helps!
I have hibernate filter like this:
#Entity
#FilterDef(name = "filter_name", parameters = { #ParamDef(name = "tenatId", type = "long") })
#Filter(name = "filter_name", condition = "tenant_id = :tenantId")
public class MyEntity {
private long tenantId;
}
And I want to enable this filter every time request come, so I create Request Interceptor like this:
#Configuration
public class RequestInterceptor implements HandlerInterceptorAdapter {
#PersistenceContext
private EntityManager entityManager;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
Filter filter = entityManager.unwrap(Session.class).enableFilter("filter_name");
filter.setParameter("tenantId", myService.getTentantId());
}
}
But I cannot the transaction manager, got error:
java.lang.IllegalStateException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:272)
at com.sun.proxy.$Proxy184.unwrap(Unknown Source)
at id.gdplabs.koi.api.config.security.RequestInterceptor.preHandle(RequestInterceptor.java:27)
at org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter.preHandle(WebRequestHandlerInterceptorAdapter.java:56)
at org.springframework.web.servlet.HandlerExecutionChain.applyPreHandle(HandlerExecutionChain.java:134)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:958)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
...
It works if I move the code to controller:
#RequestMapping(method = RequestMethod.GET, value = "/test")
public List<MyEntity> test() {
Filter filter = entityManager.unwrap(Session.class).enableFilter("filter_name");
filter.setParameter("tenantId", myService.getTentantId());
return myService.findAll();
}
Do you have idea why I cannot unwrap hibernate session in the Interceptor?
Any suggestion are welcome, Thanks!
I have an error while I am testing my rest controller on a specific method. I am using the #Query annotation to do my database query. And it's using the "principal.username" to do it. I don't have the all picture on how principal.username is fetched and used in my application. I am currently looking at the spring-security documentation about it. But my problem is in the test part, when I execute the test below, I have an error "Faillure" because of the #Query.
The repository:
public interface MeetingRepository extends JpaRepository<Meeting,Long> {
#Query("select m from Meeting m where m.visibility = 'PUBLIC' OR m.user.login = ?#{principal.username}")
List<Meeting> findOpenAndUserMeetings();
}
A Rest Controller Method:
#RequestMapping(value = "/api/meetings", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Meeting>> getAll()
{
List<Meeting> meetings = MeetingRepository.findOpenAndUserMeetings();
return new ResponseEntity<List<Meeting>>(meetings, HttpStatus.OK);
}
A test:
#Test
#Transactional
public void getAllMeetings() throws Exception {
// Initialize the database
MeetingRepository.saveAndFlush(Meeting);
// Get all the Meetinges
restMeetingMockMvc.perform(get("/api/meetings"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
And this error:
getAllMeetings(com.ent.web.rest.MeetingResourceTest) Time elapsed: 0.07 sec <<< ERROR!
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 10): Property or field 'username' cannot be found on object of type 'java.lang.String' - maybe not public?
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:226)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:93)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:46)
at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:372)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:88)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:131)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:299)
at org.springframework.data.jpa.repository.query.SpelExpressionStringQueryParameterBinder.evaluateExpression(SpelExpressionStringQueryParameterBinder.java:131)
at org.springframework.data.jpa.repository.query.SpelExpressionStringQueryParameterBinder.potentiallyBindExpressionParameters(SpelExpressionStringQueryParameterBinder.java:89)
at org.springframework.data.jpa.repository.query.SpelExpressionStringQueryParameterBinder.bind(SpelExpressionStringQueryParameterBinder.java:69)
at org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery.doCreateCountQuery(AbstractStringBasedJpaQuery.java:109)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.createCountQuery(AbstractJpaQuery.java:190)
at org.springframework.data.jpa.repository.query.JpaQueryExecution$PagedExecution.doExecute(JpaQueryExecution.java:173)
at org.springframework.data.jpa.repository.query.JpaQueryExecution.execute(JpaQueryExecution.java:74)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.doExecute(AbstractJpaQuery.java:97)
at org.springframework.data.jpa.repository.query.AbstractJpaQuery.execute(AbstractJpaQuery.java:88)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:395)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:373)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodIntercceptor.invoke(CrudMethodMetadataPostProcessor.java:122)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at com.sun.proxy.$Proxy148.findOpenAndUserMeetings(Unknown Source)
at com.ent.web.rest.MeetingResource.getAll(MeetingResource.java:77)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:776)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:705)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:144)
at com.ent.web.rest.MeetingResourceTest.getAllMeetings(MeetingResourceTest.java:151)
Edit
How to be able to use this "?#{principal.username}" in the test? After investigating, I found that here: Spring Security 4.0: WebSocket, Spring Data and Test Support
Spring Data Integration
It is now possible to access the current user within Spring Data queries using SpEL. To enable this feature with Java Configuration, you can define a #Bean.
#Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension(){
return new SecurityEvaluationContextExtension();
}
Then you can refer to Spring Security's principal in your queries. For example:
public interface BlogRepository extends JpaRepository<Blog,Long> {
#Query("select blog from Blog blog where blog.user.login = ?#{principal.username}")
List<Blog> findAllForCurrentUser();
}
Is it a context problem?
In my project, I have a repository that uses #{principal.username} in one of its queries. Here's what it looks like:
public interface BlogRepository extends JpaRepository<Blog, Long> {
#Query("select blog from Blog blog where blog.user.login = ?#{principal.username}")
List<Blog> findAllForCurrentUser();
}
My BlogResource controller calls this as follows:
#Timed
public List<Blog> getAll() {
log.debug("REST request to get all Blogs for current user");
return blogRepository.findAllForCurrentUser();
}
To test this, I upgraded to Spring Security 4.0.1 and added a dependency on spring-security-test:
<spring-security.version>4.0.1.RELEASE</spring-security.version>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<version>${spring-security.version}</version>
<scope>test</scope>
</dependency>
In BlogResourceTest, I added dependencies on WebApplicationContext and UserRepository:
#Autowired
WebApplicationContext context;
#Inject
UserRepository userRepository;
Then I modified the getAllBlogs() test to use Spring Security's "with(user(username))" functionality.
#Test
#Transactional
public void getAllBlogs() throws Exception {
restBlogMockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).build();
// Initialize the database
blog.setUser(userRepository.findOneByLogin("user").get());
blogRepository.saveAndFlush(blog);
// Get all the blogs
restBlogMockMvc.perform(get("/api/blogs").with(user("user")))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$.[*].id").value(hasItem(blog.getId().intValue())))
.andExpect(jsonPath("$.[*].name").value(hasItem(DEFAULT_NAME.toString())))
.andExpect(jsonPath("$.[*].handle").value(hasItem(DEFAULT_HANDLE.toString())));
}
I don't know why Spring Security's test annotations (#WithMockUser and #WithUserDetails) don't work. I asked a question about this a week ago:
Spring MVC Test with RequestPostProcessor vs. Annotations