spring boot 整合多数据源
多数据源产生的场景
一般情况下,不会有多数据源这样的场景出现,但老项目或者特殊需求的项目,可能会有这样的场景
- 同一个应用需要访问两个数据库
- 不用数据库中间件的读写分离
注入数据源选择的时机
声明两个数据源实例,在getConnection的时候根据业务的不同,注入不同数据源的连接
环境准备
准备sql脚本,建立两个库,这里mysql为例
create database stu;
create database tech;
use stu;
create table student
(id varchar(50) not null comment '主键',name varchar(50) null comment '姓名',stu_no varchar(50) null comment '学号',constraint student_pk primary key (id)
);insert into student values ('1','张同学','111');
insert into student values ('2','李同学','222');
use tech;
create table teacher
(id varchar(50) not null comment '主键',name varchar(50) null comment '姓名',teach_no varchar(50) null comment '教师号',constraint teacher_pk primary key (id)
);insert into teacher values ('1','王老师','111');
insert into teacher values ('2','高老师','222');
实现DataSource方式实现多数据源
配置多数据源
server:port: 9000
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedatasource1:url: jdbc:mysql://shilei.tech:3306/stu?useSSL=true&serverTimezone=Asia/Shanghaiusername: rootpassword: root123456driver-class-name: com.mysql.cj.jdbc.Driverdatasource2:url: jdbc:mysql://shilei.tech:3306/tech?useSSL=true&serverTimezone=Asia/Shanghaiusername: rootpassword: root123456driver-class-name: com.mysql.cj.jdbc.Driverdruid:initial-size: 5min-idle: 1max-active: 20mybatis-plus:mapper-locations: classpath:/mapper/*.xmltype-aliases-package: com.datasource.dynamicdatasource.model
添加数据源配置
package com.datasource.dynamicdatasource.config;import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.sql.DataSource;/*** @author sl*/
@Configuration
public class DataSourceConfig {@Bean("dataSource1")@ConfigurationProperties(prefix = "spring.datasource.datasource1")public DataSource dataSource1(){DruidDataSource druidDataSource = new DruidDataSource();return druidDataSource;}@Bean("dataSource2")@ConfigurationProperties(prefix = "spring.datasource.datasource2")public DataSource dataSource2(){DruidDataSource druidDataSource = new DruidDataSource();return druidDataSource;}
}
实现DataSource多数据源
package com.datasource.dynamicdatasource.config;import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;import javax.annotation.Resource;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;/*** @author sl* @Primary主要注入的bean*/
@Configuration
@Primary
public class DynamicDataSource implements DataSource {public static ThreadLocal<String> nameFlag = new ThreadLocal<>();@Resourceprivate DataSource dataSource1;@Resourceprivate DataSource dataSource2;@Overridepublic Connection getConnection() throws SQLException {if("student".equals(nameFlag.get())){return dataSource1.getConnection();}return dataSource2.getConnection();}@Overridepublic Connection getConnection(String username, String password) throws SQLException {return null;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return null;}@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {return false;}@Overridepublic PrintWriter getLogWriter() throws SQLException {return null;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {}@Overridepublic void setLoginTimeout(int seconds) throws SQLException {}@Overridepublic int getLoginTimeout() throws SQLException {return 0;}@Overridepublic Logger getParentLogger() throws SQLFeatureNotSupportedException {return null;}
}
测试多数据源
package com.datasource.dynamicdatasource.controller;import com.datasource.dynamicdatasource.config.DynamicDataSource;
import com.datasource.dynamicdatasource.model.Student;
import com.datasource.dynamicdatasource.model.Teacher;
import com.datasource.dynamicdatasource.service.StudentService;
import com.datasource.dynamicdatasource.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @author sl*/
@RestController
public class TestDataSourceController {@Autowiredprivate StudentService studentService;@Autowiredprivate TeacherService teacherService;@GetMapping("/stu")public String getStu(){DynamicDataSource.nameFlag.set("student");List<Student> allStudent = studentService.findAllStudent();return allStudent.toString();}@GetMapping("/tech")public String getTech(){DynamicDataSource.nameFlag.set("teacher");List<Teacher> allTeacher = teacherService.findAllTeacher();return allTeacher.toString();}
}
效果如下所示
此实现方式的弊端
实现DataSource接口我们本质上只使用了一个方法,就是getConnection()这个无参的方法,其他方法,当内部调用时可能会导致错误,我们不可能实现所有的方法,所以我们继承AbstractRoutingDataSource抽象类
继承AbstractRoutingDataSource实现多数据源
AbstractRoutingDataSource的结构
可以看到AbstractRoutingDataSource继承自DataSource,提供了一些实现方法
AbstractRoutingDataSource的重要属性
targetDataSources 所有数据源 (需指定)
defaultTargetDataSource 默认数据源(需指定)
resolvedDataSources= targetDataSources 负责最终切换的数据源map 等于 tagetDataSources
继承AbstractRoutingDataSource实现多数据源
package com.datasource.dynamicdatasource.config;import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.annotation.Resource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;/*** @author sl* @Primary主要注入的bean*/
@Configuration
@Primary
public class DynamicDataSource extends AbstractRoutingDataSource {public static ThreadLocal<String> nameFlag = new ThreadLocal<>();@Resourceprivate DataSource dataSource1;@Resourceprivate DataSource dataSource2;@Overrideprotected Object determineCurrentLookupKey() {// 返回当前数据源的标识return nameFlag.get();}@Overridepublic void afterPropertiesSet() {// 为targetDataSources 初始化所有数据源Map<Object,Object> targetDataSources=new HashMap<>();targetDataSources.put("student",dataSource1);targetDataSources.put("teacher",dataSource2);super.setTargetDataSources(targetDataSources);// 设置默认数据源super.setDefaultTargetDataSource(dataSource1);// 循环给resolvedDataSources,也就是最终数据源mapsuper.afterPropertiesSet();}
}
determineCurrentLookupKey的作用
看一段源码,就是通过determineCurrentLookupKey获取数据源的key,然后去resolvedDataSources中取数据源,resolvedDataSources数据源其实就是targetDataSources
protected DataSource determineTargetDataSource() {Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");Object lookupKey = determineCurrentLookupKey();DataSource dataSource = this.resolvedDataSources.get(lookupKey);if (dataSource == null && (this.lenientFallback || lookupKey == null)) {dataSource = this.resolvedDefaultDataSource;}if (dataSource == null) {throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");}return dataSource;}
测试多数据源
package com.datasource.dynamicdatasource.controller;import com.datasource.dynamicdatasource.config.DynamicDataSource;
import com.datasource.dynamicdatasource.model.Student;
import com.datasource.dynamicdatasource.model.Teacher;
import com.datasource.dynamicdatasource.service.StudentService;
import com.datasource.dynamicdatasource.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @author sl*/
@RestController
public class TestDataSourceController {@Autowiredprivate StudentService studentService;@Autowiredprivate TeacherService teacherService;@GetMapping("/stu")public String getStu(){// 默认数据源就是studentList<Student> allStudent = studentService.findAllStudent();return allStudent.toString();}@GetMapping("/tech")public String getTech(){DynamicDataSource.nameFlag.set("teacher");List<Teacher> allTeacher = teacherService.findAllTeacher();return allTeacher.toString();}
}
AOP自定义注解方式+AbstractRoutingDataSource实现多数据源
数据源的切换还是使用AbstractRoutingDataSource,只不过切换方式采用aop拦截自定义注解切换数据源,这种方式也是mybatis-plus多数据源插件所采用的方式
自定义注解
package com.datasource.dynamicdatasource.annotation;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author sl*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyDataSource {String value() default "student";
}
配置切面
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
package com.datasource.dynamicdatasource.aspect;import com.datasource.dynamicdatasource.annotation.MyDataSource;
import com.datasource.dynamicdatasource.config.DynamicDataSource;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;/*** @author sl*@Aspect 标识是一个切面*/
@Aspect
@Component
public class DatasourceAspect {/*** 切点规则*/@Pointcut("@annotation(com.datasource.dynamicdatasource.annotation.MyDataSource)")public void pointcut() {}@Before("pointcut()")public void dataSourceAspect(JoinPoint joinPoint){// 获取方法Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();// 判断方法中是否添加了注解if(method.isAnnotationPresent(MyDataSource.class)){// 获取方法上的注解MyDataSource annotation = method.getAnnotation(MyDataSource.class);String value = annotation.value();// 设置数据源DynamicDataSource.nameFlag.set(value);}}
}
测试自定义注解切换数据源
@GetMapping("/tech")@MyDataSource("teacher")public String getTech(){List<Teacher> allTeacher = teacherService.findAllTeacher();return allTeacher.toString();}
dynamic-datasource多数据源组件实现多数据源
官方文档及搭建指南地址:多数据源 | MyBatis-Plus
引入依赖
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.6.1</version>
</dependency>
配置数据源
server:port: 9000
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedynamic:#设置默认的数据源或者数据源组,默认值即为masterprimary: master#严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源strict: falsedatasource:master:url: jdbc:mysql://shilei.tech:3306/stu?useSSL=true&serverTimezone=Asia/Shanghaiusername: rootpassword: root123456driver-class-name: com.mysql.cj.jdbc.Driverteacher:url: jdbc:mysql://shilei.tech:3306/tech?useSSL=true&serverTimezone=Asia/Shanghaiusername: rootpassword: root123456driver-class-name: com.mysql.cj.jdbc.Driverdruid:initial-size: 5min-idle: 1max-active: 20mybatis-plus:mapper-locations: classpath:/mapper/*.xmltype-aliases-package: com.datasource.dynamicdatasource.model
测试数据源切换
数据源切换使用@DS注解,不使用此注解,使用默认数据源,方法上使用>类上使用
package com.datasource.dynamicdatasource.controller;import com.baomidou.dynamic.datasource.annotation.DS;
import com.datasource.dynamicdatasource.model.Student;
import com.datasource.dynamicdatasource.model.Teacher;
import com.datasource.dynamicdatasource.service.StudentService;
import com.datasource.dynamicdatasource.service.TeacherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.List;/*** @author sl*/
@RestController
public class TestDataSourceController {@Autowiredprivate StudentService studentService;@Autowiredprivate TeacherService teacherService;@GetMapping("/stu")public String getStu(){List<Student> allStudent = studentService.findAllStudent();return allStudent.toString();}@GetMapping("/tech")@DS("teacher")public String getTech(){List<Teacher> allTeacher = teacherService.findAllTeacher();return allTeacher.toString();}
}
项目启动日志中可以看到两个数据源的加载信息
访问tech以及stu都能正常访问,代表动态数据源添加成功
需要注意的问题
使用多数据源要注意事务的控制,提交和回滚策略,可以观看spring多数据源事务解决方案
相关文章:
spring boot 整合多数据源
多数据源产生的场景 一般情况下,不会有多数据源这样的场景出现,但老项目或者特殊需求的项目,可能会有这样的场景 同一个应用需要访问两个数据库不用数据库中间件的读写分离 注入数据源选择的时机 声明两个数据源实例,在getConnect…...
数据集成:数据挖掘的准备工作之一
⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ 🐴作者:秋无之地 🐴简介:CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作,主要擅长领域有:爬虫、后端、大数据…...
xml配置文件密码特殊字符处理
错误姿势: 正确姿势:采取转义符的方式 常用转义符:...
遥感数据与作物模型同化
基于过程的作物生长模拟模型DSSAT是现代农业系统研究的有力工具,可以定量描述作物生长发育和产量形成过程及其与气候因子、土壤环境、品种类型和技术措施之间的关系,为不同条件下作物生长发育及产量预测、栽培管理、环境评价以及未来气候变化评估等提供了…...
UI库DHTMLX Suite v8.2发布全新表单组件,让Web表单实现高度可定制!
DHTMLX Suite v8.2日前已正式发布,此版本的核心是DHTMLX Form,这个小部件接收了4个备受期待的新控件,如Fieldset、Avatar、Toggle和ToggleGroup。官方技术团队还为Grid和TreeGrid小部件中的页眉/页脚工具提示提供了一系列新的配置选项等。 在…...
河北省图书馆典藏《乡村振兴振兴战略下传统村落文化旅游设计》许少辉八一新著
河北省图书馆典藏《乡村振兴振兴战略下传统村落文化旅游设计》许少辉八一新著...
什么是卷积002
文章目录 前言1.卷积网络和传统网络区别2.卷积神经网络整体架构1.输入层2. 卷积层3.池化层4.全连接层 5.神经网络6.经典网络1.Alexnet2. Vgg3.Resnet 残差网络-特征提取 7.感受野 前言 大纲目录 首先链接图像颜色通道 1.卷积网络和传统网络区别 右边的就是CNN,卷…...
黑马JVM总结(八)
(1)StringTable面试题 1.8 1.6时 (2)StringTable的位置 jvm1.6时StringTable是常量池的一部分,它随着常量池存储在永久代当中,在1.7、1.8中从永久代变成了堆中,为什么做这个更改呢?…...
开源网安入选广东省网络空间安全标准化技术委员会新技术及应用安全技术工作组成员单位
近日,第二届广东省网络空间安全标准化技术委员会(GD/TC 124)(以下简称省网安标委)正式成立。为进一步发挥省网安标委在支撑网络强国建设、推进网络安全产业高质量发展过程中,示范引领核心技术攻关、创新产品…...
Nginx配置指南:如何定位、解读与优化Linux上的Nginx设置
🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🐅🐾猫头虎建议程序员必备技术栈一览表📖: 🛠️ 全栈技术 Full Stack: 📚…...
辉瑞与吉利德科学:制药巨头的新冠病毒之战
来源:猛兽财经 作者:猛兽财经 总结: (1)猛兽财经认为,华尔街低估了辉瑞(PFE)和吉利德科学(GILD)的前景,因为它们在开发新冠病毒疫苗和药物方面都…...
x86架构基础汇编知识
通用寄存器 EAX 32位 函数返回值 AX 低16位 AH 高八位 AL 低八位 EBX 32位 ECX 32位 循环次数,this指针 EDX 32位 EBP 32位 栈底寄存器 ESP 32位 栈顶寄存器 ESI 源索引寄存器 EDI 目标索引寄存器 EIP 无法直接通过汇编操作 例子 mov al,0xff …...
ThreadLocal的原理
ThreadLocal是Java中的一个类,它提供了线程本地变量的功能。每个线程都可以独立地访问自己的ThreadLocal变量,并且不会受到其他线程的干扰。 public class ThreadLocal<T> { ThreadLocal的原理是通过使用一个ThreadLocalMap来存储每个线程的变量副…...
Chrome 108版(64-bit 108.0.5359.125)网盘下载
还在用Selenium的朋友们注意了,目前Chrome的最新版是116,而官方的Chromedriver只支持到115版。 可惜Google不提供旧版Chrome的下载方式,需要旧版的很难回去了。如果真的想要旧版的Chrome,只能民间自救。 我在2022年12月备份了C盘…...
Mars3d插件参考开发教程并在相关页面引入
问题场景: 1.在使用Mars3d热力图功能时,提示mars3d.layer.HeatLayer is not a constructor 问题原因: 1.mars3d的热力图插件mars3d-heatmap没有安装引用。 解决方案: 1.参考开发教程,找到相关的插件库:Mars3D 三维…...
Windows 性能突然打鸡血,靠 Bug 修复了多年顽疾
要说 的 Bug 集中地,当属资源管理器。 速度缓慢、卡顿、崩溃,不同设备、不同版本的用户都有不同的感受。 严格来说,这其实是 Windows 的传统艺能,要完美修复可不容易。 而作为小老弟的文件资源管理器,时不时来个无响…...
亚马逊封买家账号的原因有哪些
亚马逊可能封锁买家账号的原因有多种,主要是出于保护市场和维护平台秩序的考虑。以下是一些可能导致亚马逊封锁买家账号的常见原因: 1、涉及违规行为:如果买家违反了亚马逊的使用政策,如发表虚假评价、滥用退货政策、欺诈或盗窃等…...
1.0零基础尝试DCM通讯(c-store)
前言 本项目是对医院放疗及相关设备的互通互联。对dcm文件及数据协议是本项目的基础。 今天在项目组成员支持下,对dcm通讯进行了初步的尝试,有人之路,这个过程可以说是非常愉快,于是乎准备将这个愉快的过程记录,方便自己查阅和后来人。 c-store 本次的安装和测试使用的…...
vue之封装tab类组件
vue之封装tab类组件 vue之封装tab类组件CSS样式方面JS方面 vue之封装tab类组件 需求:点击【上一步】、【下一步】按钮,切换tab,且有淡入浅出的动画。 CSS样式方面 <div class"parent"><div class"childDiv" id…...
固定资产管理中净值怎么算
在资产管理的领域中,我们经常听到“净值”这个词。然而,对于许多人来说,净值的概念仍然模糊不清。本文将试图揭示固定资产管理的净值计算方法,并提供一些创新的观点。 我们需要明确什么是净值。在财务术语中,净值是…...
SQlite操作后如何正确退出
在 C 语言中,使用 SQLite 库进行数据库操作后,可以通过以下步骤来正常退出和关闭 SQLite 连接: 关闭数据库连接:在完成数据库操作后,使用 sqlite3_close() 函数来关闭 SQLite 连接。该函数接受一个指向 sqlite3 数据库…...
phpcmsV9.6.0sql注入漏洞分析
目录 前言 环境准备 漏洞点 看一看parse_str函数 看一看sys_auth函数 看一看get_one函数 全局搜索sys_auth($a_k, ENCODE) 查看哪里调用了 set_cookie 查看safe_replace函数 判断登录绕过 index的业务 加载modules/wap/index.php 加载modules/attachment/attachme…...
深入理解正则表达式:高效处理文本数据的利器
💂 个人网站:【工具大全】【游戏大全】【神级源码资源网】🤟 前端学习课程:👉【28个案例趣学前端】【400个JS面试题】💅 寻找学习交流、摸鱼划水的小伙伴,请点击【摸鱼学习交流群】 引言 正则表达式是一种…...
张雪峰说网络空间安全专业
网络空间安全专业是一个涵盖了计算机科学、信息安全、法律等多个领域的学科,旨在研究保护网络空间的信息系统和数据不被非法侵入、破坏、篡改、泄露的技术和管理手段。 网络安全专业的重要性 随着网络技术的发展,网络安全问题也日益凸显,黑客…...
day11-ArrayList学生管理系统
1.ArrayList 集合和数组的优势对比: 长度可变添加数据的时候不需要考虑索引,默认将数据添加到末尾 1.1 ArrayList类概述 什么是集合 提供一种存储空间可变的存储模型,存储的数据容量可以发生改变 ArrayList集合的特点 长度可以变化…...
java springboot 如何实现小程序支付
今天给大家分享java小程序支付 首先我们学习任何东西要先看官网 下面是支付业务流程 我们具体用代码去实现上面的业务流程 功能截图 代码截图 pay(){//调用后台生成订单var orderNumber "20210101123456";var amount 0.01;WxPay.wxpay(app, amount, orderNumber…...
题目:2839.判断通过操作能否让字符串相等 I
题目来源: leetcode题目,网址:2839. 判断通过操作能否让字符串相等 I - 力扣(LeetCode) 解题思路: 两字符串奇数位字符计数相等并且偶数位字符计数相等是可通过操作让字符串相等。 解题代码ÿ…...
【Prometheus】Prometheus+Grafana部署
Prometheus 概述 官网https://prometheus.io/docs/introduction/overview/ Prometheus 是一款基于时序数据库的开源监控告警系统,非常适合Kubernetes集群的监控。Prometheus的基本原理是通过HTTP协议周期性抓取被监控组件的状态,任意组件只要提供对应的…...
无CDN场景下的传统架构接入阿里云WAF防火墙的配置实践
文章目录 1.配置网站接入WAF防火墙1.1.配置网站接入方式1.2.填写网站的信息1.3.WAF防火墙生成CNAME地址 2.配置WAF防火墙HTTPS证书3.修改域名DNS解析记录到WAF防火墙4.验证网站是否接入WAF防火墙 传统架构接入WAF防火墙非常简单,配置完WAF网站接入后,将得…...
和鲸技术!国家气象信息中心人工智能气象应用基础技术平台上线
8 月 31 日,由和鲸科技支持的人工智能气象应用基础支撑技术平台 V1.0 正式于国家气象信息中心(下简称“信息中心”)上线发布。该平台主要为人工智能技术在气象领域的融合应用提供基础性支撑,目前,已为基于深度学习的短…...
58同城通辽做网站/南京 seo 价格
题目:海滩上有一堆桃子,五只猴子来分。第一只猴子把这堆桃子凭据分为五份,多了一个,这只猴子把多的一个扔入海中,拿走了一份。第二只猴子把剩下的桃子又平均分成五份,又多了一个,它同样把多的一…...
广州美霖室内设计学校/seo培训教程视频
Collections工具类与泛型的使用 Collections工具类的使用 ●Collections是一个操作Set、List 和Map等集合的工具类 ●Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法 Co…...
wordpress post data/app推广联盟
编译不能通过 #include "iostream" using namespace std; int _tmain(int argc, _TCHAR* argv[]) { char *p "hello" ; //不是把一个字符串赋给了一个字符型的指针,而是把一个字符型的指针指向了字符串的首地址。strcpy(p,"hel"); co…...
建筑企业网站模板免费下载/seo推广策略
http://topic.csdn.net/u/20110630/12/08b2975f-cce2-4f5a-8397-7b9b87bc1324.html...
用dw做音乐网站模板/推广引流平台app大全
当互联网进入中国,随之而来的web1.0概念便在中国遍地开花,生根发芽。在04年左右web2.0这个概念初次进入我们的圈子里面。到如今在整个网站行业中的应用也已经比较普遍。今天,我将会对这两个概念来深度分析,寻找我们网站运营的方向…...
常宁市建设局网站/好的竞价账户托管外包
版本: django:1.11.9 python3 mysql 5.7.18 在这里我们认为你已经安装好了mysql,python ,django 下面是来自django官方教程的一段话 If you wish to use another database, install the appropriate database bindings and change the following keys in theDATABA…...