文档章节

那些年使用过的JFinal

大家都是低调来的
 大家都是低调来的
发布于 05/29 10:56
字数 3723
阅读 3
收藏 0

说明 : 关于微信支付与隐藏或显示右上角按钮,都需要引用微信的JS文档

因为本人的项目业务功能有限,所以没有涉及大量的代码操作,如果想要对于JFinal有更深入的了解,可以参看官方文档或在JFinal-weixin的文档。

需求

利用微信公众号进行开发一个公众号平台,包括微信支付、微信认证等功能, 同时包括一个后台管理。

使用的原因

因为本人从业时间较短,不太清楚微信内部如何使用,所以就从码云上找了关于微信公众号或小程序的开发体系。结果找到了JFinal-weixin,发现这个框架比较好用, 所以这里采用了这个框架进行搭建项目 。

开发工具

  • IDEA
  • MySQL
  • Tomcat
  • Maven

主要使用IDEA 作为 IDE 进行开发, 后台数据库使用MySQL ,部署在 Tomcat 上进行运行, 使用Maven作为项目构建体系。

项目构建

依赖信息

使用Maven 构建项目, 导入Maven依赖。依赖如下:

<dependencies>
	<!-- 单元测试 -->
     <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
	<!-- Jfinal-weixin依赖 -->
    <dependency>
      <groupId>com.jfinal</groupId>
      <artifactId>jfinal-weixin</artifactId>
      <version>2.3</version>
    </dependency>
	<!-- MySQL连接数据库 -->
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.15</version>
    </dependency>
	<!-- 使用Druid 连接数据库 ,主要是连接池 -->
    <!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>
	<!-- 使用JSP作为页面展示效果 -->
    <!-- https://mvnrepository.com/artifact/javax.servlet.jsp/javax.servlet.jsp-api -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>javax.servlet.jsp-api</artifactId>
      <version>2.3.3</version>
      <scope>provided</scope>
    </dependency>
	<!-- 使用JSTL依赖 -->
    <!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
      <version>1.2</version>
    </dependency>
	<!-- 尽管在JFinal-weixin中使用了JFinal,但是如果不引用此依赖的话,会有部分类没有支持 -->
    <!-- https://mvnrepository.com/artifact/com.jfinal/jfinal -->
    <dependency>
      <groupId>com.jfinal</groupId>
      <artifactId>jfinal</artifactId>
      <version>4.0</version>
    </dependency>
	<!-- 因为要记录每个用户访问那个网站,用了什么参数,所以这里加入了Servlet的依赖 -->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>4.0.1</version>
      <scope>provided</scope>
    </dependency>

  </dependencies>

创建WeixinConfig文件

WeixinConfig 是针对于整个项目的配置,包括使用什么模版信息,配置路由(JFinal中叫路由信息,其实本质是Servlet中的映射)等信息。具体如下:

import cn.shocksoft.model._MappingKit;
import com.jfinal.config.*;
import com.jfinal.json.FastJsonFactory;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.druid.DruidPlugin;
import com.jfinal.render.ViewType;
import com.jfinal.template.Engine;
import com.jfinal.weixin.sdk.api.ApiConfigKit;
// 这里根据官方的描述,一定是要继承JFinalConfig 这个类的,否则配置信息不会生效
public class WeixinConfig extends JFinalConfig {
    /**
     * 此方法用来配置JFinal常量值,如开发模式常量devMode的配置
     * @param constants
     */
    @Override
    public void configConstant(Constants constants) {
        // 加载配置文件(这个配置文件是放在resources 下的)
        PropKit.use("a_little_config.txt");
        // 读取配置文件,判断是否为开发模式,默认 false
        constants.setDevMode(PropKit.getBoolean("devMode" ));
        // ApiConfigKit 设为开发模式可以在开发阶段输出请求交互的 xml 与 json 数据
        ApiConfigKit.setDevMode(constants.getDevMode());
        // 默认使用的jackson,下面示例是切换到fastJson
        constants.setJsonFactory( new FastJsonFactory() );
        // 默认使用的是 JFinal 内部的 Template , 这里设置成 JSP 模板 (支持Velocity、FreeMarker等模板)
        constants.setViewType(ViewType.JSP);
    }

    /**
     * 配置路由信息 : 也就是 配置对应的 Controller
     * @param routes
     */
    @Override
    public void configRoute(Routes routes) {
		// 这里配置Controller的信息,到了具体的配置的时候,详细描述。
    }

    /**
     * 配置插件信息
     * @param plugins
     */
    @Override
    public void configPlugin(Plugins plugins) {
        // 配置 druid 数据库连接池插件
        DruidPlugin druidPlugin = new DruidPlugin(PropKit.get("jdbcUrl"), PropKit.get("user"), PropKit.get("password").trim());
        plugins.add(druidPlugin);

        // 配置ActiveRecord插件
        ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
        // 所有映射在 MappingKit 中自动化搞定 ( 也就是将实体类 添加到 对应的插件中)
		// arp.addMapping("user", User.class); // 其实应该是这样的,但是封装了一个类,从而变成下面的样子
        _MappingKit.mapping( arp );
        plugins.add(arp);
    }

    @Override
    public void configEngine(Engine engine) {

    }


    /**
     * 此方法用来配置JFinal的全局拦截器,全局拦截器将拦截所有 action 请求,
     * 除非使用@Clear在Controller中清除
     * @param interceptors
     */
    @Override
    public void configInterceptor(Interceptors interceptors) {

    }

    @Override
    public void configHandler(Handlers handlers) {

    }
}

注意 : 这个配置中使用了配置文件,这个配置文件是一个txt格式的文本文件,位于resources目录下。这个配置文件的内容是键值对的形式,与properties文件的形式是一样的, 可以将任何需要配置的东西放入到这个配置文件中。

配置Web.xml文件

主要是配置过滤器 ,如下所示 :

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" 
         id="WebApp_ID" version="4.0">
  <filter>
    <filter-name>jfinal</filter-name>
    <filter-class>com.jfinal.core.JFinalFilter</filter-class>
    <init-param>
      <param-name>configClass</param-name>
      <param-value>cn.shocksoft.config.WeiXinConfig</param-value>
    </init-param>
  </filter>

  <filter-mapping>
    <filter-name>jfinal</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
</web-app>

这里使用了4.0的版本,如果想要部署在tomcat上的话,那么就需要使用tomcat9的版本支持。

a_little_config.txt 配置文件

这个配置文件与properties文件的形式一致, 具体如下所示 :

# 数据库相关设置
jdbcUrl=jdbc:mysql://localhost:3306/xxx?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2B8
user=xxx
password=xxx

# 开发模式
devMode = true

这里放入了对应的连接数据库的配置信息,那么在 configPlugin 中,使用Druid的时候就可以使用这里的信息,从而连接数据库。

如果想要在代码中使用这里的配置文件,那么可以参看以下方式进行处理:

String jdbcUrl = PropKit.get("jdbcUrl") ;

这里使用了内部的工具类PropKit,就可以进行使用了 。

根据数据库生成对应的实体类

当数据库中的表过多的时候,可以使用程序进行生成数据库表对应的实体。 操作如下 :

import com.jfinal.kit.PathKit;
import com.jfinal.kit.PropKit;
import com.jfinal.plugin.activerecord.generator.Generator;
import com.jfinal.plugin.druid.DruidPlugin;
import javax.sql.DataSource;
public class TestGenerator {
    public static void main(String[] args) {
        // base model 所使用的包名
        String baseModelPackageName = "cn.shocksoft.model.base";
        // base model 文件保存路径
        String baseModelOutputDir = PathKit.getWebRootPath() + "/src/test/cn/shocksoft/test/base";

        // model 所使用的包名 (MappingKit 默认使用的包名)
        String modelPackageName = "cn.shocksoft.model";
        // model 文件保存路径 (MappingKit 与 DataDictionary 文件默认保存路径)
        String modelOutputDir = baseModelOutputDir + "/..";

        // 创建生成器
        Generator generator = new Generator(getDataSource(), baseModelPackageName, baseModelOutputDir, modelPackageName, modelOutputDir);
        // 设置是否生成链式 setter 方法
        generator.setGenerateChainSetter(false);
        // 添加不需要生成的表名
//        generator.addExcludedTable("adv");
        // 设置是否在 Model 中生成 dao 对象
        generator.setGenerateDaoInModel(true);
        // 设置是否生成链式 setter 方法
        generator.setGenerateChainSetter(true);
        // 设置是否生成字典文件
        generator.setGenerateDataDictionary(false);
        // 设置需要被移除的表名前缀用于生成modelName。例如表名 "osc_user",移除前缀 "osc_"后生成的model名为 "User"而非 OscUser
//        generator.setRemovedTableNamePrefixes("t_");
        // 生成
        generator.generate();
    }
    public static DataSource getDataSource() {
        PropKit.use("a_little_config.txt");
        DruidPlugin druidPlugin = new DruidPlugin(PropKit.get("jdbcUrl"), PropKit.get("user"), PropKit.get("password").trim());
        druidPlugin.start();
        return druidPlugin.getDataSource();
    }
}

这里只需要修改对应的包名即可, 因为这里会生成当前数据库下的所有表,对应的实体类。而修改对应的包名表示将对应的实体类放入到指定的包。

配置Controller的映射关系

自己根据业务需求书写相应的Controller , 但是写完一个Controller 之后, 在JFinal体系中并不能够识别。因此这里需要将对应的Controller 进行 配置的管理。示例如下 :

import com.jfinal.weixin.sdk.jfinal.MsgControllerAdapter;
import com.jfinal.weixin.sdk.msg.in.InTextMsg;
import com.jfinal.weixin.sdk.msg.in.event.InFollowEvent;
import com.jfinal.weixin.sdk.msg.in.event.InMenuEvent;
/**
 * MsgController 专门负责 对 微信用户发来的消息(包括点击菜单等)进行处理。
 * 此类需要实现 MsgControllerAdapter 类,才可以针对于 用户"指令"进行各种操作
 */
public class MsgController extends MsgControllerAdapter {
    /**
     * 关注/取关事件
     * @param inFollowEvent
     */
    @Override
    protected void processInFollowEvent(InFollowEvent inFollowEvent) {
     // 可以书写对应的逻辑
    }

    /**
     * 接收文本消息事件 : 可以针对于 文本消息进行处理
     * 这里将所有的消息 都认为是 文本消息。
     * @param inTextMsg
     */
    @Override
    protected void processInTextMsg(InTextMsg inTextMsg) {
		// 可以写具体的事件分析
    }

    /**
     * 自定义菜单事件 : 主要针对于菜单事件进行处理
     * @param inMenuEvent
     */
    @Override
    protected void processInMenuEvent(InMenuEvent inMenuEvent) {
		// 事件处理
    }
}

当然, 写完Controller之后,依旧表示不可以使用,如果想要直接访问的话,其实是没有办法访问的。需要在WeixinConfig.java中进行做配置处理。如下所示 :

 @Override
    public void configRoute(Routes routes) {
        routes.add("/msg" , MsgController.class ) ; 
    }

这里仅仅加入了 对应的一行处理,那么此时就可以将MsgController进行使用了。

使用Controller

如果要使用Controller的话, 比如说写自己的业务的时候,那么就需要定义自己的Controller,比如说,这里有一个关于用户的处理,那么就可以自己定义自己的Controller, 但是关于用户的操作,通常有以下几种做法:

1、直接使用微信对应的信息即可。
2、在系统内部使用自己的一套用户体系。

但是无论使用哪种方式,都有可能需要获取到用户对应的信息,那么如果是第三方登录的话,那么就需要涉及第三方安全认证和授权(OAuth2)。具体示例如下:

  • UserController
import cn.shocksoft.utils.Constants;
import com.jfinal.core.Controller;
import com.jfinal.weixin.sdk.api.ApiResult;
import com.jfinal.weixin.sdk.api.SnsAccessToken;
import com.jfinal.weixin.sdk.api.SnsAccessTokenApi;
import com.jfinal.weixin.sdk.api.SnsApi;

/**
 * 在 JFinal 的体系中, 不存在方法重载一说。
 * 这里将每一个方法都当做一个Action
 * 那么在WeixinConfig中进行配置的路径就是在指定了这个路径。
 * 可以对比SpringMVC中的体系 : 在 SpringMVC的时候可以在Controller类上加上一个RequestMapping的映射。
 * 同时可以在 Controller对应的方法加上 RequestMapping注解 。
 * 那么在JFinal体系中: 类上的RequestMapping写在了 WeixinConfig中, 方法上的RequestMapping则默认对应其方法名。
 */
public class UserController extends Controller {
    /**
     * 获取用户信息,在这里进行了授权操作
     */
   public void getUserInfoOauth(){
        //回调地址(必须在公网进行访问)
       String backUrl = Constants.HOST +"/user/getUserinfos";
       // 进行授权操作,并指定回调地址
       String weixinUrl = SnsAccessTokenApi.getAuthorizeURL(Constants.APP_ID, backUrl, false);
       redirect(weixinUrl);
   }

    /**
     * 这里指定了回调地址,同时可以进行任务操作
     */
   public void getUserinfos(){
       String code = get("code");  // 获取回调的 code 码
       // 将code 码转换成 具体的 accessToken
       SnsAccessToken accessToken = SnsAccessTokenApi.getSnsAccessToken(Constants.APP_ID, Constants.APP_Secret, code);
       // 根据 accessToken 与 openId 获取对应的用户信息
       ApiResult apiResult = SnsApi.getUserInfo(accessToken.getAccessToken(), accessToken.getOpenid());
       // 以 JSON的方式将获取到的 信息 输出
       System.out.println( apiResult.getJson() );
       // 在这里可以进行相应的业务处理
       // 转到对应的页面
       render("/userInfos.jsp");
   }
}

当然, 写了Controller之后,也需要在WeixinConfig.java中进行配置。示例如下 :

    /**
     * 配置路由信息 : 也就是 配置对应的 Controller
     * @param routes
     */
    @Override
    public void configRoute(Routes routes) {
        routes.add("/msg" , MsgController.class ) ;
        // 可以看到,这里添加了一行,同时这里传入了三个参数。
        routes.add( "/user", UserController.class , "/pages/user") ;
    }

这里的三个参数表示的意思如下 :

  • /user 类似于SpringMVC上Controller类上的 RequestMapping注解
  • UserController.class 表示第一个参数的路径指向哪一个Controller
  • /pages/user 表示 将要返回的页面是 存放于哪个路径之下的。

如果不清楚如何配置的话,可以查看JFinal文档进行查看。

微信支付

关于微信支付,依旧是需要对接微信的支付接口。但是在JFinal-weixin中,已经有对应的demo进行处理。可以参看直接使用。示例如下 : 页面输入金额,然后点击确定按钮:

<form id="form"  action="/user/payInfo" method="post">
<input placeholder="请输入充值金额" type="number" id="money" name="money"
onkeyup="this.value=this.value.replace(/\D/g,'')" onafterpaste="this.value=this.value.replace(/\D/g,'')">
<input type="submit" value="提交" >
</form>

当提交表单之后,需要有对应的Controller对其进行处理,因为路径是/user/payInfo , 那么在这里就需要明确这个方法放在哪里。 处理支付:

 /**
     * 写自己的支付逻辑
     */
    public void payInfo() {
        String notify_url = Constants.HOST + "/user/pay_notify";
        // openId,采用 网页授权获取 access_token API:SnsAccessTokenApi获取
        String openId = getPara("openid");
        // 因为金钱的单位是分,所以这里需要*100 再进行操作
        String money = new BigDecimal(getPara("money")).multiply(new BigDecimal(100)).toString();
        // 统一下单文档地址:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1
        Map<String, String> params = new HashMap<>();
        params.put("appid", Constants.APP_ID);
        params.put("mch_id", ""); // 这里需要根据自己申请的公众号的东西,从而进行处理
        params.put("body", "购买积分");
        params.put("out_trade_no", System.currentTimeMillis() + ""); // 用户商户自己的订单号
        params.put("total_fee", money);
        String ip = IpKit.getRealIp(getRequest());
        // 处理多IP的情况, 为了处理双卡双待的情况
        if (StrKit.isBlank(ip)) {
            ip = "127.0.0.1";
        }
        if (ip.contains(",")) {
            ip = ip.split(",")[0];
        }
        params.put("spbill_create_ip", ip);
        params.put("trade_type", PaymentApi.TradeType.JSAPI.name());
        params.put("nonce_str", System.currentTimeMillis() / 1000 + ""); // 随机字符串
        params.put("notify_url", notify_url);
        params.put("openid", openId);
        // 这里填写的是paternerKey , 这个与上方的mch_id 是一起申请的
        String sign = PaymentKit.createSign(params, "");
        params.put("sign", sign);
        String xmlResult = PaymentApi.pushOrder(params);
        Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
        String return_code = result.get("return_code");
        String return_msg = result.get("return_msg");
        if (!StrKit.isBlank(return_code) && "SUCCESS".equals(return_code)) {
            String result_code = (String) result.get("result_code");
            if (!StrKit.isBlank(result_code) && "SUCCESS".equals(result_code)) {
                String prepay_id = (String) result.get("prepay_id");
                Map<String, String> packageParams = new HashMap();
                packageParams.put("appId", Constants.APP_ID); // 这里应该是自己的AppId
                packageParams.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000L));
                packageParams.put("nonceStr", String.valueOf(System.currentTimeMillis()));
                packageParams.put("package", "prepay_id=" + prepay_id);
                packageParams.put("signType", "MD5");
                String packageSign = PaymentKit.createSign(packageParams, "");
                packageParams.put("paySign", packageSign);
                String jsonStr = JsonUtils.toJson(packageParams);
                setAttr("json", jsonStr);
                // 查看一下字符串信息
                System.out.println(jsonStr);
                /**
                 * 生成订单 等逻辑信息
                 **/
                // 这里需要嫁接一层, 从而让前端页面发送 支付请求 。
                set("money", Integer.valueOf(money) / 100);//100
                set("openid", openId);
                render("/configRecharge.jsp");
            } else {
                renderText(return_msg);
            }
        } else {
            renderText(return_msg);
        }
    }

    /**
     * 支付结果通知
     */
    public void pay_notify() {
        // 支付结果通用通知文档: https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_7
        String xmlMsg = HttpKit.readData(getRequest());
        System.out.println("支付通知=" + xmlMsg);
        Map<String, String> params = PaymentKit.xmlToMap(xmlMsg);
        String result_code = params.get("result_code");
        // 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态
        // 避免已经成功、关闭、退款的订单被再次更新
        if (PaymentKit.verifyNotify(params, "")) { // 这里被双引号引用起来的是: paternerKey
            if (("SUCCESS").equals(result_code)) {
                //更新订单信息 等操作
                System.out.println("更新订单信息");
                Map<String, String> xml = new HashMap<>();
                xml.put("return_code", "SUCCESS");
                xml.put("return_msg", "OK");
                renderText(PaymentKit.toXml(xml));
                return;
            }
        }
        renderText("");
    }

configRecharge.jsp 这个页面主要是调用js进行支付操作,如果成功了应该如何处理,如果失败了改如何处理,全看这个页面上的操作。

<a id="showTooltips"   onclick="recharge()">确定</a>

js如下 :

<script type="text/javascript">
    function recharge() {
        onBridgeReady() ;
    }
    function onBridgeReady(){
        WeixinJSBridge.invoke(
            'getBrandWCPayRequest',
            ${json},
            function(res){
                // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回    ok,但并不保证它绝对可靠。
                if(res.err_msg == "get_brand_wcpay_request:ok" ) {
                   // 支付成功
                }else{
                   // 支付失败 
                }
            }
        );
    }
    // if (typeof WeixinJSBridge == "undefined"){
    //     if( document.addEventListener ){
    //         document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
    //     }else if (document.attachEvent){
    //         document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
    //         document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
    //     }
    // }else{
    //     onBridgeReady();
    // }
</script>

隐藏/显示右上角按钮

有时候需要显示对应的右上角的按钮,那么就不用操作了;如果要不显示对应的按钮,可以使用如下方法进行操作。

  <script type="text/javascript" src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
  <script type="text/javascript" src="../../pages/js_sdk.jsp?test=false"></script>
    <script type="text/javascript">
        document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() {
            // 通过下面这个API隐藏右上角按钮
            WeixinJSBridge.call('hideOptionMenu');
        });
    </script>

注意:只能隐藏“传播类”和“保护类”按钮。但是微信推荐的方法如下 :

wx.hideMenuItems({
menuList: [] // 要隐藏的菜单项,只能隐藏“传播类”和“保护类”按钮
});

发现使用这种方式并不能实现其效果。

© 著作权归作者所有

大家都是低调来的
粉丝 7
博文 34
码字总数 91792
作品 0
西安
程序员
私信 提问
JFinal 3.2 发布,星星之火已成燎原之势

JFinal第一版于2011年3月诞生于公司内部,应用于公司项目后大受欢迎,一年后于2012年3月18日选择在OSChina社区开源。 历经6年工匠精神的打磨,现已进化成生机勃勃的生态系统,星星之火已成燎...

JFinal
2017/08/08
13.3K
161
JFinal 2.1 最终版发布,用JFinal开发,就这么定了!

由于 jfinal 2.1 在不完全统计的情况下有超过60项的升级与改进,所以自发布这几天以来,立即收到了大量的使用反馈,为了使开发者尽可能快地用上反馈后的新版本,本次jfinal 2.1延迟了推送到m...

JFinal
2016/01/11
6.8K
62
【开源访谈】玛雅牛谈 JFinal 与开源技术

【嘉宾近照】 李飞,ID:@玛雅牛 ,Git 主页:http://git.oschina.net/myaniu 【正文】 1. 请简单地介绍一下你自己(技术背景、学习经历、工作经历)。 04年毕业于西安交通大学计算机系,曾就...

孔小菜
2015/09/02
9.3K
69
【开源访谈】 JFinal作者 詹波 访谈实录

关于开源访谈 开源访谈是开源中国推出的一系列针对国内优秀开源软件作者的访谈,以文字的方式记录并传播。我们希望开源访谈能全面的展现国内开源软件、开源软件作者的现状,着实推动国内开源...

丫头潘潘
2013/07/11
12.3K
68
SoJpt Boot 2.0-3.8 发布,Spring Boot 使用 Jfinal 特性极速开发

SoJpt Boot,在Spring Boot框架下使用Jfinal特性极速开发。 可以在Spring Boot中向使用Jfinal一样使用Enjoy, Aop, controller的一系列方法(如: getFile(), renderFile....),以及ActiveRecor...

Sohnny
04/09
634
0

没有更多内容

加载失败,请刷新页面

加载更多

JDBC 连接数据库的流程?

加载 JDBC 驱动程序 利用 url,user,password 创建数据库连接 创建 statement 对象执行 sql 语句 返回查询结果 resultset 关闭连接,释放 JDBC 资源。关闭的顺序与开启的顺序相反:resultset...

happywe
20分钟前
2
0
如何让视频加速播放

当下的视频工作者越来越多,很多人在闲暇时间也会制作一些好玩有趣的短视频上传一些社交媒体。下面分享一个让视频加速播放的软件以及制作方法,学会这个方法后,可以制作一些有趣的加速视频啦...

白米稀饭2019
27分钟前
2
0
生成带图片的二维码

1,pom.xml 中 <!-- ZXing --><dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.3.3</version></dependency><dependency......

简小姐
36分钟前
3
0
可能是国内第一篇全面解读 Java 现状及趋势的文章

作者 | 张晓楠 Dragonwell JDK 最新版本 8.1.1-GA 发布,包括全新特性和更新! **导读:**InfoQ 发布《2019 中国 Java 发展趋势报告》,反映 Java 在中国发展的独特性,同时也希望大家对 Ja...

阿里巴巴云原生
50分钟前
3
0
Mybatis 配置详解

完整配置 mybatis-config.xml <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.......

xiaolyuh
51分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部