quartz储存方式之JDBC JobStoreTX

Posted long77

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了quartz储存方式之JDBC JobStoreTX相关的知识,希望对你有一定的参考价值。

这篇单单记录一下个人配置使用quartz的JDBC JobStoreTX的过程以及其中遇到的问题,这里的quartz是version2.2.1,数据库使用的MySQL

JDBCJobStore储存是速度比较慢的,但是也不至于很坏,通过JDBCJobStore储存于数据库的方式适用于Oracle,PostgreSQL, mysql, MS SQLServer, HSQLDB, DB2等数据库。

 

1) 建表

在下载的文件的docs/dbTables目录下有对应建表语句,如果没有对应于应用的就自己改动来适应。这些个表都有"QRTZ"前缀,可以作为区别于别的命名。

 

2) 选定事务

如果你不需要绑定其他事务处理,你可以选择quartz的事务,其通过JobStoreTX来管理,这也是常用的选择,当然如果你要和你的应用容器一起管理,那你可以使用quartz的

JobStoreCMT,quartz通过JobStoreCMT来的使用来让你的应用容器管理quartz的事务。

 

3) 创建数据源

一个是提供一个connection,让quartz可以连接到数据库,另一个是提供的JNDI的方式,让quartz可以从所在容器中获取到。

使用JDBC连接方式(假设你使用的是StdSchedulerFactory):

 

首先:
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX
或者
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreCMT

 

这里当然选择JobStoreTX。


其次:
选定JDBC代理类,quartz里提供了StdJDBCDelegate,如果这个不能正常工作的,你可以选用其他代理类(在org.quar.impl.jdbcjobstore package或者其子包中可以找到),包括DB2v6Delegate (for DB2 version 6 and earlier), HSQLDBDelegate (for HSQLDB),MSSQLDelegate (for Microsoft SQLServer), PostgreSQLDelegate (for PostgreSQL),
WeblogicDelegate (for using JDBC drivers made by WebLogic), OracleDelegate (for using oracle)等等。
并这样配置:
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate


第三:
指定前缀
org.quartz.jobStore.tablePrefix = QRTZ_


第四:
指定数据源名称:
org.quartz.jobStore.dataSource = myDS


第五:定义ConnectionProvider的实现类

这里我找了一下,quartz提供了一个org.quartz.utils.PoolingConnectionProvider,于是,我就有了如下配置:

org.quartz.dataSource.myDS.connectionProvider.class:org.quartz.utils.PoolingConnectionProvider


第六:配置数据源属性

org.quartz.dataSource.myDS.driver: com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8
org.quartz.dataSource.myDS.user: root
org.quartz.dataSource.myDS.password: root
org.quartz.dataSource.myDS.maxConnections = 30

完事,使用最简单的一个例子来跑跑,可是报错了……

Caused by: Java.lang.InstantiationException: org.quartz.utils.PoolingConnectionProvider
at java.lang.Class.newInstance(Unknown Source)
at org.quartz.impl.StdSchedulerFactory.instantiate(StdSchedulerFactory.java:936)
... 3 more

跟了一下源码得到详细一点的原因:

                ConnectionProvider cp = null;
                try {
                    cp = (ConnectionProvider) loadHelper.loadClass(cpClass).newInstance();
                } catch (Exception e) {
                    initException = new SchedulerException("ConnectionProvider class ‘" + cpClass
                            + "‘ could not be instantiated.", e);
                    throw initException;
                }

红色那行报的:

org.eclipse.debug.core.DebugException: com.sun.jdi.ClassNotLoadedException: Type has not been loaded occurred while retrieving component type of array.

百度了一把,没什么收获,看看文档,只说定义自己的这个类,但是为啥,也没有代码,为什么不告诉直接用上述的那个provider呢?

直接测试一下loadclass("org.quartz.utils.PoolingConnectionProvider");是没有问题的,那就是newInstance();的时候有问题咯。靠谱的还是JDK文档,立马去看一下,果然有收获:创建此Class 对象所表示的类的一个新实例。如同用一个带有一个空参数列表的 new 表达式实例化该类。如果该类尚未初始化,则初始化这个类。 

如同空参数……

肯定是那个provider没有提供无参构造……再去看看呢,果然没有,好吧,我承认我懒了那么一丢丢。

那我再找找有没有其他的用于设置这个的吧,居然没有找到(估计没有找仔细),还是得定义一个自己的provider,没事,就参考这个provider,还是用C3P0来定义呗。

定义好后,来试试,果然顶用。以下是我的可运行代码:

quartz.properties

 

[plain] view plain copy
 
  1. # Default Properties file for use by StdSchedulerFactory  
  2. # to create a Quartz Scheduler Instance, if a different  
  3. # properties file is not explicitly specified.  
  4. #  
  5.   
  6. org.quartz.scheduler.instanceName: DefaultQuartzScheduler  
  7. org.quartz.scheduler.rmi.export: false  
  8. org.quartz.scheduler.rmi.proxy: false  
  9. org.quartz.scheduler.wrapJobExecutionInUserTransaction: false  
  10.   
  11. org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool  
  12. org.quartz.threadPool.threadCount: 10  
  13. org.quartz.threadPool.threadPriority: 5  
  14. org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: false  
  15.   
  16. org.quartz.jobStore.misfireThreshold: 60000  
  17.   
  18. org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX  
  19. org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate  
  20. org.quartz.jobStore.tablePrefix: QRTZ_  
  21. org.quartz.jobStore.dataSource: myDS  
  22.   
  23. org.quartz.dataSource.myDS.connectionProvider.class: org.quartz.examples.example17.MyPoolingconnectionProvider  
  24. org.quartz.dataSource.myDS.driver: com.mysql.jdbc.Driver  
  25. org.quartz.dataSource.myDS.url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8  
  26. org.quartz.dataSource.myDS.user: root  
  27. org.quartz.dataSource.myDS.password: root  
  28. org.quartz.dataSource.myDS.maxConnections: 30  


MyPoolingconnectionProvider.java

 

[java] view plain copy
 
  1. package org.quartz.examples.example17;  
  2.   
  3. import java.beans.PropertyVetoException;  
  4. import java.sql.Connection;  
  5. import java.sql.SQLException;  
  6.   
  7. import org.quartz.SchedulerException;  
  8. import org.quartz.utils.ConnectionProvider;  
  9.   
  10. import com.mchange.v2.c3p0.ComboPooledDataSource;  
  11.   
  12. /** 
  13.  *  
  14.  * @author wz 
  15.  * 
  16.  */  
  17. public class MyPoolingconnectionProvider implements ConnectionProvider {  
  18.   
  19.     /** Default maximum number of database connections in the pool. */  
  20.     public static final int DEFAULT_DB_MAX_CONNECTIONS = 10;  
  21.   
  22.     /** Default maximum number of database connections in the pool. */  
  23.     public static final int DEFAULT_DB_MAX_CACHED_STATEMENTS_PER_CONNECTION = 120;  
  24.   
  25.     private String driver;  
  26.     private String url;  
  27.     private String user;  
  28.     private String password;  
  29.     private int maxConnections;  
  30.     private int maxCachedStatementsPerConnection;  
  31.     private int maxIdleSeconds;  
  32.     private String validationQuery;  
  33.     private int idleConnectionValidationSeconds;  
  34.     private boolean validateOnCheckout;  
  35.     private String discardIdleConnectionsSeconds;  
  36.   
  37.     private ComboPooledDataSource datasource;  
  38.   
  39.     /** 
  40.      * 无参构造,必须要有[没有其他构造的话也可以不写] 
  41.      */  
  42.     public MyPoolingconnectionProvider() {  
  43.   
  44.     }  
  45.   
  46.     public Connection getConnection() throws SQLException {  
  47.         return datasource.getConnection();  
  48.     }  
  49.   
  50.     public void shutdown() throws SQLException {  
  51.         datasource.close();  
  52.   
  53.     }  
  54.   
  55.     /** 
  56.      * 初始化方法,应该在调用其setter后调用 
  57.      */  
  58.     public void initialize() throws SQLException {  
  59.         if (this.url == null) {  
  60.             throw new SQLException("DBPool could not be created: DB URL cannot be null");  
  61.         }  
  62.   
  63.         if (this.driver == null) {  
  64.             throw new SQLException("DBPool driver could not be created: DB driver class name cannot be null!");  
  65.         }  
  66.   
  67.         if (this.maxConnections < 0) {  
  68.             throw new SQLException("DBPool maxConnectins could not be created: Max connections must be greater than zero!");  
  69.         }  
  70.   
  71.         datasource = new ComboPooledDataSource();  
  72.         try {  
  73.             datasource.setDriverClass(this.driver);  
  74.         } catch (PropertyVetoException e) {  
  75.             try {  
  76.                 throw new SchedulerException("Problem setting driver class name on datasource: " + e.getMessage(), e);  
  77.             } catch (SchedulerException e1) {  
  78.             }  
  79.         }  
  80.         datasource.setJdbcUrl(this.url);  
  81.         datasource.setUser(this.user);  
  82.         datasource.setPassword(this.password);  
  83.         datasource.setMaxPoolSize(this.maxConnections);  
  84.         datasource.setMinPoolSize(1);  
  85.         datasource.setMaxIdleTime(maxIdleSeconds);  
  86.         datasource.setMaxStatementsPerConnection(this.maxCachedStatementsPerConnection);  
  87.   
  88.         if (this.validationQuery != null) {  
  89.             datasource.setPreferredTestQuery(this.validationQuery);  
  90.             if (!validateOnCheckout)  
  91.                 datasource.setTestConnectionOnCheckin(true);  
  92.             else  
  93.                 datasource.setTestConnectionOnCheckout(true);  
  94.             datasource.setIdleConnectionTestPeriod(this.idleConnectionValidationSeconds);  
  95.         }  
  96.     }  
  97.   
  98.     /*------------------------------------------------- 
  99.      *  
  100.      * setters 如果有必要,你可以添加一些getter 
  101.      * ------------------------------------------------ 
  102.      */  
  103.     public void setDriver(String driver) {  
  104.         this.driver = driver;  
  105.     }  
  106.   
  107.     public void setUrl(String url) {  
  108.         this.url = url;  
  109.     }  
  110.   
  111.     public void setUser(String user) {  
  112.         this.user = user;  
  113.     }  
  114.   
  115.     public void setPassword(String password) {  
  116.         this.password = password;  
  117.     }  
  118.   
  119.     public void setMaxConnections(int maxConnections) {  
  120.         this.maxConnections = maxConnections;  
  121.     }  
  122.   
  123.     public void setMaxCachedStatementsPerConnection(int maxCachedStatementsPerConnection) {  
  124.         this.maxCachedStatementsPerConnection = maxCachedStatementsPerConnection;  
  125.     }  
  126.   
  127.     public void setMaxIdleSeconds(int maxIdleSeconds) {  
  128.         this.maxIdleSeconds = maxIdleSeconds;  
  129.     }  
  130.   
  131.     public void setValidationQuery(String validationQuery) {  
  132.         this.validationQuery = validationQuery;  
  133.     }  
  134.   
  135.     public void setIdleConnectionValidationSeconds(int idleConnectionValidationSeconds) {  
  136.         this.idleConnectionValidationSeconds = idleConnectionValidationSeconds;  
  137.     }  
  138.   
  139.     public void setValidateOnCheckout(boolean validateOnCheckout) {  
  140.         this.validateOnCheckout = validateOnCheckout;  
  141.     }  
  142.   
  143.     public void setDiscardIdleConnectionsSeconds(String discardIdleConnectionsSeconds) {  
  144.         this.discardIdleConnectionsSeconds = discardIdleConnectionsSeconds;  
  145.     }  
  146.   
  147.     public void setDatasource(ComboPooledDataSource datasource) {  
  148.         this.datasource = datasource;  
  149.     }  
  150.   
  151.     protected ComboPooledDataSource getDataSource() {  
  152.         return datasource;  
  153.     }  
  154. }  


SimpleExample.java

 

[java] view plain copy
 
  1. package org.quartz.examples.example17;  
  2.   
  3. import java.util.Date;  
  4.   
  5. import org.quartz.DateBuilder;  
  6. import org.quartz.JobBuilder;  
  7. import org.quartz.JobDetail;  
  8. import org.quartz.Scheduler;  
  9. import org.quartz.SchedulerFactory;  
  10. import org.quartz.TriggerBuilder;  
  11. import org.quartz.impl.StdSchedulerFactory;  
  12. import org.quartz.impl.triggers.SimpleTriggerImpl;  
  13. import org.slf4j.Logger;  
  14. import org.slf4j.LoggerFactory;  
  15.   
  16. public class SimpleExample  
  17. {  
  18.   public void run()  
  19.     throws Exception  
  20.   {  
  21.     Logger log = LoggerFactory.getLogger(SimpleExample.class);  
  22.   
  23.     log.info("------- Initializing ----------------------");  
  24.     //通过调度器工厂获取调度器,初始化工程时须指定其使用我们自己的配置文件  
  25.     SchedulerFactory sf = new StdSchedulerFactory("org/quartz/examples/example17/quartz.properties");  
  26.     Scheduler sched = sf.getScheduler();  
  27.       
  28.     //这儿clear一下,因为使用数据库储存方式时,shutdown的时候没有清除,第二次运行会报Job is already exist  
  29.     sched.clear();    
  30.   
  31.     log.info("------- Initialization Complete -----------");  
  32.   
  33.     Date runTime = DateBuilder.evenMinuteDate(new Date());  
  34.   
  35.     log.info("------- Scheduling Job  -------------------");  
  36.       
  37.     //创建任务详情  
  38.     JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();  
  39.     //创建触发器  
  40.     SimpleTriggerImpl trigger = (SimpleTriggerImpl)TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startAt(new Date()).build();  
  41.     trigger.setRepeatCount(5);  
  42.     trigger.setRepeatInterval(3000);  
  43.     log.info("------- Starttime =  "+trigger.getStartTime()+" -----------------");  
  44.       
  45.     //调度器、触发器、任务,三者关联  
  46.     sched.scheduleJob(job, trigger);  
  47.     log.info(job.getKey() + " will run at: " + runTime);  
  48.     //调度启动  
  49.     sched.start();  
  50.     log.info("------- Started Scheduler -----------------");  
  51.   
  52.     log.info("------- Waiting 1 minute... -------------");  
  53.     try  
  54.     {  
  55.       Thread.sleep(60000L);  
  56.     }  
  57.     catch (Exception e)  
  58.     {  
  59.     }  
  60.   
  61.     log.info("------- Shutting Down ---------------------");  
  62.     //调度关闭  
  63.     sched.shutdown(true);  
  64.     log.info("------- Shutdown Complete -----------------");  
  65.   }  
  66.   
  67.   public static void main(String[] args) throws Exception  
  68.   {  
  69.     SimpleExample example = new SimpleExample();  
  70.     example.run();  
  71.   }  
  72. }  


当然,运行example之前,得先添加mysql的driver的包,然后在mysql里建立一个叫“quartz”的database,并将docs/dbTables下的tables_mysql.sql脚本运行一下以建表。

 

 

注意:

1) org.quartz.jobStore.class: org.quartz.impl.jdbcjobstore.JobStoreTX,这个配置在这儿当然是用这个,

而org.quartz.jobStore.driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate则根据你的数据,通常使用当前这个,如果这个不适用就去quartz里org.quar.impl.jdbcjobstore包或其子包下找找,如还是未能满足需求,就自己实现一个。

2) org.quartz.jobStore.tablePrefix: QRTZ_,这个是quartz默认的,tables_mysql.sql以及一些其他操作(比如clear等)的代码都这样写了,所以就不要去修改了。

3) org.quartz.jobStore.dataSource: myDS这个的“键”必须是org.quartz.jobStore.dataSource,而“值”随你取,但必须有。

4) dataSource的属性配置,如下几个(可以自己添加其他的对应修改provider):

 

[html] view plain copy
 
  1. org.quartz.dataSource.myDS.connectionProvider.class: org.quartz.examples.example17.MyPoolingconnectionProvider  
  2. org.quartz.dataSource.myDS.driver: com.mysql.jdbc.Driver  
  3. org.quartz.dataSource.myDS.url: jdbc:mysql://localhost:3306/quartz?useUnicode=true&characterEncoding=utf-8  
  4. org.quartz.dataSource.myDS.user: root  
  5. org.quartz.dataSource.myDS.password: root  
  6. org.quartz.dataSource.myDS.maxConnections: 30  

它们的“键”必须是

[html] view plain copy
 
  1. org.quartz.dataSource."+yourdatasourcename+"."+yourProvider#datamembername  

原因如下:

 

 

[java] view plain copy
 
    1. String[] dsNames = cfg.getPropertyGroups(PROP_DATASOURCE_PREFIX);  
    2.         for (int i = 0; i < dsNames.length; i++) {  
    3.             PropertiesParser pp = new PropertiesParser(cfg.getPropertyGroup(  
    4.                     PROP_DATASOURCE_PREFIX + "." + dsNames[i], true));  
    5.   
    6.             String cpClass = pp.getStringProperty(PROP_CONNECTION_PROVIDER_CLASS, null);  
    7.   
    8.             // custom connectionProvider...  
    9.             if(cpClass != null) {  
    10.                 ConnectionProvider cp = null;  
    11.                 try {  
    12.                     cp = (ConnectionProvider) loadHelper.loadClass(cpClass).newInstance();  
    13.                 } catch (Exception e) {  
    14.                     initException = new SchedulerException("ConnectionProvider class ‘" + cpClass  
    15.                             + "‘ could not be instantiated.", e);  
    16.                     throw initException;  
    17.                 }  
    18.   
    19.                 try {  
    20.                     // remove the class name, so it isn‘t attempted to be set  
    21.                     pp.getUnderlyingProperties().remove(  
    22.                             PROP_CONNECTION_PROVIDER_CLASS);  
    23.   
    24.                     setBeanProps(cp, pp.getUnderlyingProperties());  
    25.                     cp.initialize();  
    26.                 } catch (Exception e) {  
    27.                     initException = new SchedulerException("ConnectionProvider class ‘" + cpClass  
    28.                             + "‘ props could not be configured.", e);  
    29.                     throw initException;  
    30.                 }  
    31.   
    32.                 dbMgr = DBConnectionManager.getInstance();  
    33.                 dbMgr.addConnectionProvider(dsNames[i], cp);  
    34.             }   


































以上是关于quartz储存方式之JDBC JobStoreTX的主要内容,如果未能解决你的问题,请参考以下文章

Spring任务调度之Quartz

Spring任务调度之Quartz

Spring任务调度之Quartz

iOS 2D绘图详解(Quartz 2D)之路径(stroke,fill,clip,subpath,blend)

SpringBoot2.6.3集成quartz

SpringBoot2.6.3集成quartz