Skip to Content
0

Developing a framework library using Spring + JPA on SAP Cloud Platform

Dec 19, 2017 at 06:36 AM

160

avatar image
Former Member

Hi,

I am trying to develop a java library .jar project using spring and JPA framework. This library .jar project will be utilized by different teams for specific development.

This project is connected to SAP HANA default data source to provide database storage.

I have used Spring mvc framework for the API development and JPA + Eclipselink (Container Managed persistence) for Database connectivity.

Below is the web.xml of my project:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
          http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<display-name>cfa-framework</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:META-INF/framework-applicationContext.xml</param-value>
</context-param>
<login-config>
<auth-method>BASICCERT</auth-method>
</login-config>

<security-role>
<description>All SAP HANA Cloud Platform users</description>
<role-name>Everyone</role-name>
</security-role>
<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/key</url-pattern>
<url-pattern>/index.jsp</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>Everyone</role-name>
</auth-constraint>
</security-constraint>


<resource-ref>
<res-ref-name>jdbc/DefaultDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>


<security-role>
<description>All SAP HANA Cloud Platform users</description>
<role-name>Everyone</role-name>
</security-role>


<security-constraint>
<web-resource-collection>
<web-resource-name>Protected Area</web-resource-name>
<url-pattern>/key</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>Everyone</role-name>
</auth-constraint>
</security-constraint>


<servlet>
<servlet-name>cfa-framework</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:META-INF/framework-applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>cfa-framework</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
</web-app>

Below is my application context xml 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:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jee="http://www.springframework.org/schema/jee"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.1.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx-4.1.xsd 
    http://www.springframework.org/schema/jee 
    http://www.springframework.org/schema/jee/spring-jee.xsd">

	<context:component-scan base-package="com.sap.cec.marketing.cfa" />
	<context:annotation-config />

	<jee:jndi-lookup id="dbDataSource" jndi-name="jdbc/DefaultDB"
		expected-type="javax.sql.DataSource" />
	<bean id="emfBean"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<!-- <property name="dataSource" ref="dbDataSource" /> -->
		<property name="persistenceUnitName" value="cfa-framework" />
		<property name="jpaDialect">
			<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect" />
		</property>
		<property name="jpaPropertyMap">
			<props>
				<prop key="eclipselink.weaving">false</prop>
			</props>
		</property>
		<property name="loadTimeWeaver">
			<bean
				class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
		</property>
	</bean>


	<bean id="jpaTxnManagerBean" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="emfBean"></property>
	</bean>
	<tx:annotation-driven transaction-manager="jpaTxnManagerBean"
		proxy-target-class="true" />


	<bean id="scenarioHandlerProto"
		class="com.sap.cec.marketing.cfa.handler.AbstractScenarioHandler"
		abstract="true">
		<property name="requestStorageEnabled" value="false" />
		<property name="requestValidationEnabled" value="false" />
		<property name="requestBackendAccessEnabled" value="false" />
		<property name="customAliasEnabled" value="false" />


	</bean>
</beans>

Below is the persistence.xml entry:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
	<persistence-unit name="cfa-framework" transaction-type="JTA">
		<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
		<jta-data-source>jdbc/DefaultDB</jta-data-source>
		<shared-cache-mode>NONE</shared-cache-mode>
		<properties>
			<property name="eclipselink.ddl-generation" value="create-tables"/>
			<property name="eclipselink.query-results-cache" value="false"/>
		</properties>
	</persistence-unit>
</persistence>


Below is my repository object, which is used as DAO:

@Repository
@Transactional
public class TenantSecretBean {


  @PersistenceContext
  private EntityManager em;


  @SuppressWarnings("unchecked")
  public List<TenantSecret> getAllTenantSecrets() {
    return em.createNamedQuery("AllTenantSecrets")
        .getResultList();
  }

......................

......................

And below is my entity object, mapping to the database table:

@Entity
@Table(name = "T_TENANT_SECRET")
@NamedQueries({@NamedQuery(name = "AllTenantSecrets", query = "select s from TenantSecret s"),
    @NamedQuery(name = "TenantSecretByTenant",
        query = "select s from TenantSecret s where s.tenant = :tenantCode order by s.timestamp desc"),
    @NamedQuery(name = "TenantSecretsOlderThanTimestamp",
        query = "select s from TenantSecret s where s.timestamp < :timestamp")})
public class TenantSecret {
  @Id
  @Column(length = 255)
  private String id;


  @Column(length = 255)
  private String tenant;

And finally here is my Controller snippet from where the DAO is called:

@Controller
@RequestMapping(value = "/key")
@Transactional
public class SecretKeySyncController {

  @Autowired
  protected TenantSecretBean tenantSecretBean;

  @RequestMapping(method = RequestMethod.PUT)
  public ResponseEntity<String> saveSecretKey(HttpServletRequest request,
      @RequestBody String body) {
  
    saveTenantSecret(request, jsonObject); 
    return new ResponseEntity<>("Successfully Stored!!", HttpStatus.OK);
  }

  private void saveTenantSecret(HttpServletRequest request, JSONObject jsonBody)
      throws JSONException {
    TenantSecret tenantSecret = populateTenantSecret(request, jsonBody);
    tenantSecretBean.addTenantSecret(tenantSecret);
  }

I have another project, cfa-main which will be deployed on SCP as a .war and includes this project as a library .jar.

I have deployed the cfa-main project in the Java EE 7 Web Profile TomEE 7 Server container.

I have additionally added the below entry in the web.xml of cfa-main project as well:

<resource-ref> <res-ref-name>jdbc/DefaultDB</res-ref-name> <res-type>javax.sql.DataSource</res-type> </resource-ref>

I am getting the below exception in my local eclipse when I try to save something into the database using the tenantSecretBean.addTenantSecret(tenantSecret) method:

<!doctype html><html lang="en"><head><title>HTTP Status 500 – Internal Server Error</title><style type="text/css">h1 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:22px;} h2 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:16px;} h3 {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;font-size:14px;} body {font-family:Tahoma,Arial,sans-serif;color:black;background-color:white;} b {font-family:Tahoma,Arial,sans-serif;color:white;background-color:#525D76;} p {font-family:Tahoma,Arial,sans-serif;background:white;color:black;font-size:12px;} a {color:black;} a.name {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style></head><body><h1>HTTP Status 500 – Internal Server Error</h1><hr class="line" /><p><b>Type</b> Exception Report</p><p><b>Message</b> Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException:</p><p><b>Description</b> The server encountered an unexpected condition that prevented it from fulfilling the request.</p><p><b>Exception</b></p><pre>org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: 
Exception Description: Cannot use an EntityTransaction while using JTA.
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:986)
	org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:892)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:664)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:855)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
</pre><p><b>Root Cause</b></p><pre>org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: 
Exception Description: Cannot use an EntityTransaction while using JTA.
	org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:450)
	org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:374)
	org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:474)
	org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)
	org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
	org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
	com.sap.cec.marketing.cfa.controller.SecretKeySyncController$$EnhancerBySpringCGLIB$$e80317ec.saveSecretKey(<generated>)
	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:209)
	org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
	org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:871)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:777)
	org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
	org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:892)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:664)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:855)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
</pre><p><b>Root Cause</b></p><pre>java.lang.IllegalStateException: 
Exception Description: Cannot use an EntityTransaction while using JTA.
	org.eclipse.persistence.internal.jpa.transaction.JTATransactionWrapper.getTransaction(JTATransactionWrapper.java:73)
	org.eclipse.persistence.internal.jpa.EntityManagerImpl.getTransaction(EntityManagerImpl.java:1311)
	org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect.beginTransaction(EclipseLinkJpaDialect.java:86)
	org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:400)
	org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:374)
	org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:474)
	org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:289)
	org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
	org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
	org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
	com.sap.cec.marketing.cfa.controller.SecretKeySyncController$$EnhancerBySpringCGLIB$$e80317ec.saveSecretKey(<generated>)
	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:209)
	org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:136)
	org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:102)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:871)
	org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:777)
	org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
	org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
	org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
	org.springframework.web.servlet.FrameworkServlet.doPut(FrameworkServlet.java:892)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:664)
	org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:855)
	javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
	org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
</pre><p><b>Note</b> The full stack trace of the root cause is available in the server logs.</p><hr class="line" /></body></html>

I tried removing/changing the place of @Transactional annotation, added @Scope annotation, and many more things, but to no effect.

But if I deploy a standalone spring + JPA project with the same configuration as above without any framework, the same works and I am able to successfully store data in the database, both in local and on SCP.

Can you please help me with the same?

Regards,

Suhas R

10 |10000 characters needed characters left characters exceeded
* Please Login or Register to Answer, Follow or Comment.

1 Answer

Best Answer
Richard Zhao
Dec 21, 2017 at 06:38 AM
1

Hello, Suhas. You'd better share the code snippet for us to see how you manage transaction in this function.

 tenantSecretBean.addTenantSecret(tenantSecret);

This error seems that you've already use JTA transaction handled by the container by still somewhere you are using another configuration to manage transaction.

Show 1 Share
10 |10000 characters needed characters left characters exceeded
Former Member

It worked for me now!!

I made changes in the application-context xml file

I changed the content from :

<bean id="jpaTxnManagerBean" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="emfBean"></property>
</bean>
<tx:annotation-driven transaction-manager="jpaTxnManagerBean" proxy-target-class="true" />

to

<bean id="jtaTxnManagerBean" class="org.springframework.transaction.jta.JtaTransactionManager"/>
<tx:annotation-driven transaction-manager="jtaTxnManagerBean" proxy-target-class="true" />
0