I'm trying to integrate Spring 3.1 with Hibernate 4.1.4, but I am having problems when getting my session from a generic DAO.
The problem is, if I use getCurrentSession() I get a NullPointerException and Hibernate says No session is bound to the context, yet if I use openSession everything seems to be working. Somehow I feel I should be using getCurrentSession, but can't find the origin of the problem.
I've searched this over the internet but none of the solutions worked for me.
Here's my genericDAO code:
public class GenericDaoHibernateImpl<E, PK extends Serializable> implements GenericDao<E, PK> {
private Class<E> entityClass;
protected GenericDataBaseExceptionHandler exceptionHandler;
private SessionFactory sessionFactory;
#SuppressWarnings("unchecked")
public GenericDaoHibernateImpl() {
this.entityClass = (Class<E>) ((ParameterizedType) getClass().
getGenericSuperclass()).getActualTypeArguments()[0];
}
public void setExceptionHandler(GenericDataBaseExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
protected Session getSession() {
if (sessionFactory.getCurrentSession() == null)
throw new IllegalStateException("Session has not been set on DAO before usage");
return sessionFactory.getCurrentSession(); //This crashes
// return sessionFactory.openSession(); //This does not
}
public Class<E> getEntityClass() {
return entityClass;
}
public void persist(E entity) throws GenericDataBaseException {
try {
getSession().persist(entity);
} catch (Throwable t) {
Collection<Object> args = new ArrayList<Object>();
args.add(entity);
throw exceptionHandler.handle(t, "persist", args);
}
}
(...)
}
And my spring config file:
<context:annotation-config />
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="jdbcUrl">
<value>jdbc:mysql://localhost/forestool</value>
</property>
<property name="user">
<value>forestool</value>
</property>
<property name="password">
<value>forestool</value>
</property>
</bean>
<!-- Declaración de la factorÃa de sesiones hibernate con los mappings necesarios -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="mappingLocations">
<list>
<value>classpath*:/hbm/*.hbm.xml</value>
</list>
</property>
<property name="hibernateProperties">
<ref bean="hibernateProperties"/>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<bean id="hibernateProperties" class="java.util.Properties">
<constructor-arg index="0">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
</props>
</constructor-arg>
</bean>
Exception:
org.hibernate.HibernateException: No Session found for current thread
at org.springframework.orm.hibernate4.SpringSessionContext.currentSession(SpringSessionContext.java:97)
at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:1041)
at es.fsc.core.dao.impl.GenericDaoHibernateImpl.getSession(GenericDaoHibernateImpl.java:83)
at es.fsc.core.dao.impl.GenericDaoHibernateImpl.findAll(GenericDaoHibernateImpl.java:244)
at es.fsc.dao.explotacion.impl.ExplotacionDaoImpl.getExplotacionesAbiertas(ExplotacionDaoImpl.java:21)
at es.fsc.service.explotacion.impl.ExplotacionServiceImpl.getExplotacionesAbiertas(ExplotacionServiceImpl.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)
at $Proxy5.getExplotacionesAbiertas(Unknown Source)
at es.fsc.app.FscApp.runApp(FscApp.java:58)
at es.fsc.app.App.run(App.java:65)
at es.fsc.main.Main.main(Main.java:20)
Exception in thread "main" java.lang.NullPointerException
at es.fsc.core.dao.impl.GenericDaoHibernateImpl.findAll(GenericDaoHibernateImpl.java:253)
at es.fsc.dao.explotacion.impl.ExplotacionDaoImpl.getExplotacionesAbiertas(ExplotacionDaoImpl.java:21)
at es.fsc.service.explotacion.impl.ExplotacionServiceImpl.getExplotacionesAbiertas(ExplotacionServiceImpl.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)
at $Proxy5.getExplotacionesAbiertas(Unknown Source)
at es.fsc.app.FscApp.runApp(FscApp.java:58)
at es.fsc.app.App.run(App.java:65)
at es.fsc.main.Main.main(Main.java:20)
I don't see anything in this configuration that would actually open a Hibernate session for you, or control when that is done.
If you are new to Spring, you might just want to configure an OpenSessionInViewFilter in your web.xml to have a Session open and bound to each request automatically.
Related
I repurposed an existing hibernate-spring project and upgraded to Hibernate 4 and Spring 4 and added multiple datasources using multitenancy. The application starts fine, the datasources are being read in using the MultiTenantDataSourceLookup class. When setting the new tenant, the tenant is resolved but then I get the nullpointerexception on line 41 of MultiTenantConnectionProviderImpl(See comment for line). I'm also using GenericHibernateDAO if that helps. I can post that code by request. I'm new to spring so the problem may be a very simple one. However, if more code is needed to help me, I will be happy to share more as I troubleshoot and research the problem myself. Any help will be greatly appreciated. Thanks. Here is the full stack trace: http://pastebin.com/LjyhTwvY
MultiTenantConnectionProviderImpl.java
public class MultiTenantConnectionProviderImpl extends AbstractDataSourceBasedMultiTenantConnectionProviderImpl
{
#Autowired
private DataSource defaultDataSource;
#Autowired
private DataSourceLookup dataSourceLookup;
/**
* Select datasources in situations where not tenantId is used (e.g. startup processing).
*/
#Override
protected DataSource selectAnyDataSource() {
return defaultDataSource;
}
/**
* Obtains a DataSource based on tenantId
*/
#Override
protected DataSource selectDataSource(String tenantIdentifier) {
//Below is line 41 where the nullpointerexeption is occurring
DataSource ds = dataSourceLookup.getDataSource(tenantIdentifier);
return ds;
}
}
CurrentTenantIdentifierResolverImpl.java
public class CurrentTenantIdentifierResolverImpl implements CurrentTenantIdentifierResolver {
private static final String KEY_TENANTID_SESSION = "hibernate.tenant_identifier_resolver";
private static final String DEFAULT_TENANTID = "customer1";
public String resolveCurrentTenantIdentifier() {
String tenant = resolveTenantByHttpSession();
System.out.println("Tenant resolved: " + tenant);
return tenant;
}
/**
* Get tenantId in the session attribute KEY_TENANTID_SESSION
* #return TenantId on KEY_TENANTID_SESSION
*/
public String resolveTenantByHttpSession()
{
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//If session attribute exists returns tenantId saved on the session
if(attr != null){
HttpSession session = attr.getRequest().getSession(false); // true == allow create
if(session != null){
String tenant = (String) session.getAttribute(KEY_TENANTID_SESSION);
if(tenant != null){
return tenant;
}
}
}
//otherwise return default tenant
return DEFAULT_TENANTID;
}
public boolean validateExistingCurrentSessions() {
return true;
}
}
Context.xml
<context:annotation-config />
<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />
<bean id="multitenancyConnectionProvider"
class="com.github.elizabetht.util.MultiTenantConnectionProviderImpl"/>
<bean id="dataSourceLookup"
class="com.github.elizabetht.util.MultiTenantDataSourceLookup"/>
<bean id="tenantResolver"
class="com.github.elizabetht.util.CurrentTenantIdentifierResolverImpl"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="packagesToScan">
<list>
<value>com.github.elizabetht.model</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
<prop key="hibernate.jdbc.lob.non_contextual_creation">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.multiTenancy">DATABASE</prop>
<prop key="hibernate.multi_tenant_connection_provider">com.github.elizabetht.util.MultiTenantConnectionProviderImpl</prop>
<prop key="hibernate.tenant_identifier_resolver">com.github.elizabetht.util.CurrentTenantIdentifierResolverImpl</prop>
</props>
</property>
</bean>
<bean id="defaultDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/studentEnrollment" />
<property name="username" value="springy" />
<property name="password" value="pass" />
</bean>
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
<property name="autodetectDataSource" value="false"/>
</bean>
OUTPUT
Tenant resolved: customer1
Feb 25, 2017 1:34:31 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [studentHibernateServlet] in context with path [/StudentEnrollmentWithSpring] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause
java.lang.NullPointerException
at com.github.elizabetht.util.MultiTenantConnectionProviderImpl.selectDataSource(MultiTenantConnectionProviderImpl.java:41)
at org.hibernate.engine.jdbc.connections.spi.AbstractDataSourceBasedMultiTenantConnectionProviderImpl.getConnection(AbstractDataSourceBasedMultiTenantConnectionProviderImpl.java:52)
at org.hibernate.internal.AbstractSessionImpl$ContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:423)
at org.hibernate.engine.jdbc.internal.LogicalConnectionImpl.obtainConnection(LogicalConnectionImpl.java:228)
I changed my hibernate properties to the following and everything works now:
<property name="hibernateProperties">
<map>
<entry key="hibernate.multi_tenant_connection_provider" value-ref="multitenancyConnectionProvider"/>
<entry key="hibernate.tenant_identifier_resolver" value-ref="tenantResolver"/>
<entry key="hibernate.multiTenancy" value="DATABASE"/>
</map>
</property>
I have this in my servlet:
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/pages/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<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="12345" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.tricas.models" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.enable_lazy_load_no_trans">true</prop>
<prop key="hibernate.default_schema">test</prop>
<prop key="format_sql">true</prop>
<prop key="use_sql_comments">true</prop>
<!--<prop key="hibernate.hbm2ddl.auto">create</prop> -->
</props>
</property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
and my DaoImp
#Repository
#Transactional
public class UserDaoImp implements UserDao {
#Autowired
SessionFactory session;
public List<Users> list() {
return session.getCurrentSession().createQuery("from Users").list();
}
here is my HibernateUtil
private static final SessionFactory sessionFactory;
static {
try {
// Create the SessionFactory from standard (hibernate.cfg.xml)
// config file.
sessionFactory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
// Log the exception.
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
And After executing the application I have a NullPointerException:
SEVERE [http-nio-8084-exec-97] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service () for servlet [spring-web] in context with path [/ Holaspringmvc] threw exception [Request processing failed; Nested exception is java.lang.NullPointerException] with root cause
Java.lang.NullPointerException
At com.tricas.dao.UserDaoImp.list (UserDaoImp.java:32)
Please help me.
Be simpler. Just declare SessionFactory bean
#Bean
public AbstractSessionFactoryBean sessionFactoryBean(){
AnnotationSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean();
sessionFactoryBean.setConfigLocation(new ClassPathResource("hibernate.cfg.xml"));
return sessionFactoryBean;
}
similar for LocalSessionFactoryBean
btw: did you define component-scan ?
<context:component-scan base-package="<my.base.package>" />
I found the error, results that I had to define in the service and in the controller also with #Autowired
Here is my Service.
#Autowired
UserDao usrdao;
//private UserDao usrdao = new UserDaoImp();
#Transactional
public List<Users> getAllUsers() {
return usrdao.list();
}
and here is my controller
#Autowired
UserService usrv;
//private UserService usrv = new UserService();
#RequestMapping(value = "/verusuarios", method = RequestMethod.GET)
public String listPersons(Model model) {
List<Users> list = usrv.getAllUsers();
model.addAttribute("user", new Users());
model.addAttribute("list", list);
return "verusuarios";
}
Additionally I must add to guide me from this answer: answer here
it's my first time I'm using hibernate so I've got a little question.
Is this way of using hibernate 5.1 with hikariCP 2.4.3 correct?
I'm not sure if the pooling is working like this.
should I call
Session session = sessionFactory.openSession();
or
Session session = sessionFactory.getCurrentSession();
to get a session?
when i call
session.close();
is the connection closed or given back to the pool.
the HibernateDataStore is used by threads for accessing the db
thanks for help
The Config
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.provider_class">
org.hibernate.hikaricp.internal.HikariCPConnectionProvider
</property>
<property name="hibernate.hikari.dataSourceClassName">
com.mysql.jdbc.jdbc2.optional.MysqlDataSource
</property>
<property name="hibernate.hikari.dataSource.url">
jdbc:mysql://localhost/xyz
</property>
<property name="hibernate.hikari.dataSource.user">user</property>
<property name="hibernate.hikari.dataSource.password">xyz</property>
<property name="hibernate.hikari.minimumIdle">20</property>
<property name="hibernate.hikari.maximumPoolSize">100</property>
<property name="hibernate.current_session_context_class">
org.hibernate.context.internal.ThreadLocalSessionContext
</property>
<!-- SQL dialect -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
<!-- Names the annotated entity class -->
<mapping class="XYZ"/>
</session-factory>
</hibernate-configuration>
My DataStore-Class
public class HibernateDataStore implements DataStore {
private static final Logger logger = LogManager.getLogger();
/** factory for hibernate sessions */
private static SessionFactory sessionFactory;
static {
setUpHibernate();
}
/**
* set up the hibernate session-factory
*/
private static void setUpHibernate() {
logger.debug("building session factory");
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure( "config/server/hibernate.cfg.xml" )
.build();
try {
sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
}
catch (Exception e) {
logger.error("problems building hibernate sessionFactory " + e.getMessage());
// The registry would be destroyed by the SessionFactory, but we had trouble building the SessionFactory
// so destroy it manually.
StandardServiceRegistryBuilder.destroy( registry );
}
logger.debug("finished building session factory");
}
#Override
public void storeProduct(Product product) {
logger.info("storing product: " + product.getTitle());
Instant start = Instant.now();
final Session session = sessionFactory.openSession();
//final Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
session.save( product );
session.getTransaction().commit();
session.close();
Instant stop = Instant.now();
logger.debug("product saved (took " + Duration.between(start, stop).toMillis() + "ms)");
}
}
I think you should use Spring to handle session factory
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"
destroy-method="close">
<constructor-arg ref="hikariConfig" />
</bean>
<bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="poolName" value="springHikariCP" />
<property name="connectionTestQuery" value="SELECT 1" />
<property name="dataSourceClassName"
value="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" />
<property name="dataSourceProperties">
<props>
<prop key="url">jdbc:mysql://localhost:3306/xyz</prop>
<prop key="user">root</prop>
<prop key="password"></prop>
</props>
</property>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="packagesToScan" value="com.xyz.model" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<!-- <prop key="hibernate.hbm2ddl.auto">update</prop> -->
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.hikari.dataSource.minimumIdle">5</prop>
<prop key="hibernate.hikari.dataSource.maximumPoolSize">20</prop>
<prop key="hibernate.hikari.dataSource.idleTimeout">30000</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory">
</bean>
I am using spring 3.2 with hibernate 4. I want to use spring to control the transactions.
However with the configuration mentioned below I get the
'Servlet.service() for servlet spring threw exception: org.hibernate.HibernateException: No Session found for current thread'
exception:
<aop:config>
<aop:pointcut id="serviceMethods"
expression="execution(* com.locator.service.impl.ServiceTypeService.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
</aop:config>
<tx:advice id="txAdvice" transaction-manager="hbTransactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<!-- Hibernate session factory -->
<bean id="hbSessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
<property name="mappingResources">
<list>
<value>../spring/model/ServiceType.hbm.xml</value>
</list>
</property>
</bean>
<bean id="hbTransactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="hbSessionFactory" />
</bean>
<bean id="serviceTypeService" class="com.locator.service.impl.ServiceTypeService">
<property name="serviceTypeDao" ref="serviceTypeDao"></property>
</bean>
<bean id="serviceTypeDao" class="com.locator.dao.impl.ServiceTypeDao">
<property name="sessionFactory" ref="hbSessionFactory"></property>
</bean>
The code for the Dao layer and the Service is as follows:
public class ServiceTypeDao implements IServiceTypeDao{
private static final Log log = LogFactory.getLog(ServiceTypeDao.class);
private SessionFactory sessionFactory;
public SessionFactory getSessionFactory(){
return sessionFactory;
}
public void setSessionFactory(SessionFactory sessionFactory){
this.sessionFactory = sessionFactory;
}
public ServiceType findById(int id) {
log.debug("getting ServiceType instance with id: " + id);
try {
Session session = getSessionFactory().getCurrentSession();
ServiceType instance = (ServiceType) session.get("com.locator.model.ServiceType", id);
if (instance == null) {
log.debug("get successful, no instance found");
} else {
log.debug("get successful, instance found");
}
instance.setName(instance.getName()+"0");
session.saveOrUpdate(instance);
return instance;
}catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
}
public class ServiceTypeService implements IServiceTypeService{
private ServiceTypeDao serviceTypeDao;
public void setServiceTypeDao(ServiceTypeDao serviceTypeDao){
this.serviceTypeDao = serviceTypeDao;
}
public ServiceType getServiceTypeById(int id){
return serviceTypeDao.findById(id);
}
}
Replacing getSessionFactory().getCurrentSession() with getSessionFactory().openSession() will resolve the above issue however, it will mean that the developer will then be responsible for the session open/close rather than spring. Therefore, please advise how this can be resolved using spring.
I was able to resolve the issue. It was occurring due to the following problems:
The Service class had not been Auto wired into the controller i.e. the #Autowired annotation was missing.
The configuration for the web.xml had to be modified with the listener class 'org.springframework.web.context.ContextLoaderListener' and the context-param was added.
I have this bit of code in our service layer:
#Override
public <T extends BaseEntity> T find(Long id, Class<? extends BaseEntity> clazz) throws Exception {
BaseEntity e = em.find(clazz, id);
if (e != null) {
deserialize(e, e.getClass());
} else
LOG.error("Found null for id " + id);
return (T) e;
}
public void delete(BaseEntity e, String index, String type) throws Exception {
if (e == null)
return;
if (e.getId() == null)
return;
Delete delete = new Delete.Builder(e.getId().toString()).index(index).type(type).build();
getJestClient().execute(delete);
if (em.contains(e)) {
em.remove(e);
} else {
BaseEntity ee = find(e.getId(), e.getClass());
em.remove(ee);
}
}
protected void deserialize(BaseEntity dst, Class<?> dstClass) throws Exception {
Object src = serializer.deserialize(new String(dst.getContent(), "UTF-8"), dstClass);
for (Field f : getClassFields(src.getClass())) {
if (Modifier.isStatic(f.getModifiers())) continue;
if (!f.isAnnotationPresent(Expose.class)) continue;
f.setAccessible(true);
f.set(dst, f.get(src));
LOG.trace("deserializing " + f.getName() + " : " + f.get(src));
}
}
However, if I use it to remove the entity, it will fail with:
Caused by: java.lang.IllegalArgumentException: Removing a detached instance FooEntity
at org.hibernate.ejb.event.EJB3DeleteEventListener.performDetachedEntityDeletionCheck(EJB3DeleteEventListener.java:65)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:107)
at org.hibernate.event.def.DefaultDeleteEventListener.onDelete(DefaultDeleteEventListener.java:73)
at org.hibernate.impl.SessionImpl.fireDelete(SessionImpl.java:956)
at org.hibernate.impl.SessionImpl.delete(SessionImpl.java:934)
at org.hibernate.ejb.AbstractEntityManagerImpl.remove(AbstractEntityManagerImpl.java:867)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
at $Proxy42.remove(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
at $Proxy31.remove(Unknown Source)
... 16 more
But that makes no sense to me, since line before em.remove(ee), entity is loaded via find method, thus it can't be detached... Transactions are implemented via spring xml configuration
// Edit: added deserialize method. Basically, it will take json content that is stored with every object and create copy of the object, from which then fields are assigned to.
getJestClient().execute is handler for elasticsearch, which is unrelated to the hibernate and jpa.
Em is created via spring:
<bean class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close" id="dsFoo">
<property name="driverClassName" value="${foo.census.driverClassName}"/>
<property name="url" value="${foo.census.url}"/>
<property name="username" value="${foo.census.user}"/>
<property name="password" value="${foo.census.password}"/>
<property name="testOnBorrow" value="true"/>
<property name="testOnReturn" value="true"/>
<property name="testWhileIdle" value="true"/>
<property name="timeBetweenEvictionRunsMillis" value="1800000"/>
<property name="numTestsPerEvictionRun" value="3"/>
<property name="minEvictableIdleTimeMillis" value="1800000"/>
<property name="validationQuery" value="SELECT 1"/>
</bean>
<bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" id="emfCensus">
<property name="persistenceUnitName" value="puFoo"/>
<property name="dataSource" ref="dsFoo"/>
</bean>
<bean id="emFoo" class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name = "entityManagerFactory" ref="emfFoo"/>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emfFoo" />
<property name="persistenceUnitName" value="puFoo" />
</bean>
<bean id="repoFoo" class="service.FooService">
<property name="entityManager" ref="emFoo" />
</bean>
<aop:aspectj-autoproxy proxy-target-class="true"/>
<aop:config>
<aop:pointcut id="repoFooOperation" expression="execution(* service.FooService.*(..))"/>
<aop:advisor advice-ref="txAdviceFoo" pointcut-ref="repoFooOperation"/>
</aop:config>
<tx:advice id="txAdviceFoo" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
Okay, thanks to stackoverflow and googling, I managed to find a way to solve it:
if (em.contains(e)) {
em.remove(e);
} else {
BaseEntity ee = em.getReference(e.getClass(), e.getId());
em.remove(ee);
}
is correct way to remove entity that is detached.