2

What am I trying to achieve is schedule a query to my database every 30 minutes, this query will bring me many records with many new task to schedule, these new task will perform new CRUD operations to my database and some other operations.

First, I'm using @EnableScheduling to schedule a "SELECT" query

@Service
@EnableScheduling
public class ScheduleUpdatesTransaction {
    final static Logger logger = Logger.getLogger(ScheduleUpdatesTransaction.class);

    @Scheduled(fixedDelay = 10000)
    public void executeTransaction() {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(10);

        List<Object[]> programUpdatesList = playerDAO.getUpdatesByTimeIterval(myCustomDateTime);//Ok, it access database an retrieve data

        for(Object[] element: programUpdatesList){
            Long seconds = Long.parseLong(element[0].toString());
            Long myKey = Long.parseLong(element[1].toString());
            executor.schedule(new ScheduleThreadTransaction(myKey), seconds , TimeUnit.SECONDS);//Ok, it triggers the task
        }

        executor.shutdown();
    }
}

This is the ScheduleThreadTransaction class:

@Component
public class ScheduleThreadTransaction implements Runnable{

    @Autowired
    private PlayerTaskDAO playerTaskDAO;

    private Long myIdentificator;

    public ScheduleThreadTransaction() {
        super();
    }

    public ScheduleThreadTransaction(Long identificator) {
        myIdentificator = identificator;
    }

    public void run() {
        try{

            PlayerTask playerTask =  playerTaskDAO.findOne(myIdentificator);// not working, java.lang.NullPointerException

            //More CRUD operations here

        }catch(Exception e){
            e.printStackTrace();
        }
    }


    public Long getMyIdentificator() {
        return myIdentificator;
    }

    public void setMyIdentificator(Long myIdentificator) {
        this.myIdentificator = myIdentificator;
    }

}

The problem is when calling findOne, I get NullPointerException. Any suggestion? I'm using Spring 4 and JPA 2.1.

EDIT:

I made some changues in the configuration, this is my XML configuration:

<context:component-scan base-package="com.myproject"/>

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">

            <property name="objectMapper">
                <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                    <property name="serializationInclusion" value="NON_NULL"/>
                </bean>
            </property>
        </bean>     
    </mvc:message-converters>
</mvc:annotation-driven>


<mvc:resources mapping="/images/*" location="/images/" />

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/" />
    <property name="suffix" value=".jsp" />
</bean>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />
    <property name="persistenceUnitName" value="myProjectPersistence" />
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="jpaVendorAdapter" />
    <property name="jpaDialect" ref="jpaDialect" />
</bean>

<bean id="jpaVendorAdapter" class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="database" value="HSQL" />
    <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" />
</bean>

<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
    <property name="dataSource" ref="dataSource" />
    <property name="jpaDialect" ref="jpaDialect" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://127.0.0.1:3306/myConection" />
    <property name="username" value="123456" />
    <property name="password" value="654321" />
</bean>

<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="resourceLoaderPath" value="/WEB-INF/velocity/"/>
</bean>

<bean id="statusUpdateService" class="com.myproject.StatusUpdateService" />

<bean id="scheduledExecutorFactoryBean" class="org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean" />

<bean id="scheduleThreadTransactionService" class="com.myproject.ScheduleThreadTransactionService" />

This is the ScheduleUpdatesTransaction class:

@Service
@EnableScheduling
public class ScheduleUpdatesTransaction {
    final static Logger logger = Logger.getLogger(ScheduleUpdatesTransaction.class);

    @Autowired
    private PlayerCoreUpdateDAO playerCoreUpdateDAO;

    @Autowired
    StatusUpdateService statusUpdateService;

    @Scheduled(fixedDelay = 100000)
    public void executeTransaction() {
        Util util = new Util();

        List<Object[]> programUpdatesList = playerCoreUpdateDAO.getWeaponUpdatesByTimeIterval(new Date());

        for(Object[] element: programUpdatesList){
            Long seconds = Long.parseLong(element[2].toString());
            String finishDate =element[3].toString();
            Long myUpdateKey = Long.parseLong(element[0].toString());

            System.out.println("UPGRADETIME " + seconds + " FINISHUPGRADETIME " + myUpdateKey);

            statusUpdateService.executeTransaction(seconds, myUpdateKey);   
        }
    }
}

The StatusUpdateService class:

@Service
public class StatusUpdateService {
    final static Logger logger = Logger.getLogger(StatusUpdateService.class);

    @Autowired
    private ScheduledExecutorFactoryBean scheduledExecutorFactoryBean;

    @Autowired
    private Provider<ScheduleThreadTransactionService> runnableFactory;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void executeTransaction(Long seconds, Long myUpdateKey){
        try{
            ScheduledExecutorService executor = scheduledExecutorFactoryBean.getObject();

            ScheduleThreadTransactionService runnable = runnableFactory.get();

            runnable.setElementKey(myUpdateKey);

            executor.schedule(runnable, seconds, TimeUnit.SECONDS);

        }catch(Exception e){
            logger.error(e);
        }
    }

}

And finally the ScheduleThreadTransactionService class:

@Service
public class ScheduleThreadTransactionService implements Runnable{
    final static Logger logger = Logger.getLogger(ScheduleThreadTransactionService.class);

    private Long elementKey;

    public ScheduleThreadTransactionService() {

    }

    public ScheduleThreadTransactionService(Long elementKey) {
        this.elementKey = elementKey;
    }

    @Autowired
    private PlayerCoreUpdateDAO playerCoreUpdateDAO;
    //Adding @transactional throws an error
    public void run() {
        try{
            PlayerCoreUpdate playerCoreUpdate = playerCoreUpdateDAO.findOne(elementKey);//It works!
            playerCoreUpdateDAO.update(playerCoreUpdate);//Didn't work, need @transactional

        }catch(Exception e){
            logger.error(e);
        }
    }

    public Long getElementKey() {
        return elementKey;
    }

    public void setElementKey(Long elementKey) {
        this.elementKey = elementKey;
    }

    public PlayerCoreUpdateDAO getPlayerCoreUpdateDAO() {
        return playerCoreUpdateDAO;
    }

    public void setPlayerCoreUpdateDAO(PlayerCoreUpdateDAO playerCoreUpdateDAO) {
        this.playerCoreUpdateDAO = playerCoreUpdateDAO;
    }

}

Adding @Transactional give me this error:

2015-09-28 12:33:32,840 scheduledExecutorFactoryBean-1 ERROR service.StatusUpdateService - org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.myproject.ScheduleThreadTransactionService] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

Is there a short fix to get this thing running?

Regards.

Eric DZ
  • 23
  • 1
  • 4

2 Answers2

0

You are using new to instantiate your runnable spring component. Spring container would not be able to inject the dependencies if you are instantiating manually.

You need to get the instance of the runnable from the bean factory. A cleaner approach would be to use a factory and lookup method injection.

If all your spring config's are using annotations the lookup method equivalent would be

IN your sheduleThreadTransaction class make the following changes

@Autowired
  private javax.inject.Provider<ScheduleThreadTransaction> runnableFactory;

and inside your executeTransaction method

 ScheduleThreadTransaction runnable = runnableFactory.get();
 runnable.setIdentifier(myIdentifier);
jozzy
  • 2,863
  • 3
  • 15
  • 12
  • Injecting runnableFactory did the trick, the problem is I can only execute SELECT querys, i can't modify my data because "No transactional EntityManager available exception" is thrown, and adding @Transactional to my method throws "No qualifying bean of type". Do I need to handle the transaction manually? – Eric DZ Aug 18 '15 at 08:46
  • Thats a different question altogether. You need to look into configuring spring transactions. http://stackoverflow.com/questions/14522691/java-lang-illegalstateexception-no-transactional-entitymanager-available – jozzy Aug 18 '15 at 08:57
  • If you have multiple crud operations inside run (), its better to define a service class backed by an interface which has method/s that calls multiple dao operations. Mark those methods as transactional and inject this service instead of playerDAO..... **** Since the answer above solved your original question pls mark it as right/upvote..**** – jozzy Aug 18 '15 at 09:11
  • Yes, I'm using this: – Eric DZ Aug 18 '15 at 09:14
0

you are not injecting the spring bean in a right way but you are creating the reference with new , thus its not a context aware spring bean and so the DAO is null ,but because you're already using the scheduling / async that spring provides , you could create a singleton bean , with the the method you want and inject in there all your DAOs and staff , and make that method called async by the scheduler , so you will avoid creating by your own a thread executor and the corresponding threads and let spring do this for you so at least the executor and the services will be context aware.

@Component
public class ScheduleThreadTransactionService{

 @Autowired
 private PlayerTaskDAO playerTaskDAO;

 @Async
 public void callAsync(Long myIdentificator)
 {
    try{
         PlayerTask playerTask =  playerTaskDAO.findOne(myIdentificator);
         //More CRUD operations here
         }catch(Exception e){
            e.printStackTrace();
        }
     }
}

inject that in your scheduler

@Service
@EnableScheduling
public class ScheduleUpdatesTransaction {

    @Autowired
    private ScheduleThreadTransactionService service;

    @Scheduled(fixedDelay = 10000)
    public void executeTransaction() 
    {
       for(Long key : playerDAO.getUpdatesByTimeIterval(myCustomDateTime))
          service.callAsync(key);
    }
}

note you might need something more in your spring-conf.xml, sharing a configuration that uses spring 3 + , with a ConcurrentTaskExecutor , but you can check the other implementations that spring provides , cause there might be a case that suits your needs.

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:task="http://www.springframework.org/schema/task" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
 http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.0.xsd
 ">


    <context:component-scan base-package = "your.package"/>  
    <task:annotation-driven executor="taskExecutor" proxy-target-class="false"/>

    <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ConcurrentTaskExecutor">
        <property name="concurrentExecutor" ref="threadPoolExecutor" />
    </bean>

    <bean id="threadPoolExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >
        <property name="corePoolSize" value="25" />
        <property name="maxPoolSize" value="50" />
        <property name="queueCapacity" value="100" />
    </bean> 

</beans>
AntJavaDev
  • 1,204
  • 1
  • 18
  • 24
  • Using @Async allows me to perform updates in the database? the time to trigger can be parameterized programmatically? – Eric DZ Aug 18 '15 at 09:04
  • yes you can use it in a postExecute way , but there is no point for that because you have the scheduler already for that reason , so for your example , in which the scheduler is defined every 10 seconds , each time it will invoke a new thread to execute the update and when it finishes it will return the thread to the thread pool – AntJavaDev Aug 18 '15 at 09:25
  • Yes, I ran this example to execute every 10 seconds just for testing, it will be 30 minutes. The new scheduled processes from getUpdatesByTimeIterval can't be fixedDelay, the time in which each new proccess will wait to execute will vary. – Eric DZ Aug 18 '15 at 20:33