如何用PHP/MySQL为 iOS App 写一个简单的web服务器(译) PART1

本文详细介绍如何搭建一个简易的Web服务器,该服务器基于PHP和MySQL,用于与iOS应用程序交互,实现促销码验证等功能。

原文:http://www.raywenderlich.com/2941/how-to-write-a-simple-phpmysql-web-service-for-an-ios-app

  作为一个iPhone/iPad开发者,能够自己写一个简单的web服务器将是很有用的。

  例如,你可能希望在软件启动时显示一些来自服务器的更新,或者在服务器端保存一些用户数据。除了你的想象力,没有什么能限制你了。

  在第一篇中,我们将会一步一步的建立一个web服务器,基于promo code system(促销码系统),我在我的第一个软件中使用的,Wild Fables.在第二篇中,我们将会写一个iOS App来和它进行交互。

为了完成这个教程,你将需要一个web服务器,并装有MySQL和PHP。如果你没有,那么你有以下几种选择:

  • 如果你想在你的Mac(free)上运行Apache/MySQL/PHP,有很多教程可以帮你。这里有一个教程
  • 如果你想租一个服务器(需要花钱),这里有一个教程
  • 或者你很懒,以上两种你都不想做,那么你可以使用我在本教程PART2做的服务器。

你不需要有PHP和MySQL的经验(当然有更好)因为这个教程包含了所有你需要的代码。

 

你将做什么

也许你已经知道了,如果为你的App添加了内购功能,苹果并没有提供内置的系统来提供内购的促销码。

然而,建立你自己的内购促销码将会很有用。

如果你不需要建立这个特殊的系统也没关系,你会学到怎么建立web服务器并与App交互。

建立数据库:

第一步时建立一个数据库表。这个教程你需要3个表:

  • rw_app:保存需要使用促销码系统的软件列表。这样,你就可以为不同的App使用相同的表


    id
: app的唯一标示.

    app_id:  app 的唯一字符串标示.

  • w_promo_code:保存可用促销码的表
    •   id:唯一表示.
    •   rw_app_id: 对应的App.
    •   code: 用户输入的促销码字符.
    •   unlock_code: 返回给App的促销码字符.
    •   uses_remaining:促销码剩余可使用次数.
  • rw_promo_code_redeemed:保存促销码兑取后的信息。为了防止一个设备用一个促销码兑取多次。
    • id: 唯一标示.
    • rw_promo_code_id: 已经兑取的促销码ID (from rw_promo_code).
    • device_id: 已经兑取的设备ID.
    • redeemed_time: 兑取的时间.

这是建表的SQL代码:

DROP TABLE IF EXISTS rw_promo_code;
DROP TABLE IF EXISTS rw_app;
DROP TABLE IF EXISTS rw_promo_code_redeemed;

CREATE TABLE rw_promo_code (
    id mediumint NOT NULL AUTO_INCREMENT PRIMARY KEY,    
    rw_app_id tinyint NOT NULL, 
    code varchar(255) NOT NULL,
    unlock_code varchar(255) NOT NULL,
    uses_remaining smallint NOT NULL
);

CREATE TABLE rw_app (
    id mediumint NOT NULL AUTO_INCREMENT PRIMARY KEY,    
    app_id varchar(255) NOT NULL
);

CREATE TABLE rw_promo_code_redeemed (
    id mediumint NOT NULL AUTO_INCREMENT PRIMARY KEY,    
    rw_promo_code_id mediumint NOT NULL,
    device_id varchar(255) NOT NULL,
    redeemed_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

在你的web服务器上,你需要建立一个MySQL数据库并建立这三张表。这里是完成的命令:

保存上面的代码到一个名为create.sql的文件,然后:

rwenderlich@kermit:~$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1286
Server version: 5.1.37-1ubuntu5.1-log (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> create database promos;
Query OK, 1 row affected (0.00 sec)

mysql> use promos;
Database changed
mysql> grant all privileges on promos.* to 'username'@'localhost' identified by 'password';
Query OK, 0 rows affected (0.00 sec)

mysql> exit
Bye

rwenderlich@kermit:~$ mysql -u username -p promos < create.sql
Enter password: 
rwenderlich@kermit:~$ mysql -u root -p
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1417
Server version: 5.1.37-1ubuntu5.1-log (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use promos;
Database changed
mysql> show tables ;
+------------------------+
| Tables_in_promos       |
+------------------------+
| rw_app                 | 
| rw_promo_code          | 
| rw_promo_code_redeemed | 
+------------------------+
3 rows in set (0.00 sec)

现在已有了三张空表。下一步,建立一个测试的app:

INSERT INTO rw_app VALUES(1, 'com.razeware.test');
INSERT INTO rw_promo_code VALUES(1, 1, 'test', 'com.razeware.test.unlock.cake', 10000);

好的。现在数据库已经连接,可以写PHP服务器了。

验证PHP/MySQL

在开始实现PHP服务器之前,首先检查PHP是否在你的服务器上运行正常。在你的服务器上建立一个叫promos的文件夹,在里面建立一个叫index.php的文件:

<?php
 
class RedeemAPI {
    // Main method to redeem a code
    function redeem() {
        echo "Hello, PHP!";
    }
}
 
// This is the first thing that gets called when this page is loaded
// Creates a new instance of the RedeemAPI class and calls the redeem method
$api = new RedeemAPI;
$api->redeem();
 
?>

你可以用你的服务器的URL测试,也可以像下面这样在命令行测试:

Ray-Wenderlichs-Mac-mini-2:~ rwenderlich$ curl http://www.wildfables.com/promos/
Hello, PHP!

下一步,扩展这个类,确保你的服务器可以连接到数据库:

class RedeemAPI {
    private $db;
 
    // Constructor - open DB connection
    function __construct() {
        $this->db = new mysqli('localhost', 'username', 'password', 'promos');
        $this->db->autocommit(FALSE);
    }
 
    // Destructor - close DB connection
    function __destruct() {
        $this->db->close();
    }
 
    // Main method to redeem a code
    function redeem() {
        // Print all codes in database
        $stmt = $this->db->prepare('SELECT id, code, unlock_code, uses_remaining FROM rw_promo_code');
        $stmt->execute();
        $stmt->bind_result($id, $code, $unlock_code, $uses_remaining);
        while ($stmt->fetch()) {
            echo "$code has $uses_remaining uses remaining!";
        }
        $stmt->close();
    }
}

这里添加了一个构造函数来连接给定用户名和密码的数据库,一个 析构函数来关闭数据库。现在你可以测试一下了:

Ray-Wenderlichs-Mac-mini-2:~ rwenderlich$ curl http://www.wildfables.com/promos/
test has 10000 uses remaining!

服务器策略:GET还是POST:

 好的,现在是时候来实现完成的功能了。但首先,让我们来谈谈web服务器的策略。

我们知道我们需要向服务器发送一些数据,包括app的ID,兑换吗,要兑换的设备ID。

如何发送呢?有两种方法:GET(普通方法)和POST(用于发送表单)

  • 如果你选择GET,那么参数是URL的一部分,就是把参数发到URL里,然后向服务器发送请求。
  • 如果你选择POST,参数被放到request body中

每个都能满足你的需求,但是当你要尝试做些什么的时候,比如兑换促销码,还是用POST比较好。这也是我将要做的。

这是什么意思呢?如果我们想在PHP中访问这些参数,我们可以通过内建的$_POST 数组:

$_POST["rw_app_id"]

我们将会用ASIHTTPRequest来连接服务器,用ASIFormDataRequest类发送一个POST请求:

ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:@"1" forKey:@"rw_app_id"];

更多GET和POST的信息,请看Wikipedia entry

更新:请看@smpdawg’s的精彩评论forum topic

 

web服务器策略:算法 

下一步,我们要看看将要使用的web服务器的算法:

  1. 确保需要的参数是通过POST发送的。
  2. 确保数据库里有兑换码。
  3. 确保兑换码剩余可使用次数。
  4. 确保设备没有使用过兑换码。
  5. 到这一步,就要完成了
  • 添加一个rw_promo_code_redeemed的入口来记录兑换
  • 将在rw_promo_code的uses_remaining减去一
  • 返回unlock_code给App。

web服务器的实现

 首先,添加一个辅助方法用于返回HTTP状态信息:

// Helper method to get a string description for an HTTP status code
// From http://www.gen-x-design.com/archives/create-a-rest-api-with-php/ 
function getStatusCodeMessage($status)
{
    // these could be stored in a .ini file and loaded
    // via parse_ini_file()... however, this will suffice
    // for an example
    $codes = Array(
        100 => 'Continue',
        101 => 'Switching Protocols',
        200 => 'OK',
        201 => 'Created',
        202 => 'Accepted',
        203 => 'Non-Authoritative Information',
        204 => 'No Content',
        205 => 'Reset Content',
        206 => 'Partial Content',
        300 => 'Multiple Choices',
        301 => 'Moved Permanently',
        302 => 'Found',
        303 => 'See Other',
        304 => 'Not Modified',
        305 => 'Use Proxy',
        306 => '(Unused)',
        307 => 'Temporary Redirect',
        400 => 'Bad Request',
        401 => 'Unauthorized',
        402 => 'Payment Required',
        403 => 'Forbidden',
        404 => 'Not Found',
        405 => 'Method Not Allowed',
        406 => 'Not Acceptable',
        407 => 'Proxy Authentication Required',
        408 => 'Request Timeout',
        409 => 'Conflict',
        410 => 'Gone',
        411 => 'Length Required',
        412 => 'Precondition Failed',
        413 => 'Request Entity Too Large',
        414 => 'Request-URI Too Long',
        415 => 'Unsupported Media Type',
        416 => 'Requested Range Not Satisfiable',
        417 => 'Expectation Failed',
        500 => 'Internal Server Error',
        501 => 'Not Implemented',
        502 => 'Bad Gateway',
        503 => 'Service Unavailable',
        504 => 'Gateway Timeout',
        505 => 'HTTP Version Not Supported'
    );
 
    return (isset($codes[$status])) ? $codes[$status] : '';
}
 
// Helper method to send a HTTP response code/message
function sendResponse($status = 200, $body = '', $content_type = 'text/html')
{
    $status_header = 'HTTP/1.1 ' . $status . ' ' . getStatusCodeMessage($status);
    header($status_header);
    header('Content-type: ' . $content_type);
    echo $body;
}

如果你不理解为什么我们不要这个,那是因为这是一个遵守HTTP协议的web服务器,当你发送一个相应你可以制定一个包含错误码和详细描述的头。有标准错误码可以用,这些方法不过是用起来更方便。

正如你看到的,我防线了一个可以把状态吗转换成HTML信息的教程

下一步,就是真正的实现了!

function redeem() {
 
    // Check for required parameters
    if (isset($_POST["rw_app_id"]) && isset($_POST["code"]) && isset($_POST["device_id"])) {
 
        // Put parameters into local variables
        $rw_app_id = $_POST["rw_app_id"];
        $code = $_POST["code"];
        $device_id = $_POST["device_id"];
 
        // Look up code in database
        $user_id = 0;
        $stmt = $this->db->prepare('SELECT id, unlock_code, uses_remaining FROM rw_promo_code WHERE rw_app_id=? AND code=?');
        $stmt->bind_param("is", $rw_app_id, $code);
        $stmt->execute();
        $stmt->bind_result($id, $unlock_code, $uses_remaining);
        while ($stmt->fetch()) {
            break;
        }
        $stmt->close();
 
        // Bail if code doesn't exist
        if ($id <= 0) {
            sendResponse(400, 'Invalid code');
            return false;
        }
 
        // Bail if code already used        
        if ($uses_remaining <= 0) {
            sendResponse(403, 'Code already used');
            return false;
        }    
 
        // Check to see if this device already redeemed    
        $stmt = $this->db->prepare('SELECT id FROM rw_promo_code_redeemed WHERE device_id=? AND rw_promo_code_id=?');
        $stmt->bind_param("si", $device_id, $id);
        $stmt->execute();
        $stmt->bind_result($redeemed_id);
        while ($stmt->fetch()) {
            break;
        }
        $stmt->close();
 
        // Bail if code already redeemed
        if ($redeemed_id > 0) {
            sendResponse(403, 'Code already used');
            return false;
        }
 
        // Add tracking of redemption
        $stmt = $this->db->prepare("INSERT INTO rw_promo_code_redeemed (rw_promo_code_id, device_id) VALUES (?, ?)");
        $stmt->bind_param("is", $id, $device_id);
        $stmt->execute();
        $stmt->close();
 
        // Decrement use of code
        $this->db->query("UPDATE rw_promo_code SET uses_remaining=uses_remaining-1 WHERE id=$id");
        $this->db->commit();
 
        // Return unlock code, encoded with JSON
        $result = array(
            "unlock_code" => $unlock_code,
        );
        sendResponse(200, json_encode($result));
        return true;
    }
    sendResponse(400, 'Invalid request');
    return false;
 
}

你应该能够读懂这段代码,否则的话查看以下这个教程Mysqli reference。这里有一些事情我需要指出:

  • isset是一个用于检测变量是否已经设置了的PHP函数。我们这里用它来确保所有需要的POST参数都发送了。
  • 注意我们没有自己把传进来的变量放到SQL语句中,而是使用bind_param方法。这是更安全的方法,否则你可能使自己易受SQL injection的攻击。
  • 注意 unlock_code 用JSON返回。我们当然可以直接用字符串返回因为我们只返回一个信息,但是用JSON便于以后扩展。

现在,你的web服务器就已经可以工作了。你可以用下面命令来测试:

curl -F "rw_app_id=1" -F "code=test" -F "device_id=test" http://www.wildfables.com/promos/
{"unlock_code":"com.razeware.wildfables.unlock.test"}

注意,如果你在我的服务器上测试,如果你得到“code already used”的错误,你应该更改你的device_id。

你可能希望进入你的数据库看看那里是否有一个rw_promo_code_redeemed的入口,uses_remaining是否减一等等。

下一步?

这里是源码source code

敬请期待PART2. 

转载于:https://www.cnblogs.com/shangdahao/archive/2013/06/01/3111755.html

eco.gateway.enablePcWebApiAppCode=true eco.gateway.pcWebApiAppCode=alipay.planet.xxxapp The configuration you provided appears to be related to enabling and setting up a specific application code for a gateway service, likely in a backend or microservices architecture. Here's a breakdown of what each line might mean: Configuration Breakdown: eco.gateway.enablePcWebApiAppCode=true Purpose: This setting likely enables the use of an application-specific API code for the "PC Web" gateway. Implication: When set to true, the gateway will allow or process requests associated with the pcWebApiAppCode. If set to false, this feature would be disabled. eco.gateway.pcWebApiAppCode=alipay.planet.xxxapp Purpose: This specifies the application code (alipay.planet.xxxapp) that the gateway should recognize when handling PC Web API requests. Implication: The gateway will use this code to identify, route, or authenticate requests related to the "PC Web" platform. The placeholder xxxapp suggests this is a placeholder and should be replaced with the actual application identifier. Possible Use Case: Gateway Service: This configuration seems to belong to a gateway service that handles API requests for different platforms (e.g., PC Web, Mobile). Application Code: The pcWebApiAppCode is likely used to distinguish between different applications or modules accessing the gateway. For example, an "Alipay" app might have a specific code to identify its requests. Enable/Disable: The enablePcWebApiAppCode flag allows toggling this feature, which could be useful for testing or security purposes. Common Questions: What happens if enablePcWebApiAppCode is set to false? The gateway might reject or ignore requests with the pcWebApiAppCode, depending on how the service is implemented. Is alipay.planet.xxxapp a valid application code? The xxxapp part seems like a placeholder. It should be replaced with a valid, unique identifier for the application (e.g., alipay.planet.myapp). Where is this configuration used? This is typically part of a backend configuration file (e.g., application.properties, application.yml, or environment variables). It would be read by the gateway service at runtime. What other related configurations might exist? Similar settings for other platforms (e.g., Mobile Web, iOS, Android) might exist, such as eco.gateway.enableMobileWebApiAppCode or eco.gateway.mobileWebApiAppCode. Security Considerations: Ensure that the pcWebApiAppCode is kept confidential if it is sensitive (e.g., used for authentication or authorization). Verify that enabling this feature does not inadvertently expose the gateway to unauthorized requests. If you need further clarification or help with implementing this configuration, feel free to provide more context! 第二行这个是什么 第二行配置: eco.gateway.pcWebApiAppCode=alipay.planet.xxxapp 解释: eco.gateway.pcWebApiAppCode 这是一个配置键(Key),用于定义某个特定的应用代码(Application Code)。 eco.gateway 可能是一个命名空间(Namespace),用于网关(Gateway)相关的配置。 pcWebApiAppCode 表示这个配置项是用于 "PC Web API" 的应用代码。 alipay.planet.xxxapp 这是配置的值(Value),表示一个具体的应用代码(App Code)。 在这个例子中,alipay.planet.xxxapp 可能是一个占位符(Placeholder),表示某个具体的应用标识。 实际使用时,通常需要替换为真实的应用程序标识符(例如 alipay.planet.myapplication)。 作用: 这个配置项可能是用于标识某个特定的应用程序或服务,当网关处理 "PC Web API" 请求时,会根据这个应用代码进行相应的处理(例如路由、鉴权等)。 示例场景: 假设这是一个支付网关服务,alipay.planet.xxxapp 可能是某个支付宝支付应用的应用代码。网关会根据这个代码识别请求的来源,并进行相应的处理。 如果你有更多上下文或具体问题,可以进一步说明! # ------------ PcWebApi ------------- eco.gateway.enablePcWebApiAppCode=true eco.gateway.pcWebApiAppCode=alipay.planet.${spring.application.name} 这段配置是关于一个名为 PcWebApi 的功能模块,具体说明如下: 配置项解析 1. eco.gateway.enablePcWebApiAppCode=true 含义:启用 PcWebApi 相关的应用代码(App Code)功能。 作用:当设置为 true 时,网关会开启对 PcWebApiAppCode 的处理逻辑(例如验证、路由等)。如果设置为 false,则禁用该功能。 2. eco.gateway.pcWebApiAppCode=alipay.planet.${spring.application.name} 含义: 这是一个动态配置项,${spring.application.name} 是 Spring Boot 的内置变量,表示当前应用的名称(在 application.properties 或环境变量中定义)。 配置值的最终结果是 alipay.planet.<当前应用名>。例如: 如果 spring.application.name=my-app,则 eco.gateway.pcWebApiAppCode 的值为 alipay.planet.my-app。 如果 spring.application.name=payment-service,则值为 alipay.planet.payment-service。 作用:为每个应用生成唯一的应用代码(App Code),便于网关根据代码识别请求来源。 可能的使用场景 应用标识: 网关通过 pcWebApiAppCode 来标识不同应用的请求,确保请求被正确路由或处理。 例如:不同业务模块(如支付、订单、会员)的应用会使用不同的 spring.application.name, @RequestMapping(value = "/test/CertificateListQueryReq", method = RequestMethod.POST)如果我想访问这个,我需要如何拼接url 要访问这个接口,你需要按照以下步骤拼接 URL: 1. 基础 URL 首先,你需要知道你的服务的基础 URL(通常是服务的域名或 IP 地址,例如 http://localhost:8080 或 https://api.example.com)。 2. 接口路径 根据你的代码,接口的路径是: /test/CertificateListQueryReq 3. 完整的 URL 将基础 URL 和接口路径拼接起来。例如: 如果是本地服务(端口 8080): http://localhost:8080/test/CertificateListQueryReq 如果是生产环境(假设域名是 https://api.example.com): https://api.example.com/test/CertificateListQueryReq 4. HTTP 方法 这个接口使用的是 POST 方法,所以你需要使用 POST 请求来访问它。 示例请求 使用 curl 发送请求: bash curl -X POST \ http://localhost:8080/test/CertificateListQueryReq \ -H "Content-Type: application/json" \ -d '{"key":"value"}' 使用 Postman: 选择 POST 方法。 输入 URL(例如 http://localhost:8080/test/CertificateListQueryReq)。 在 Body 部分输入 JSON 数据(如果接口需要参数)。 设置 Content-Type 为 application/json。 注意事项 如果你的服务部署在某个上下文路径(context path)下(例如 /myapp),需要在 URL 中加上。例如: http://localhost:8080/myapp/test/CertificateListQueryReq 确保接口的权限和认证配置正确,否则可能会返回 403 或 401 错误。 如果你的服务使用了 HTTPS,记得使用 https:// 而不是 http://。 如果你还有其他问题(例如如何测试、如何传递参数等),可以继续提问! quarzt如何从持久化的数据库中读取需要执行的任务吗,来个代码实例 在使用 Quartz 调度器时,可以通过持久化存储(如数据库)来管理任务的调度。以下是一个示例代码,展示如何从数据库中读取任务并启动 Quartz 调度器。 1. 配置 Quartz 数据源 首先,需要在 quartz.properties 文件中配置 Quartz 使用数据库存储: properties # quartz.properties org.quartz.scheduler.instanceName = MyScheduler org.quartz.scheduler.instanceId = AUTO org.quartz.threadPool.threadCount = 3 org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate org.quartz.jobStore.useProperties = false org.quartz.jobStore.dataSource = myDS org.quartz.jobStore.tablePrefix = QRTZ_ org.quartz.jobStore.isClustered = true org.quartz.dataSource.myDS.driver = com.mysql.cj.jdbc.Driver org.quartz.dataSource.myDS.URL = jdbc:mysql://localhost:3306/quartz org.quartz.dataSource.myDS.user = root org.quartz.dataSource.myDS.password = password org.quartz.dataSource.myDS.maxConnections = 5 2. 创建 Quartz 数据库表 Quartz 提供了一些 SQL 脚本来创建所需的表(在 quartz 数据库中运行这些脚本)。以下是 MySQL 的例子: sql -- 创建 Quartz 数据表的 SQL 脚本 CREATE TABLE QRTZ_JOB_DETAILS ( SCHED_NAME VARCHAR(120) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, JOB_CLASS_NAME VARCHAR(250) NOT NULL, IS_DURABLE VARCHAR(1) NOT NULL, IS_NONCONCURRENT VARCHAR(1) NOT NULL, IS_UPDATE_DATA VARCHAR(1) NOT NULL, REQUESTS_RECOVERY VARCHAR(1) NOT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP) ); CREATE TABLE QRTZ_TRIGGERS ( SCHED_NAME VARCHAR(120) NOT NULL, TRIGGER_NAME VARCHAR(200) NOT NULL, TRIGGER_GROUP VARCHAR(200) NOT NULL, JOB_NAME VARCHAR(200) NOT NULL, JOB_GROUP VARCHAR(200) NOT NULL, DESCRIPTION VARCHAR(250) NULL, NEXT_FIRE_TIME BIGINT NULL, PREV_FIRE_TIME BIGINT NULL, PRIORITY INTEGER NULL, TRIGGER_STATE VARCHAR(16) NOT NULL, TRIGGER_TYPE VARCHAR(8) NOT NULL, START_TIME BIGINT NOT NULL, END_TIME BIGINT NULL, CALENDAR_NAME VARCHAR(200) NULL, MISFIRE_INSTR SMALLINT NULL, JOB_DATA BLOB NULL, PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP), FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP) REFERENCES QRTZ_JOB_DETAILS(SCHED_NAME, JOB_NAME, JOB_GROUP) ); -- 其他表可以参考 Quartz 官方文档 3. Java 代码示例 以下是一个完整的示例代码,展示如何从数据库中读取任务并启动 Quartz 调度器: 3.1 创建一个简单的任务类 java import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; public class HelloJob implements Job { @Override public void execute(JobExecutionContext context) throws JobExecutionException { System.out.println("Hello, Quartz! 时间: " + new java.util.Date()); } } 3.2 创建一个 Quartz 调度器并启动任务 java import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; public class QuartzExample { public static void main(String[] args) { try { // 1. 创建 Scheduler Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); // 2. 启动 Scheduler scheduler.start(); // 3. 定义一个 JobDetail JobDetail job = JobBuilder.newJob(HelloJob.class) .withIdentity("job1", "group1") .build(); // 4. 定义一个 Trigger Trigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(5) .repeatForever()) .build(); // 5. 将 Job 和 Trigger 注册到 Scheduler scheduler.scheduleJob(job, trigger); // 6. Scheduler 会根据配置的表存储任务 System.out.println("Scheduler 已启动..."); } catch (SchedulerException e) { e.printStackTrace(); } } } 3.3 从数据库中读取任务 当 Quartz 调度器启动时,它会自动从 QRTZ_JOB_DETAILS 和 QRTZ_TRIGGERS 表中读取任务和触发器的定义,并开始执行。如果你在数据库中手动插入了任务,Quartz 会自动加载它们。 4. 数据库中的任务示例 假设你在数据库中插入了以下任务: sql INSERT INTO QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP, JOB_CLASS_NAME, IS_DURABLE, IS_NONCONCURRENT, IS_UPDATE_DATA, REQUESTS_RECOVERY, JOB_DATA) VALUES ('MyScheduler', 'job1', 'group1', 'com.example.HelloJob', 'Y', 'N', 'N', 'N', NULL); INSERT INTO QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, JOB_NAME, JOB_GROUP, START_TIME, NEXT_FIRE_TIME, TRIGGER_STATE, TRIGGER_TYPE, REPEAT_INTERVAL, REPEAT_COUNT) VALUES ('MyScheduler', 'trigger1', 'group1', 'job1', 'group1', 1697040000000, 1697040005000, 'WAITING', 'SIMPLE', 5000, -1); 当 Quartz 调度器启动时,它会自动加载这些任务并开始执行。 总结 配置 Quartz 使用数据库存储(通过 quartz.properties)。 创建 Quartz 数据表。 使用 Java 代码定义任务并启动调度器。 Quartz 会自动从数据库中读取任务和触发器的定义。 希望这个示例对你有帮助!如果有任何问题,请随时提问。 我的问题是,如果我将一些任务信息保存到数据库,quartz如何从数据库中读取这些配置,然后去依次执行这些任务
09-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值