二、一套代码适配多种数据库之初步方案

前言

经过需求概述的分析,如果要实现一套代码适配多种数据库,因为service层要调用dao层,dao层调用mapper.xml,所以service层、dao层、mapper.xml会受到适配多种数据库方案的影响,我们先从最简单的方案:service层、dao层、mapper.xml全部改动开始,然后分析该方案的优缺点,针对该方案缺点进行方案升级,及过程中遇到的问题和解决思路,按照这个思路不断升级并形成最终方案。

一、初步方案概述

最简单的方案莫过于根据不同的数据库创建不同的dao层和mapper.xml,然后在service层根据当前数据库驱动类型调用对应的dao层接口。所以大概步骤如下:

  1. 准备新数据库环境
  2. 添加新数据库驱动依赖
  3. 添加新数据库连接配置
  4. 创建不同数据库适配的dao层接口及其实现mapper.xml
  5. service层创建不同数据库的dao层接口对象;根据当前配置的数据库驱动类型,选择适用的dao层接口
  6. 使用不同的数据库测试

二、项目改造

1、示例项目简介

如果自己有项目,可以在自己项目上测试,如果没有或者不想在自己项目测试,可以使用本文提供的示例项目

  1. 开发框架:Spring Boot 2.7.18
  2. 持久层框架:MyBatis 3.5.6
  3. 数据库连接池:Druid 1.2.6
  4. 数据库:MySQL 5.7
  5. 数据库驱动:com.mysql.cj.jdbc.Driver 8.0.33
  6. 下载示例项目后先看README.md
  7. 功能简介:用户查询功能,数据库:demo,表:user,列:id、name
  8. 项目需求:添加PostgreSQL数据库(版本10+)的支持

2、项目改造

  1. 如果使用示例项目,先根据README.md正确运行示例项目

  2. 准备PostgreSQL数据库环境(版本10+),使用其他数据库环境也可以,根据实际情况选择

    -- ----------------------------
    -- postgres_user.sql概述:数据库:demo,模式:multi_database,表:user,自增主键:id,列:name
    -- ----------------------------
    DROP TABLE IF EXISTS "multi_database"."user";
    CREATE TABLE "multi_database"."user" (
      "id" int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY (
    INCREMENT 1
    MINVALUE  1
    MAXVALUE 9223372036854775807
    START 1
    CACHE 1
    ),
      "name" varchar(32) COLLATE "pg_catalog"."default"
    )
    ;
    COMMENT ON COLUMN "multi_database"."user"."id" IS '主键';
    COMMENT ON COLUMN "multi_database"."user"."name" IS '名称';
    
    -- ----------------------------
    -- Records of user
    -- ----------------------------
    INSERT INTO "multi_database"."user" VALUES (1, '张三');
    INSERT INTO "multi_database"."user" VALUES (2, '李四');
    INSERT INTO "multi_database"."user" VALUES (3, '王二');
    INSERT INTO "multi_database"."user" VALUES (4, '麻子');
    
    -- ----------------------------
    -- Auto increment value for user
    -- ----------------------------
    SELECT setval('"multi_database"."user_id_seq"', 4, true);
    
    -- ----------------------------
    -- Primary Key structure for table user
    -- ----------------------------
    ALTER TABLE "multi_database"."user" ADD CONSTRAINT "user_pkey" PRIMARY KEY ("id");
    
  3. 添加PostgreSQL驱动依赖

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
        </dependency>
  1. 修改为PostgreSQL数据库连接配置
# 数据库配置 TODO 根据实际情况修改
database:
  ip: xxx
  # mysql: 3306 ;postgres: 5432
  port: 5432
  database: demo
  # postgres需要
  schema: multi_database
  username: xxx
  password: xxx
  # mysql: com.mysql.cj.jdbc.Driver ;postgres: org.postgresql.Driver
  driver-class-name: org.postgresql.Driver
  # mysql: jdbc:mysql://${database.ip}:${database.port}/${database.database}?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8
  # postgres: jdbc:postgresql://${database.ip}:${database.port}/${database.database}?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&&currentSchema=${database.schema},sys_catalog
  url: jdbc:postgresql://${database.ip}:${database.port}/${database.database}?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8&&currentSchema=${database.schema},sys_catalog

# 数据源配置
spring:
  datasource:
    #  数据源
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: ${database.driver-class-name}
    url: ${database.url}
    username: ${database.username}
    password: ${database.password}
  1. 创建UserPgMapper.java,文件内容与 UserMapper.java保持一致
package com.yu.dao;

import com.yu.entity.User;

import java.util.List;

public interface UserPgMapper {
    /**
     * 查询用户列表
     *
     * @param user 搜索条件
     * @return 用户列表
     */
    List<User> list(User user);
}
  1. 创建UserPgMapper.xml,内容如下
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yu.dao.UserPgMapper">

    <resultMap type="User" id="BaseResultMap">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
    </resultMap>

    <sql id="Base_Column_List">
        select
        id,
        name
        <!--TODO 特别注意,系统也有user表,所以这里需要加上模式-->
        from multi_database.user
    </sql>

    <select id="list" parameterType="User" resultMap="BaseResultMap">
        <include refid="Base_Column_List"/>
        <where>
            <if test="name != null  and name != ''">
                and name like concat('%', #{name}, '%')
            </if>
        </where>
    </select>

</mapper>
  1. service层创建不同数据库的dao层接口对象,根据驱动类型调用dao层接口
package com.yu.service.impl;

import com.yu.dao.UserMapper;
import com.yu.dao.UserPgMapper;
import com.yu.entity.User;
import com.yu.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserServiceImpl implements UserService {
    private static final String MYSQL = "com.mysql.cj.jdbc.Driver";
    private static final String POSTGRESQL = "org.postgresql.Driver";


    @Value("${spring.datasource.driver-class-name}")
    private String driverClass;


    @Autowired
    private UserMapper userMapper;
    @Autowired
    private UserPgMapper userPgMapper;

    @Override
    public List<User> list(User user) {
        if (MYSQL.equals(driverClass)) {
            return userMapper.list(user);
        } else if (POSTGRESQL.equals(driverClass)) {
            return userPgMapper.list(user);
        }
        return null;
    }
}
  1. 使用PostgreSQL数据库测试接口,访问获取用户列表接口 http://localhost:8080/user/list

在这里插入图片描述

三、方案总结

从上面修改情况来看,还是相当麻烦的,总结该方案的缺点如下:

  1. 重复代码太多
    1. mapper.java除了文件名不一样,其他全部一样,不管是扩展新功能还是升级旧功能,需要做好多没有价值的工作。
    2. mapper.xml重复也不少,即使有不同数据库使用相同的语法,也无法做到重用,必须copy一遍。
  2. Service层改动较大
    1. service层本身就是事务层,会有较多地方操作mapper,现在每次调用mapper之前需要判断当前数据库类型,本身工作量就很大,在新添加数据库类型时这部分工作又需要再做一遍,而且没什么价值,提升不了专业技能。
  3. 下文针对该缺点提出升级思路,升级过程中遇到的问题,及解决问题的思路,并形成升级方案
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那你为何对我三笑留情

感谢支持,沉淀自己,帮助他人!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值