一个比MyBatis还好用的数据库持久层框架!

1、Mybatis缺点

相信来看博客后端大佬们都用过mybatis或者它的plus版吧,如果没有那Hibernate、JPA至少有一款绝对是你常用的数据库持久层框架,不知道你是否会感觉到每次使用mybatis时,会感到有一些比较繁杂冗余的地方,比如建了一个新表,得随之加一个新表对应的映射吧,即使仅仅给表加了一个字段,你也得去对应的映射去加上这个字段,而且建映射的时候还有很多注意事项,比如标记主键啊:如果你的主键不是id但是没有给字段上加上@TableId注释,你就会发现新增的数据主键没了,就问头不头大?来看看百度到的这些个缺点

  1. SQL 映射复杂:虽然 MyBatis 可以让开发人员更加方便地编写 SQL语句,但是这同时也意味着需要开发者自己解决一些查询语句上的问题。在相当多数情况下,使用 MyBatis 需要具备一定的 SQL能力才可以完成项目的 CRUD 操作,否则可能会导致代码质量差、映射不正确等问题。
  2. XML 配置维护困难:MyBatis 的配置文件需要通过 XML 来进行书写,这种方式与 Java代码并不是很直观。由于项目规模越来越大,配置文件中的内容也逐渐增多,这就导致了配置文件变得十分庞大、难以维护。
  3. 编码效率较低:MyBatis 对于开发人员的编码技术要求比较高,因为需要手动编写 SQL查询语句和参数映射。这就增加了开发人员的工作量,降低了开发效率,对技术水平要求也相应提高。
  4. 难以掌控SQL执行细节: 在某些场景下,特别是对于更加复杂的 SQL 执行,MyBatis 可能会顾及不到底层细节问题,导致性能劣化。需要开发者自行掌握数据库相关知识并评估。
  5. 重复代码:使用 MyBatis 进行开发时,需要为每个映射文件编写单独的 mapper 文件,这可能会导致出现大量冗余代码。
  6. 缺乏完备的文档和社区: 尽管 MyBatis 是一个流行的 ORM 框架,但是与其他一些框架相比,Mybatis 的中文资料较少且不清晰,同时社区活跃度较低。

作为一个混迹后端的三年小开发,我个人是觉得说的有道理的,上面说到的确实有几个痛点,但是我觉得还不是很全,让我补上几点:

  1. 如果要做比较复杂的表联接,用自带的方法写起来比较麻烦,不如直接写自定义查询;
  2. 写一个自定义查询也比较麻烦,需要在mapper里新建一个方法,新建完后还要在对应的xml写,sql写在xml中,麻烦!
  3. 如果查出来的字段是多个表中的某些字段或者有as后的自定义字段名,这样子就不能用表的映射实体类了,只能使用Map或者单独给这个数据结构建一个实体类,Map中的方法比较少,get()的结果是Object,用起来每次都需要转类型且容易报错

2、Jfinal的activeRecord介绍

相信我,这玩意你用了就知道有多爽!

独创的Db + Record模式太好用了真的,不用再纠结自建映射了,有自动生成映射的工具类,Record这个结果收集类也提供了很多get方法,特殊的类型直接getStr、getDate…等等,不再需要手动转类型;甚至如果你擅长写SQL的话,可以说完全用不着映射,配合Enjoy SQL 模板,写自定义SQL直接爽到起飞,增删改查一步到位,我想当一个个简简单单的CRUD员 (●’◡’●) 。

具体介绍可以看Jfinal的官方文档: ActiveRecord 5.1 概述

下面开始正文,介绍SpringBoot整合Jfinal的ActiveRecord的具体步骤

3、SpringBoot整合Jfinal的ActiveRecord

① pom引入

SpringBoot 的核心引入

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.0</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
    </dependencies>

用到的工具类引入

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.4.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.17.1</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.1-jre</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>

整合JFinal activeRecord的核心引入

        <!-- activeRecord引入-->
        <dependency>
            <groupId>com.jfinal</groupId>
            <artifactId>activerecord</artifactId>
            <version>4.9.06</version>
        </dependency>

        <!-- 阿里数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.20</version>
        </dependency>

        <!-- Mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.22</version>
        </dependency>

build打包工具的引入(这个也不能忽视,不然后面会遇到问题

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <!-- 这个一定不能少 少了打包后不会把sql文件打入 -->
                    <include>**/*.sql</include>
                    <include>**/*.txt</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

② 新建配置文件application.yml

server:
  port: 11188
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/project?zeroDateTimeBehavior=CONVERT_TO_NULL&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&useOldAliasMetadataBehavior=true&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver

(仅供参考,具体的配置根据用者自己情况改)

③ 编写配置类config

@Configuration
public class ActiveRecordPluginConfig {

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

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

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

    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    /**
     * logger
     */
    private static final Logger log = LoggerFactory.getLogger(ActiveRecordPluginConfig.class);

    public DruidPlugin createDruidPlugin() {
        return new DruidPlugin(url, username, password,driverClassName);
    }

    @Bean
    public ActiveRecordPlugin initActiveRecordPlugin() {
        // 配置druid数据库连接池插件
        DruidPlugin druidPlugin = this.createDruidPlugin();
        // 配置ActiveRecord插件
        ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
        arp.setShowSql(true);
        arp.setDevMode(true);

        log.info("自动初始化");
        // 所有映射在 MappingKit 中自动化搞定
        //_MappingKit.mapping(arp);
        // com.active 填你的包名 这个工具类下一步会讲
        AutoSqlMappingKit.auto(arp,new String[]{"com.active"});

        // 开启插件
        druidPlugin.start();
        arp.start();
        log.info("整合ActiveRecord成功");

        return arp;
    }
}

上面内容需要改AutoSqlMappingKit.auto(arp,new String[]{“com.active”});里的内容,内容是你的包名

④ 加载sql文件工具类AutoSqlMappingKit(重点)

public class AutoSqlMappingKit {
    private static final Logger log = LoggerFactory.getLogger(AutoSqlMappingKit.class);
    /**
     * 默认路径是项目的相对路径
     */
    public static void auto(ActiveRecordPlugin ds) {
        if (isWinOS()) {
            auto(ds, "/");
        } else {
            //获取项目跟路径
            String webRootPath = AutoSqlMappingKit.class.getResource("/").getPath();
            //获取项目绝对根路径
            Path absoluteWebPath = Paths.get(webRootPath, "/");
            auto(ds, absoluteWebPath.toString());
        }
    }

    public static void auto(ActiveRecordPlugin arp, String... packgeName) {
        auto(arp, CollectionUtil.newHashSet(packgeName));
    }

    /**
     * 扫描jar包中的sql文件
     *
     * @param arp
     */
    public static void auto(ActiveRecordPlugin arp, Set<String> packageName) {
        debug("开始映射包路径{} sql文件", packageName.toString());
        ScanKit.search(packageName, o -> true, new Medium<String, ISource>() {
            @Override
            public boolean test(String s) {
                return StringKit.endsWithIgnoreCase(s, ".sql");
            }

            @Override
            public ISource map(String s) {
                try {
                    debug("映射sql文件:" + s);
                    if (s.startsWith(File.separator) || s.contains(":")) {
                        return new FileSource(null, s, Charset.defaultCharset().toString());
                    }
                    StringSource source = new StringSource(IoUtil.read(new InputStreamReader(this.getClass().getResourceAsStream("/" + s), "UTF-8")), true);
                    if (ReUtil.isMatch(".*#namespace.*#end.*", source.getContent().toString())) {
                        return source;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public void apply(ISource stringSource) {
                if (stringSource != null) {
                    arp.addSqlTemplate(stringSource);
                }
            }
        });

    }

    private static void debug(String msg, Object... args) {
        log.debug(msg, args);
    }

    public static boolean isWinOS() {
        String os = System.getProperty("os.name");
        if (os.toLowerCase().startsWith("win")) {
            return true;
        } else {
            return false;
        }
    }
}

这个工具类需要着重介绍一下,主要功能是把你写的sql模板读取出来,然后加载到③中的配置,其实就是整合了Enjoy SQL 模板,而此工具类的作用是让其自动化识别你的模板而无需一个一个配置

ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
// 将sql模板加载到ActiveRecordPlugin中
arp.addSqlTemplate("all.sql");
_MappingKit.mapping(arp);
me.add(arp);

如上所示这是官方提供的案例,当有多个sql模板时需要一个一个手动配置,比较麻烦;用AutoSqlMappingKit可以将包里的所有sql模板全部添加到ActiveRecordPlugin中

  // com.active 填你的包名
  AutoSqlMappingKit.auto(arp,new String[]{"com.active"});

AutoSqlMappingKit里边还使用了其他的工具类,其他的工具类代码比较多就不再粘贴到文章了,需要的可以下载文章绑定的资源,或者私信我自取(认准CSDN:汤圆不扁),要是被转载了就找不到咯!

⑤ 启动项目

2024-01-18 19:43:42.851 [main] INFO  com.jfinal.config.ActiveRecordPluginConfig - 自动初始化 
2024-01-18 19:43:42.889 [main] DEBUG com.jfinal.kit.AutoSqlMappingKit - 开始映射包路径[com.active] sql文件 
2024-01-18 19:43:42.901 [main] DEBUG com.jfinal.kit.AutoSqlMappingKit - 映射sql文件:D:\java_a\project\zode\active\target\classes\com\active\api\sql\demo.sql 
2024-01-18 19:43:43.146 [main] INFO  com.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited 
2024-01-18 19:43:43.249 [main] INFO  com.jfinal.config.ActiveRecordPluginConfig - 整合ActiveRecord成功 

当日志出现这些内容时,已经代表你成功整合好了,就是这么简单!

⑥ 尝试使用

   // Db自带的方法Db.save 插入某条数据
   Record savePara = new Record().set("id",1000).set("title","Db自带的方法Db.save新增的内容");
   boolean save = Db.save("news", "id", savePara);
   System.out.println("save = " + save);

   // 通过使用SQL模板 插入某条数据
   int b = Db.update(Db.getSqlPara(SQL_KEY + "alertNews", Kv.by("id", 1001).set("title", "通过使用SQL模板新增的内容")));
   System.out.println("b = " + b);

        // Db自带的方法Db.update 更新某条数据
        Record updatePara = new Record().set("id", 1).set("title", "Db自带的方法Db.update修改后的内容");
        boolean update = Db.update("news", "id", updatePara);
        System.out.println("update = " + update);

        // 通过使用SQL模板 更新某条数据
        int a = Db.update(Db.getSqlPara(SQL_KEY + "updateNews", Kv.by("id", 2).set("title", "通过使用SQL模板修改后的内容")));
        System.out.println("a = " + a);

   // 通过使用SQL模板带有参数的查询
   Record record = Db.findFirst(Db.getSqlPara(SQL_KEY+"queryNews",Kv.by("id",1)));
   System.out.println(record);

   // Db自带的方法Db.findById通过主键查询
   Record item = Db.findById("news","id",1);
   System.out.println("item = " + item);

sql模板:

#namespace("com.active.api.sql")
    #sql("queryNews")
        SELECT id,title
        FROM news
        WHERE id = #para(id)
    #end

    #sql("updateNews")
        UPDATE news_item
        SET title = #para(title)
        WHERE id = #para(id)
    #end

    #sql("alertNews")
        INSERT INTO news_item(
            id,title
        ) values (
            #para(id),#para(title)
        )
    #end
#end

Db提供了很多内置的方法,支持增删改查,也支持批量操作batchUpdate等等,非常好用!
如果内置的方法无法实现你的复杂SQL,可以借助于SQL模板编写自定义SQL
然后具体的SQL模板语法可以参考官方文档:Enjoy SQL 模板

至此,已经整合结束了,你可以使用jfinal独创的Db + Record模式,以及Enjoy SQL模板,写自定义SQL可以写到飞起

4、自动生成映射

好吧还未结束!

走完上面这些步骤有人肯定会发现,怎么没有生成表的映射和实体类呢,万一我需要实体类来获取接收参数,你这不是也不太行?

由于本人喜欢写自定义SQL,用表的实体类比较少,所以这里就简单介绍一下如何整合,具体详细内容可以参考这篇大佬写的文章 链接: springboot 集成 Jfinal activerecord插件

jfinal提供了自动生成映射的方法 生成器的使用,让你无需手动新建映射和实体类,完全通过工具自动创建

① model数据库实体类生成工具 AutoGenerator

public class AutoGenerator {

    public static DruidPlugin createDruidPlugin() {
        Prop p = PropKit.useFirstFound("wms-generator.txt");
        return new DruidPlugin(p.get("jdbcUrl"), p.get("user"), p.get("password").trim(),p.get("driverClassName"));
    }

    public static DataSource getDataSource() {
        DruidPlugin druidPlugin = createDruidPlugin();
        druidPlugin.start();
        return druidPlugin.getDataSource();
    }

    public static void main() {
        // base model 所使用的包名
        String baseModelPackageName = "com.jfinal.model.base";
        // base model 文件保存路径
        String baseModelOutputDir = PathKit.getWebRootPath() + "/src/main/java/com/jfinal/model/base";

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

        // 创建生成器
        Generator generator = new Generator(getDataSource(), baseModelPackageName, baseModelOutputDir, modelPackageName, modelOutputDir);

        // 配置是否生成备注
        generator.setGenerateRemarks(true);

        // 设置数据库方言
        generator.setDialect(new MysqlDialect());

        // 设置是否生成链式 setter 方法
        generator.setGenerateChainSetter(false);

        // 添加不需要生成的表名
        generator.addExcludedTable("adv");

        // 设置是否在 Model 中生成 dao 对象
        generator.setGenerateDaoInModel(true);

        // 设置是否生成字典文件
        generator.setGenerateDataDictionary(false);

        // 设置需要被移除的表名前缀用于生成modelName。例如表名 "osc_user",移除前缀 "osc_"后生成的model名为 "User"而非 OscUser
        generator.setRemovedTableNamePrefixes("wms");

        // 生成
        generator.generate();
    }
}

以上代码需要改三个地方:baseModelPackageName 、baseModelOutputDir 、modelPackageName
显而易见是你要把model存的目录,找个地方建一个model目录,然后module里建一个base目录

② 生成_MappingKit

可以单独启动这个main方法,也可以讲这个main方法放到启动类中,每次启动时候自动执行

@SpringBootApplication
@ComponentScan({"com.active","com.jfinal"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class,args);
        System.out.println("启动成功(●'◡'●)");
        AutoGenerator.main();
    }
}

(注意:我这里是放到了启动类中,如果单独启动这个main方法要把String[] args加上)

    public static void main(String[] args) {
        // base model 所使用的包名
        String baseModelPackageName = "com.jfinal.model.base";
        // base model 文件保存路径
        String baseModelOutputDir = PathKit.getWebRootPath() + "/src/main/java/com/jfinal/model/base";
        ...

③ 检查是否自动生成了_MappingKit以及表的映射

当你执行完上一步后,你建的目录中已经自动生成了_MappingKit以及你库里所有表对应的映射
如下图所示
在这里插入图片描述

④ 在ActiveRecordPluginConfig中调用_MappingKit.mapping(arp);

    @Bean
    public ActiveRecordPlugin initActiveRecordPlugin() {
        // 配置druid数据库连接池插件
        DruidPlugin druidPlugin = this.createDruidPlugin();
        // 配置ActiveRecord插件
        ActiveRecordPlugin arp = new ActiveRecordPlugin(druidPlugin);
        arp.setShowSql(true);
        arp.setDevMode(true);

        log.info("自动初始化");
        // *******************重点在这里
        // 所有映射在 MappingKit 中自动化搞定
        _MappingKit.mapping(arp);
        // *******************
        AutoSqlMappingKit.auto(arp,new String[]{"com.active"});

        // 开启插件
        druidPlugin.start();
        arp.start();
        log.info("整合ActiveRecord成功");

        return arp;
    }

⑤ 通过使用表的映射来查询

以下是摘抄自官方文档中的代码示例:

// 创建name属性为James,age属性为25的User对象并添加到数据库
new User().set(“name”,James).set(“age”, 25).save();
 
// 删除id值为25的User
User.dao.deleteById(25);
 
// 查询id值为25的User将其name属性改为James并更新到数据库
User.dao.findById(25).set(“name”,James).update();
 
// 查询id值为25的user, 且仅仅取name与age两个字段的值
User user = User.dao.findByIdLoadColumns(25, “name, age”);
 
// 获取user的name属性
String userName = user.getStr(“name”);
 
// 获取user的age属性
Integer userAge = user.getInt(“age”);
 
// 查询所有年龄大于18岁的user
List<User> users = User.dao.find(“select * from user where age>18);
 
// 分页查询年龄大于18的user,当前页号为1,每页10个user
Page<User> userPage = User.dao.paginate(1, 10, “select *, “from user where age > ?, 18);

5、结束

以上便是所以的内容,还有不足的地方就是没用上事务!后续再慢慢研究吧

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值