SpringBoot整合JFinal的ActiveRecord的ORM+实现SQL与代码分离

QQ:61720061,邮箱:61720061@qq.com

项目说明:

前期自己做小项目用的是国产架构JFinal,整体使用感觉轻便、灵活、极简,特别是对数据库的操作更是节省了大量的精力。工作上大家都在用SpringBoot,数据库ORM用的则是MyBatisPlus,整体使用感觉在数据库操作方面极为不便,特别是xml文件配置成为了项目的难点之一。鉴于这样的情况,我就想为何不能把JFinal在数据ORM方面的能力搬到SpringBoot上(其实JFinal在Controller、Router方面也很有优势),即不用改变架构,也可以以极简的思路完成数据的操作。

这个整合项目在这样的情景下产生:

1、目标

(1)在SpringBoot项目中集成JFinal的ORM功能(实现)

(2)完成项目数据库的连接、代码书写,包括Controller、Service、Dao、View层(实现)

(3)完成SQL与代码分离编写和运维(实现)

2、准备

(1)SpringBoot项目,环境及jar版本号,JDK14.8+SpringBoot2.6.13+Mysql5.1.49

(2)JFinal版本5.2.2

(3)Mysql服务器版本5.5.6

3、测试准备

(1)新建数据库leave和表user,字段user_id、user_name、user_tel,其他字段自行设计

4、整体实施情况如下

(1)项目结构

src/main/java

├── com.example.demo

│   ├── config

│   │   ├── _Generator.java

│   │   └── ActiveRecordConfig.java

│   ├── controller

│   │   └── UserController.java

│   ├── model

│   │   ├── base

│   │   │   └── BaseUser.java

│   │   ├── _MappingKit.java

│   │   └── User.java

│   ├── service

│   │   └── UserService.java

│   └── DemoApplication.java

src/main/resources

├── application.properties

├──sql

│   ├── user.sql

├── templates

│   ├── userList.html

│   └── userView.html

说明:这里的base子包与BaseUser.java可生成也可不生成,区别在于不生成BaseUser.java时,User.jave继承Mode<User>,生成时,User.java继承BaseUser<User>,而这两者区别在于后期前端取数据时的方法;当生成时,前端可以用user.userId取值,如果不生成时,前端只能采用user.get(user_id)来取值。_Generator.java用来自动批量生成User.java、BaseUser.java和_MappingKit.java文件,生成新的实体类前应先删除已有类或删除需要重新生成的类。如果项目没有使用BaseUser.java的生成,那么数据表字段变化则不需要重新生成User.java,具有自适应性。

(2)创建项目

添加项目Maven依赖(pom.xml),除SpringBoot、lombok、devtools外,还应当添加thymeleaf、jfinal、mysql-connector-java、druid,另外我还引入了undertow,具体如下,

【xml】

<dependencies>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

            <exclusions>

                <exclusion>

                    <groupId>org.springframework.boot</groupId>

                    <artifactId>spring-boot-starter-tomcat</artifactId>

                </exclusion>

            </exclusions>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-undertow</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-devtools</artifactId>

            <scope>runtime</scope>

            <optional>true</optional>

        </dependency>

        <dependency>

            <groupId>org.projectlombok</groupId>

            <artifactId>lombok</artifactId>

            <optional>true</optional>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-test</artifactId>

            <scope>test</scope>

        </dependency>

        <!-- JFinal  -->

        <dependency>

            <groupId>com.jfinal</groupId>

            <artifactId>jfinal</artifactId>

            <version>5.2.2</version>

        </dependency>

        <!-- MySQL 驱动 -->

        <dependency>

            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <version>5.1.49</version>

        </dependency>

        <!-- Druid 连接池 -->

        <dependency>

            <groupId>com.alibaba</groupId>

            <artifactId>druid</artifactId>

            <version>1.2.8</version>

        </dependency>

        <!-- Thymeleaf 模板引擎 -->

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-thymeleaf</artifactId>

        </dependency>

</dependencies>

------

(3)完成数据源配置 (application.properties)

【properties】

server.port=8080

server.servlet.context-path=/demo

spring.datasource.url=jdbc:mysql://localhost:3306/leave?useSSL=false&serverTimezone=UTC

spring.datasource.username=root

spring.datasource.password=123456

spring.datasource.driver-class-name=com.mysql.jdbc.Driver

spring.datasource.type=com.alibaba.druid.pool.DruidDataSource

#thymeleaf

spring.thymeleaf.mode=HTML

spring.thymeleaf.encoding=UTF-8

spring.thymeleaf.content-type=text/html

spring.thymeleaf.cache=false

spring.thymeleaf.prefix=classpath:/templates/

spring.thymeleaf.suffix=.html

------

(4)集成Jfinal

完成JFinal的ActiveRecord与SpringBoot的集成配置,配置文件命名为ActiveRecordConfig.java,代码如下:

【java】

import com.alibaba.druid.filter.stat.StatFilter;

import com.alibaba.druid.wall.WallFilter;

import com.example.demo.model._MappingKit;

import com.jfinal.plugin.activerecord.ActiveRecordPlugin;

import com.jfinal.plugin.druid.DruidPlugin;

import com.jfinal.template.Engine;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class ActiveRecordConfig {

    @Value("${spring.datasource.url}")

    private String url;

    @Value("${spring.datasource.username}")

    private String username;

    @Value("${spring.datasource.password}")

private String password;

// 扫描 SQL 文件的目录路径

String directoryPath = System.getProperty("user.dir")+ "/src/main/resources/sql";

    @Bean

    public ActiveRecordPlugin initActiveRecordPlugin() {

        DruidPlugin dp = new DruidPlugin(url, username, password);

        WallFilter wallFilter = new WallFilter();// 加强数据库安全

        wallFilter.setDbType("mysql");

        dp.addFilter(wallFilter);

        dp.addFilter(new StatFilter()); // 添加StatFilter才会有统计数据

        dp.start();

        ActiveRecordPlugin arp = new ActiveRecordPlugin(dp);

        arp.setShowSql(true);  //设置sql显示与调试模式打开

        arp.setDevMode(true);

        _MappingKit.mapping(arp);

// 扫描 SQL 文件

 String[] paths=directoryPath.split("/");

String sqlFolder=paths[paths.length-1];

         List<String> sqlFiles = SqlFileScanner.scanSqlFiles(directoryPath);

         for (String sqlFile : sqlFiles) {

            arp.addSqlTemplate(sqlFolder+"/"+sqlFile);

         }

        arp.start();   //与jfinal web环境唯一的不同是要手动调用一次相关插件的start()方法

        return arp;

    }

}

【SqlFileScanner.java】

//对sql文件夹进行扫描

public class SqlFileScanner {

    public static List<String> scanSqlFiles(String directoryPath) {

        List<String> sqlFiles = new ArrayList<>();

        File directory = new File(directoryPath);

        if (directory.exists() && directory.isDirectory()) {

            File[] files = directory.listFiles();

            if (files != null) {

                for (File file : files) {

                    if (file.isFile() && file.getName().endsWith(".sql")) {

                        sqlFiles.add(file.getName());

                    }

                }

            }

        }

        return sqlFiles;

    }

}

(5)创建实体类定义 (User.java)

【java】

package com.example.demo.model;

import com.jfinal.plugin.activerecord.Model;

//不生成BaseUser.java

public class User extends Model<User> {

public static final User dao = new User().dao();

}

//采用生成BaseUser.java

import com.jfinal.plugin.activerecord.Model;

Import com.example.demo.model.base.BaseUser;

public class User extends BaseUser<User> {

}

【BaseUser.java】

import com.jfinal.plugin.activerecord.Model;

import com.jfinal.plugin.activerecord.IBean;

/** Generated by JFinal, do not modify this file. */

@SuppressWarnings("serial")

public abstract class BaseUser<M extends BaseUser<M>> extends Model<M> implements IBean {

public void setUserId(java.lang.Long userId) { set("user_id", userId); } 

public java.lang.Long getUserId() { return getLong("user_id"); }

public void setUserName(java.lang.String userName) { set("user_name", userName); }

public java.lang.String getUserName() { return getStr("user_name"); }

}

-------

(6)实体映射类 (_MappingKit.java)

【java】

package com.example.demo.model;

import com.jfinal.plugin.activerecord.ActiveRecordPlugin;

public class _MappingKit {

    public static void mapping(ActiveRecordPlugin arp) {

        arp.addMapping("user", "user_id", User.class);

        // 添加其他表的映射

    }

}

------

(7)服务层实现 (UserService.java)

【java】

package com.example.demo.service;

import com.example.demo.model.User;

import com.jfinal.plugin.activerecord.Page;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

@Service

public class UserService {

    private static final User dao =new User().dao();

    public User findById(Integer id) {

        return dao.findById(id);

    }

public Page<User> paginate(int pageNumber, int pageSize) {

//第一种,将sql写在service代码层中,不利于 sql 优化及维护

//return dao.paginate(pageNumber, pageSize, "SELECT *", "FROM user ORDER BY user_id DESC");

//第二种,将sql写在sql模板中,方便 sql 优化及维护;这种写法需要自己探索复杂用法和参数传递方法

        SqlPara sqlPara=dao.getSqlPara("user.paginate","");

        return dao.paginate(pageNumber, pageSize, sqlPara);

    }

   public boolean save(User user) {

        return user.save();

    }  

    public boolean update(User user) {

        return user.update();

    }

    public boolean delete(Integer id) {

        return dao.deleteById(id);

    }

    public User findByUsername(String username) {

        return dao.findFirst("SELECT * FROM user WHERE username = ?", username);

    }

}

------

(8)控制器层实现 (UserController.java)

【java】

package com.example.demo.controller;

import com.example.demo.model.User;

import com.example.demo.service.UserService;

import com.jfinal.plugin.activerecord.Page;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Controller;

import org.springframework.ui.Model;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestParam;

@Controller

public class UserController {

    @Autowired

    private UserService userService;

    @GetMapping("/users")

    public String listUsers(Model model, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) {

        Page<User> userPage = userService.paginate(page, size);

        model.addAttribute("userPage", userPage);

        return "userList";

    }

    @GetMapping("/users/{id}")

    public String viewUser(Model model, @PathVariable int id) {

        User user = userService.findById(id);

        model.addAttribute("user", user);

        return "userView";

    }

}

------

(9)主应用类 (DemoApplication.java)

【java】

package com.example.demo;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import lombok.extern.slf4j.Slf4j;

@SpringBootApplication

@Slf4j

public class DemoApplication {

    public static void main(String[] args) {

        SpringApplication.run(DemoApplication.class, args);

        Log.info(“项目启动成功”);

    }

}

------

(10)视图层

用户列表页面 (templates/userList.html)

【html】

 <!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>

    <title>用户列表</title>

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css">

</head>

<body>

<div class="container mt-4">

    <h1 class="mb-4">用户列表</h1>

    <table class="table table-striped table-hover">

        <thead class="table-dark">

        <tr>

            <th>ID</th><th>用户名</th><th>操作</th>

        </tr>

        </thead>

        <tbody>

        <tr th:each="user : ${userPage.list}">

            <!--后端如果生成了BaseUser.java,就可以用user.userId取值,且符合驼峰命名 -->

            <td th:text="${user.userId}" ></td>

            <!--后端如果没有生成BaseUser.java,就可以用user.get('user_name')取值,与数据库字段名相同 -->

            <td th:text="${user.get('user_name')}"></td>

           <td>

                <a th:href="@{/users/{id}(id=${user.get(‘user_id’)})}" class="btn btn-primary btn-sm">查看</a>

            </td>

        </tr>

        </tbody>

    </table>

    <!-- 分页控件 -->

    <nav aria-label="Page navigation">

        <ul class="pagination justify-content-center mt-3">

            <li class="page-item" th:if="${userPage.pageNumber > 1}">

                <a class="page-link" th:href="@{/users(page=${userPage.pageNumber - 1}, size=${userPage.pageSize})}" aria-label="Previous">

                    <span aria-hidden="true">«</span>

                </a>

            </li>

            <li class="page-item active">

                <a class="page-link" th:text="${userPage.pageNumber}" aria-current="page"></a>

            </li>

            <li class="page-item" th:if="${userPage.pageNumber < userPage.totalPage}">

                <a class="page-link" th:href="@{/users(page=${userPage.pageNumber + 1}, size=${userPage.pageSize})}" aria-label="Next">

                    <span aria-hidden="true">»</span>

                </a>

            </li>

        </ul>

    </nav>

</div>

</body>

</html>

------

用户详情页面 (templates/userView.html)

【html】

 <!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

<head>

    <title>用户详情</title>

    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css">

</head>

<body>

<div class="container mt-4">

    <h1 class="mb-4">用户详情</h1>

    <div class="card">

        <div class="card-body">

            <h5 class="card-title">用户信息</h5>

            <ul class="list-group">

                <li class="list-group-item"><strong>ID:</strong> <span th:text="${user.get(‘user_id’)}"></span></li>

                <li class="list-group-item"><strong>用户名:</strong> <span th:text="${user.get(‘user_name’)}"></span></li>

                <!-- 如果有更多字段,可以在这里添加 -->

            </ul>

            <a href="/users" class="btn btn-secondary mt-3">返回用户列表</a>

        </div>

    </div>

</div>

</body>

</html>

------

(11)数据库创建sql

【sql】

CREATE DATABASE IF NOT EXISTS `leave` ;

USE `leave`;

DROP TABLE IF EXISTS `leaves`;

CREATE TABLE `leaves` (

  `leave_id` bigint(11) NOT NULL AUTO_INCREMENT,

  `leave_user` char(20) DEFAULT NULL,

  `leave_title` char(100) DEFAULT NULL,

  `leave_note` text,

  `leave_time` char(20) DEFAULT NULL,

  `leave_show` char(1) DEFAULT '0',

  `leave_bak` text,

  `leave_bak_time` char(20) DEFAULT NULL,

  `leave_bak_user` char(20) DEFAULT NULL,

  PRIMARY KEY (`leave_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (

  `user_id` bigint(11) NOT NULL AUTO_INCREMENT,

  `user_name` char(20) NOT NULL,

  `user_pwd` char(50) NOT NULL DEFAULT '123456',

  `user_tel` char(20) DEFAULT NULL,

  `user_last_time` char(30) DEFAULT NULL,

  `user_right` char(20) DEFAULT 'user' COMMENT 'user,admin',

  PRIMARY KEY (`user_id`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

完成,有问题欢迎交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值