I have a java project using spring and hibernate. I am integrating hibernate search to have a full text search. Since i've integrated hibernate search im a not able to save my entity. I am getting the following error:
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.NoSuchMethodError: org.hibernate.engine.transaction.spi.TransactionEnvironment.getJtaPlatform()Lorg/hibernate/engine/transaction/jta/platform/spi/JtaPlatform;
The complete stacktrace is here http://pastebin.com/GTthsRv6
Here my spring controller when i want to save a project for a user:
#RequestMapping(value="/saveproject", method = RequestMethod.POST)
public ModelAndView saveProject(#ModelAttribute Project project,HttpSession session){
User user = (User) session.getAttribute(USER);
user.getProjects().add(project);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("projectSuccess");
modelAndView.addObject("project", project);
userServices.updateUser(user);
return modelAndView;
}
Here the updateUser method implementation in services part:
#Transactional(propagation= Propagation.REQUIRED, readOnly=false)
#Service
public class UserServicesImpl implements UserServices {
#Autowired
private UserDao userDao;
public void updateUser(User user) {
userDao.updateUser(user);
}
..other methods
}
Here is the update user method of #Repository:
#Repository
public class UserDaoImpl extends AbstractDaoImpl<User, Long> implements UserDao {
protected UserDaoImpl() {
super(User.class);
}
#Override
public void updateUser(User user) {
saveOrUpdate(user);
}
...other methods
}
AbstractDaoImpl:
public abstract class AbstractDaoImpl<E, I extends Serializable> implements AbstractDao<E,I> {
private Class<E> entityClass;
protected AbstractDaoImpl(Class<E> entityClass) {
this.entityClass = entityClass;
}
#Autowired
private SessionFactory sessionFactory;
public Session getCurrentSession() {
return sessionFactory.getCurrentSession();
}
#Override
public void saveOrUpdate(E e) {
getCurrentSession().saveOrUpdate(e);
}
... others methods
public void indexDatabase(){
Session session = getCurrentSession();
FullTextSession fullTextSession = Search.getFullTextSession(session);
try {
fullTextSession.createIndexer().startAndWait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
In my hibernate.cfg.xml i configure index :
filesystem
E:\workspace\indexes
I use hibernate 4.2 and hibernate search 4.5. I don't understand where is the problem.
Wrong version of Hibernate ORM is used with Hibernate Search 4.5. Method getJtaPlatform exists, but return type differs.
In Hibernate 4.2 method TransactionEnvironment.getJtaPlatform() returns
org.hibernate.service.jta.platform.spi.JtaPlatform
In Hibernate 4.3 it returns:
org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform
Hibernate Search 4.5 needs Hibernate 4.3. This is documented for example here:
You will need hibernate-core-4.3.1.Final.jar and its transitive dependencies
I had this problem too. I used hibernate-search 4.5 Final in a maven project.
Maven imported the correct dependencies like hibernate-code 4.3 and so on but this problem still resists.
After ours of debugging and even going into the hibernate-core JAR, I gave up and tried hibernate-search-orm 4.0 Final and hibernate-core 4.0 Final. The Exception is gone and he found the missing methods.
Thats a very strange behaviour...
I know that's this isn't a clear solution but it works if you are able to live with the Version 4.0
Related
I am new to Spring Transaction. Something that I found really odd, probably I did understand this properly.
I wanted to have a transactional around method level and I have a caller method within the same class and it seems like it does not like that, it has to be called from the separate class. I don't understand how is that possible.
If anyone has an idea how to resolve this issue, I would greatly appreciate. I would like to use the same class to call the annotated transactional method.
Here is the code:
public class UserService {
#Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
It's a limitation of Spring AOP (dynamic objects and cglib).
If you configure Spring to use AspectJ to handle the transactions, your code will work.
The simple and probably best alternative is to refactor your code. For example one class that handles users and one that process each user. Then default transaction handling with Spring AOP will work.
Configuration tips for handling transactions with AspectJ
To enable Spring to use AspectJ for transactions, you must set the mode to AspectJ:
<tx:annotation-driven mode="aspectj"/>
If you're using Spring with an older version than 3.0, you must also add this to your Spring configuration:
<bean class="org.springframework.transaction.aspectj
.AnnotationTransactionAspect" factory-method="aspectOf">
<property name="transactionManager" ref="transactionManager" />
</bean>
In Java 8+ there's another possibility, which I prefer for the reasons given below:
#Service
public class UserService {
#Autowired
private TransactionHandler transactionHandler;
public boolean addUsers(List<User> users) {
for (User user : users) {
transactionHandler.runInTransaction(() -> addUser(user.getUsername, user.getPassword));
}
}
private boolean addUser(String username, String password) {
// TODO call userRepository
}
}
#Service
public class TransactionHandler {
#Transactional(propagation = Propagation.REQUIRED)
public <T> T runInTransaction(Supplier<T> supplier) {
return supplier.get();
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public <T> T runInNewTransaction(Supplier<T> supplier) {
return supplier.get();
}
}
This approach has the following advantages:
It may be applied to private methods. So you don't have to break encapsulation by making a method public just to satisfy Spring limitations.
Same method may be called within different transaction propagations and it is up to the caller to choose the suitable one. Compare these 2 lines:
transactionHandler.runInTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
transactionHandler.runInNewTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
It is explicit, thus more readable.
The problem here is, that Spring's AOP proxies don't extend but rather wrap your service instance to intercept calls. This has the effect, that any call to "this" from within your service instance is directly invoked on that instance and cannot be intercepted by the wrapping proxy (the proxy is not even aware of any such call). One solutions is already mentioned. Another nifty one would be to simply have Spring inject an instance of the service into the service itself, and call your method on the injected instance, which will be the proxy that handles your transactions. But be aware, that this may have bad side effects too, if your service bean is not a singleton:
<bean id="userService" class="your.package.UserService">
<property name="self" ref="userService" />
...
</bean>
public class UserService {
private UserService self;
public void setSelf(UserService self) {
this.self = self;
}
#Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
self.addUser(user.getUserName, user.getPassword);
}
}
}
With Spring 4 it's possible to Self autowired
#Service
#Transactional
public class UserServiceImpl implements UserService{
#Autowired
private UserRepository repository;
#Autowired
private UserService userService;
#Override
public void update(int id){
repository.findOne(id).setName("ddd");
}
#Override
public void save(Users user) {
repository.save(user);
userService.update(1);
}
}
This is my solution for self invocation:
public class SBMWSBL {
private SBMWSBL self;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void postContruct(){
self = applicationContext.getBean(SBMWSBL.class);
}
// ...
}
You can autowired BeanFactory inside the same class and do a
getBean(YourClazz.class)
It will automatically proxify your class and take into account your #Transactional or other aop annotation.
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advised, as it may look strange to colleagues. But it works with singletons, is easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution as described in Espens answer.
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {
private final PersonDao _personDao;
#Autowired
public PersonDao(PersonDao personDao) {
_personDao = personDao;
}
#Transactional
public void addUser(String username, String password) {
// call database layer
}
public void addUsers(List<User> users) {
for (User user : users) {
_personDao.addUser(user.getUserName, user.getPassword);
}
}
}
The issue is related to how spring load classes and proxies. It will not work , untill you write your inner method / transaction in another class or go to other class and then again come to your class and then write the inner nested transcation method.
To summarize, spring proxies does not allow the scenarios which you are facing. you have to write the 2nd transaction method in other class
There is no point to use AspectJ or Other ways. Just using AOP is sufficient. So, we can add #Transactional to addUsers(List<User> users) to solve current issue.
public class UserService {
private boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
#Transactional
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
I am using Drools with Spring Boot 2.3 and I have implemented the persistent aware KieSession, in which MySQL is used for storing the session. I have successfully integrated the default EntityManagerFactory of Spring Boot with Drools but my problem is with transactions. By default, Drools uses Optimistic Lock during transactions but it allows us to use the Pessimistic Lock as well, which is what I want. Now while firing rules, Drools persists/updates the KieSession in MySQL with the following query:
update SessionInfo set lastModificationDate=?, rulesByteArray=?, startDate=?, OPTLOCK=? where id=? and OPTLOCK=?
Now the above statement is executed twice if I do not use transactions using the #Transactional annotation in the method, and if #Transactional is used then the above statement is executed only once after firing the rules.
Now, if I manually change the value of the OPTLOCK field then Drools throws an exception:
javax.persistence.OptimisticLockException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.drools.persistence.info.SessionInfo#1]
followed by:
Caused by: org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [org.drools.persistence.info.SessionInfo#1]
I am unable to post the entire Stacktrace due to text length limitations here. The entire stacktrace can be viewed in this GitHub project.
I am not sure whether Drools is using the Pessimistic Lock as defined in the environment. About my session implementation, I want to have a single KieSession since I am using KieSession as a Bean.
Below is my implementation:
The configuration class:
#Configuration
public class DynamicDroolsConfig {
private KieServices kieServices;
private KieFileSystem kieFileSystem;
#Autowired
private PersistentSessionDAO persistentSessionDAO;
#PersistenceUnit
private EntityManagerFactory entityManagerFactory;
#Autowired
private PlatformTransactionManager platformTransactionManager;
#PostConstruct
private void init() {
this.kieServices = KieServices.Factory.get();
this.kieFileSystem = kieServices.newKieFileSystem();
}
#Bean
public KieServices getKieServices() {
return this.kieServices;
}
#Bean
public KieContainer getKieContainer() {
kieFileSystem.write(ResourceFactory.newClassPathResource("rules/rules.drl"));
final KieRepository kieRepository = kieServices.getRepository();
kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
KieBuilder kb = kieServices.newKieBuilder(kieFileSystem).buildAll();
KieModule kieModule = kb.getKieModule();
return kieServices.newKieContainer(kieModule.getReleaseId());
}
#Bean
public KieFileSystem getFileSystem() {
return kieFileSystem;
}
#Bean
public KieSession kieSession() {
List<SessionInfo> sessionDetails = persistentSessionDAO.getSessionDetails();
if (sessionDetails.size() == 0) {
return kieServices.getStoreServices().newKieSession(getKieContainer().getKieBase(), null, getEnv());
} else {
return kieServices.getStoreServices().loadKieSession(sessionDetails.get(0).getId(), getKieContainer().getKieBase(), null, getEnv());
}
}
private Environment getEnv() {
Environment env = kieServices.newEnvironment();
env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, entityManagerFactory);
env.set(EnvironmentName.TRANSACTION_MANAGER, platformTransactionManager);
env.set(EnvironmentName.USE_PESSIMISTIC_LOCKING, true);
env.set(EnvironmentName.USE_PESSIMISTIC_LOCKING_MODE, LockModeType.PESSIMISTIC_FORCE_INCREMENT.name());
return env;
}
}
The controller class:
#RestController
public class MyController {
#Autowired
private KieSession kieSession;
#Transactional
#GetMapping("fire-person")
public void firePerson() {
Person person = new Person();
person.setName("Christy");
kieSession.insert(person);
kieSession.fireAllRules();
}
}
The Fact class
public class Person implements Serializable {
private String name;
private int age;
private String gender;
private String toCompareName;
private String toCompareGender;
// getters and setters
}
The repository interface:
public interface DroolsSessionRepository extends JpaRepository<SessionInfo, Long> {
}
The service class:
#Service
public class PersistentSessionDAO {
#Autowired
private DroolsSessionRepository droolsSessionRepository;
public List<SessionInfo> getSessionDetails() {
return droolsSessionRepository.findAll();
}
}
The runner class:
#EntityScan(basePackages = {"com.sam.springdroolspersistence.entity", "org.drools.persistence.info"})
#EnableJpaRepositories
#SpringBootApplication
public class SpringDroolsPersistenceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringDroolsPersistenceApplication.class, args);
}
}
The Drools dependencies used:
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-persistence-jpa</artifactId>
<version>${drools-version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>${drools-version}</version>
</dependency>
<dependency>
<groupId>org.jbpm</groupId>
<artifactId>jbpm-persistence-jpa</artifactId>
<version>${drools-version}</version>
</dependency>
The code implementation can also be found in this GitHub Project. Any kind of help/suggestions will be much appreciated. Thank you.
Pessimistic locking is implemented only in JBPM see here
There's no such functionality in Drools persistence, SessionInfo will always use OptimisticLocking based on JPA's #Version annotation.
If you need such feature, please file a feature request on Drools' Jira
I have built an entity listener but have not figured out how to register it so that it will get called. This all runs, and I verified in the debugger that the
registration code executes (apparently successfully) at startup. But the debugger never stops in the listener code.
This is my listener:
public class DirtyAwareListener implements PostLoadEventListener
{
#Override
public void onPostLoad(PostLoadEvent postLoadEvent)
{
if (postLoadEvent.getEntity() instanceof DirtyAware)
{
((DirtyAware)postLoadEvent.getEntity()).commitFields();
}
}
}
and this is the registration component:
#Component
public class HibernateListenerConfigurer
{
#PersistenceUnit
private EntityManagerFactory emf;
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
protected void init()
{
DirtyAwareListener listener = new DirtyAwareListener();
// SessionFactoryImpl sessionFactory = emf.unwrap(SessionFactoryImpl.class);
EventListenerRegistry registry = ((SessionFactoryImpl)sessionFactory).getServiceRegistry().getService(EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.POST_LOAD).appendListener(listener);
}
}
Here is how my general Hibernate configuration code generates a session factory:
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(getDataSource());
sessionFactory.setPackagesToScan("com.my.entities");
sessionFactory.setHibernateProperties(getHibernateProperties());
sessionFactory.setEntityInterceptor(new DirtyAwareInterceptor());
return sessionFactory;
Note that the interceptor does work as expected (but unfortunately does not have hooks where I need them.)
To add entity listeners implement org.hibernate.integrator.spi.Integrator. See example https://www.boraji.com/hibernate-5-event-listener-example
I got this working as desired using the Integrator approach as Anton suggested. The link provided in his answer did not provide sufficient information for me to get this to work - I had to reference multiple posts and also do a bit of trial and error. Since I could not find a single post which provided the info, here is how I did it:
The listener code is the same as the above. The Configurer code is not needed - I deleted it. Here is the new Integrator code:
#Component
public class EventListenerIntegrator implements Integrator
{
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry)
{
EventListenerRegistry eventListenerRegistry =
sessionFactoryServiceRegistry.getService(EventListenerRegistry.class);
DirtyAwareListener t = new DirtyAwareListener();
eventListenerRegistry.getEventListenerGroup(EventType.POST_LOAD).appendListener(t);
}
#Override
public void disintegrate(SessionFactoryImplementor sessionFactoryImplementor, SessionFactoryServiceRegistry sessionFactoryServiceRegistry) {}
}
And here is the revised getSessionFactory method on my #Configuration class:
private static SessionFactory sessionFactory = null;
#Bean
public SessionFactory getSessionFactory()
{
if (sessionFactory == null)
{
BootstrapServiceRegistry bootstrapRegistry =
new BootstrapServiceRegistryBuilder()
.applyIntegrator(new EventListenerIntegrator())
.build();
StandardServiceRegistryBuilder registryBuilder =
new StandardServiceRegistryBuilder(bootstrapRegistry);
registryBuilder.applySetting(org.hibernate.cfg.Environment.DATASOURCE, getDataSource());
registryBuilder.applySettings(getHibernateProperties());
StandardServiceRegistry registry = registryBuilder.build();
MetadataSources sources = new MetadataSources(registry).addPackage("com.my.entities");
sources.addAnnotatedClass(User.class);
Metadata metadata = sources.getMetadataBuilder().build();
sessionFactory = metadata.getSessionFactoryBuilder().build();
}
return sessionFactory;
}
Note: I think the addPackage call is not needed and does not do anything. I had hoped it would do the package scan the old code was doing, but it does not do that. I simply changed that to explicity add each annotated class.
I'm using Spring 4.3.8.RELEASE with Hibernate 5.1.5.Final. I want to have a method executed after another another transaction completes. That transaction is defined below
#Service("organizationService")
#Transactional
public class OrganizationServiceImpl implements OrganizationService, ApplicationEventPublisherAware
{
private ApplicationEventPublisher publisher;
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher)
{
this.publisher = publisher;
}
#Override
public void save(Organization organization)
{
...
// sync data with ThirdParty but only if something has definitelychanged on the SB
// side, in which case we want to send ThirdParty an update.
if (!hasSameAttributes)
{
publisher.publishEvent(new ThirdPartyOrganizationEvent(organization.getId()));
} // if
} // save
So here is the method that I want executed after the above transaction completes ...
#Service
public class ThirdPartyAPIServiceImpl implements ThirdPartyAPIService
{
#Override
#TransactionalEventListener
public boolean updateOrg(final ThirdPartyOrganizationEvent thirdPartyOrgEvent)
{
...
}
But when I load my application context I get this error
Caused by: java.lang.IllegalStateException: No TransactionalEventListener annotation found on method: public abstract boolean org.mainco.subco.myproject.service.ThirdPartyAPIService.updateOrg(org.mainco.subco.myproject.domain.ThirdPartyOrganizationEvent)
at org.springframework.transaction.event.ApplicationListenerMethodTransactionalAdapter.<init>(ApplicationListenerMethodTransactionalAdapter.java:55)
at org.springframework.transaction.event.TransactionalEventListenerFactory.createApplicationListener(TransactionalEventListenerFactory.java:55)
at org.springframework.context.event.EventListenerMethodProcessor.processBean(EventListenerMethodProcessor.java:159)
at org.springframework.context.event.EventListenerMethodProcessor.afterSingletonsInstantiated(EventListenerMethodProcessor.java:104)
... 34 more
Wbat do I need to do to get this configured properly?
Defining #TransactionalEventListener on interface method rather then on method implementing interface worked for me.
I'm trying to move away from manually-managed transactions to annotation based transactions in my Neo4j application.
I've prepared annotation-based Spring configuration file:
#Configuration
#EnableNeo4jRepositories("xxx.yyy.neo4jplanetspersistence.repositories")
#ComponentScan(basePackages = "xxx.yyy")
#EnableTransactionManagement
public class SpringDataConfiguration extends Neo4jConfiguration
implements TransactionManagementConfigurer{
public SpringDataConfiguration() {
super();
setBasePackage(new String[] {"xxx.yyy.neo4jplanetspojos"});
}
#Bean
public GraphDBFactory graphDBFactory(){
GraphDBFactory graphDBFactory = new GraphDBFactory();
return graphDBFactory;
}
#Bean
public GraphDatabaseService graphDatabaseService() {
return graphDBFactory().getTestGraphDB(); //new GraphDatabaseFactory().newEmbeddedDatabase inside
}
#Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
return neo4jTransactionManager(graphDatabaseService());
}
}
I've marked my repositories with #Transactional:
#Transactional
public interface AstronomicalObjectRepo extends
GraphRepository<AstronomicalObject>{
}
I've marked my unit test classes and test methods with #Transactional and commented old code that used to manually manage transactions:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {SpringDataConfiguration.class},
loader = AnnotationConfigContextLoader.class)
#Transactional
public class AstronomicalObjectRepoTest {
#Autowired
private AstronomicalObjectRepo repo;
#Autowired
private Neo4jTemplate neo4jTemplate;
(...)
#Test #Transactional
public void testSaveAndGet() {
//try (Transaction tx =
//neo4jTemplate.getGraphDatabaseService().beginTx()) {
AstronomicalObject ceres = new AstronomicalObject("Ceres",
1.8986e27, 142984000, 9.925);
repo.save(ceres); //<- BANG! Exception here
(...)
//tx.success();
//}
}
After that change the tests do not pass.
I receive:
org.springframework.dao.InvalidDataAccessApiUsageException: nested exception is org.neo4j.graphdb.NotInTransactionException
I have tried many different things (explicitly naming transaction manager in #Transactional annotation, changing mode in #EnableTransactionManagment...), nothing helped.
Will be very grateful for a clue about what I'm doing wrong.
Thanks in advance!
I found the reason...
SDN does not support newest Neo4j in the terms of transaction.
I believe it is because SpringTransactionManager in neo4j-kernel has gone in 2.2+ releases, but not 100% sure.
On github we can see that 7 hours ago the change was made to fix it:
https://github.com/spring-projects/spring-data-neo4j/blob/master/spring-data-neo4j/src/main/java/org/springframework/data/neo4j/config/JtaTransactionManagerFactoryBean.java
A quick fix that worked for me was to override neo4jTransactionManager method from Neo4jConfiguration in my configuration, using Neo4jEmbeddedTransactionManager class:
#Override
public PlatformTransactionManager neo4jTransactionManager(GraphDatabaseService graphDatabaseService) {
Neo4jEmbeddedTransactionManager newTxMgr = new Neo4jEmbeddedTransactionManager(graphDatabaseService());
UserTransaction userTransaction = new UserTransactionAdapter( newTxMgr );
return new JtaTransactionManager( userTransaction, newTxMgr );
}