Mybatis

1 什么是MyBatis

MyBatis是一个优秀的持久层框架,它对JDBC操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、 结果集检索等JDBC繁杂的过程代码 。

MyBatis通过 xml 文件或注解的方式将要执行的各种 statement 配置起来,并通过 Java 对象和 statement 中 sql 的动态参数进行映射生成最终执行的 sql 语句,最后由MyBatis框架执行sql并将结果 映射成Java对象并返回。

1.2 MyBatis的优点

  1. 简单易、 没有任何第三方依赖,最简单安装只要两个jar文件 + 配置几个sql映射文件,
  2. Mybatis不会对应用程序或者数据库的现有设计强加任何影响,sql写在xml里,便于统一管理和优 化。 通过sql语句可以满足操作数据库的所有需求。
  3. 解除sql与程序代码的耦合: 通过提供DAO层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测 试。 sql和代码的分离,提高了可维护性 。
  4. 提供丰富且强大的标签 。

1.3 为什么要学Mybatis

当使用java代码操作数据库时,需要用到 JDBC编程 ,但JDBC在使用时存在问题:

  1. 数据库连接使用时就创建,不使用时便立即释放,从而对数据库进行频繁的操作,导致资源的浪
    费、影响性能
  2. sql都是硬编码到Java程序中,如果改变sql,那么得重新编译Java代码,不利于系统后期的维护。
  3. 向PreparedStatement设置参数,也是硬编码到Java程序中,不利于后期的维护。
  4. 从resultset遍历结果集数据时,也存在硬编码,不利于后期系统的维护。

1.4 MyBatis整体架构

  1. MyBatis配置: mybatis-config.xml(名称不固定),此文件作为MyBatis的全局(核心)配置文件,配置了 MyBatis的运行环境等信息。 mapper.xml文件即sql映射文件,文件中配置了操作数据库的sql语句。
  2. 通过MyBatis环境等配置信息构造SqlSessionFactory,即会话工厂。
  3. 由会话工厂创建SqlSession即会话,操作数据库需要通过SqlSession进行。
  4. MyBatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行 器、一个是缓存执行器。
  5. MappedStatement也是MyBatis一个底层封装对象,Mybatis将SQL的配置信息加载成为一个个 MappedStatement对象(包括了传入参数映射配置、执行的SQL语句、结果映射配置),存储在内存 中。mapper.xml文件中一个sql对应一个MappedStatement对象,sql的id即是MappedStatement的 id。
  6. MappedStatement对sql执行输入参数进行定义,包括HashMap、基本类型、字符串类型、实体类 类型,Executor通过MappedStatement在执行sql前将输入的Java对象映射至sql中,输入参数映射就是 JDBC编程中对PreparedStatement设置参数。
  7. MappedStatement对sql执行输出结果进行定义,包括HashMap、基本类型、字符串类型、实体类 类型,Executor通过MappedStatement在执行sql后将输出结果映射至Java对象中,输出结果映射过程 相当于JDBC编程中对结果的解析处理过程。

1.5 vo、po、dto、bo、pojo、entity 等层的解释。

  • VO:值对象 ,由new创建,由GC回收,存放指定的数据,可以和表数据不一致。
  • PO:是 ORM 框架中Entity,PO属性和数据库中表的字段形成一一对应关系 加上get和set方法 ;
  • entity :和PO的功能类似,和数据表一 一对应,一个实体一张表 。
  • dto: 数据传输对象 ,存放页面需要的表的部分字段。
  • bo: BO是封装业务逻辑的Java对象,封装了多个po,vo,通过调用DAO方法,进行业务操作。
  • pojo:简单来说可以理解成不包含业务逻辑的单纯用来存储数据的Java类 , 可以转化为PO、DTO、VO。
    • 一个POJO持久化以后就是PO; 直接用它传递、传递过程中就是DTO; 直接用来对应表示层就是VO;

2 建立MyBatis项目

2.1新建一个java项目,导入maven包

xml 复制代码
<dependencies>
  <!-- mybatis -->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
  </dependency>
  <!-- oracle:大家在引入oracle依赖的时候肯定出错 -->
  <dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc6</artifactId>
    <version>11.2.0.1.0</version>
  </dependency>

  <!--  mysql -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.49</version>
  </dependency>
  <!-- log4j Mybatis的日志输出组件 -->
  <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
  </dependency>
  <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
  </dependency>
</dependencies>

2.2 编写MyBatis中全局配置文件 mybatis-config.xml

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.dtd">
 <!-- configuration 文件的根节点 -->
<!-- MyBatis配置文件报错:
在编写xml配置文件引入DTD约束时,可能会出现这个错误提示 URI is not registered(Settings | 
Languages & Frameworks | Schemas and DTDs),此时使用快捷键,选择Fetch external 
resource 或 Ignore external resource选项。或者直接在设置settings -> languages&frameworks -> schemas and dtds 中添加出问题的路径。 -->
<configuration>
    <!--
      properties 用于引入外部的properties配置文件
      resource:引入类路径下的文件
      url:引入磁盘或网路
    -->
    <properties/>
    <!-- 
      environments:多个配置环境;通过default属性可以对多个环境快速切换。
      environments default属性的值必须和某个environment的id值一致。
    -->
    <!-- 和spring整合后 environments配置将废除,了解即可 -->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/ssm?
 characterEncoding=utf8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
 </configuration>

2.3 导入一个数据库SQL

sql 复制代码
CREATE TABLE `dept`  (
  `deptno` int PRIMARY KEY AUTO_INCREMENT,
  `dname` varchar(20),
  `loc` varchar(40)
);
 INSERT INTO `dept` VALUES (10, 'ACCOUNTING', 'NEW YORK');
 INSERT INTO `dept` VALUES (20, 'RESEARCH', 'DALLAS');
 INSERT INTO `dept` VALUES (30, 'SALES', 'CHICAGO');
 INSERT INTO `dept` VALUES (40, 'OPERATIONS', 'BOSTON');
 CREATE TABLE `emp`  (
   `empno` int PRIMARY KEY AUTO_INCREMENT,
   `ename` varchar(20),
   `job` varchar(20),
   `mgr` int,
   `hiredate` date,
   `sal` double,
   `comm` double,
   `deptno` int,
   CONSTRAINT `FK_EMP_DEPTNO` FOREIGN KEY (`deptno`) REFERENCES `dept` (`deptno`)
 );
 INSERT INTO `emp` VALUES (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 1300, 
                           NULL, 20);
 INSERT INTO `emp` VALUES (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 2100, 
                           300, 30);
 INSERT INTO `emp` VALUES (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1750, 
                           500, 30);
 INSERT INTO `emp` VALUES (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 3475, 
                           NULL, 20);
 INSERT INTO `emp` VALUES (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1750, 
                           1400, 30);
 INSERT INTO `emp` VALUES (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 3350, 
                           NULL, 30);
 INSERT INTO `emp` VALUES (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2950, 
                           NULL, 10);
 INSERT INTO `emp` VALUES (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3500, 
                           NULL, 20);
 INSERT INTO `emp` VALUES (7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5500, 
                           NULL, 10);
 INSERT INTO `emp` VALUES (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 2000, 
                           0, 30);
 INSERT INTO `emp` VALUES (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1600, 
                           NULL, 20);
 INSERT INTO `emp` VALUES (7900, 'JAMES', 'CLERK', 7698, '0198-12-31', 1450, 
                           NULL, 30);
 INSERT INTO `emp` VALUES (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3500, 
                           NULL, 20);
 INSERT INTO `emp` VALUES (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1800, 
                           NULL, 10);

2.4 编写实体类

java 复制代码
import java.util.Date;
 public class Emp {
    private Integer empno;
    private String ename;
    private String job;
    private Integer mgr;
    private Date hiredate;
    private Double sal;
    private Double comm;
    private Integer deptno;
     // 省略set,get方法
 }

2.5 编写映射文件

在src/main/resources下创建mapper目录,在该目录下创建sql映射文件Emp.xml。  
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">
<!--
namespace: 命名空间,作用是mapper文件进行分类管理,用于隔离sql语句。
注意:如果使用mapper代理的方式进行开发,namespace有特殊的作用。-->
<mapper namespace="emp">
  <!-- 通过员工编号查询员工信息 -->
  <!--
  通过<select>标签编写查询语句
  id: 映射文件中SQL语句的唯一标识。
  mybatis会将SQL语句封装到MappedStatement对象中,所以此处的id也可以标识
  MappedStatement对象的id;
  注意:同一个mapper文件中id不能重复,而且id在mapper代理模式下有着重要作用;
  parameterType: 输入参数的类型。
  sql语句的占位符:#{};
  #{empno}:其中empno表示接收输入的参数值,参数名称为empno;
  但是如果参数的类型为简单类型(基本数据类型、包装类、字符串类型),参数名称可以任意
  指定;
  resultType: 输出参数的类型。
  需要指定输出数据为Java中的数据类型(实体类的全限定名);-->
  <select id="selectById" parameterType="java.lang.Integer" 
    resultType="com.gs.entity.Emp">
    select empno, ename, job, hiredate, mgr, sal, comm, deptno from emp where 
    empno=#{empno}
  </select>
</mapper>

将这个mapper文件都丢到全局配置文件中。

xml 复制代码
<!-- 加载映射文件的位置 -->
 <mappers>
 <mapper resource="mapper/Emp.xml"/>
 </mappers>

2.6 log4j配置 (在控制台可查看sql语句)

Mybatis日志输出:log4j.properties配置文件。

properties 复制代码
#井号表示注释,配置内容为键值对格式,每行只能有一个键值对,键值对之间以=连接
#指定logger
 #设定log4j的日志级别和输出的目的地
#INFO日志级别,Console和logfile输出的目的地
#等级 OFF,ERROR,WARN,INFO,DEBUG,TRACE,ALL
 log4j.rootLogger=DEBUG,Console
 #指定appender
 #设定Logger的Console,其中Console为自定义名称,类型为控制台输出
log4j.appender.Console=org.apache.log4j.ConsoleAppender
 #设定Logger的logfile,其中logfile为自定义名称,类型为文件
#org.apache.log4j.FileAppender文件
#org.apache.log4j.RollingFileAppender文件大小到达指定尺寸后产生一个新的文件
#org.apache.log4j.DailyRollingFileAppender每天产生一个日志文件
log4j.appender.logfile=org.apache.log4j.RollingFileAppender
 #设定文件的输出路径
log4j.appender.logfile.File=d:/log/test.log
 #设定文件最大尺寸  单位可以使KB,MB,GB
 log4j.appender.logfile.MaxFileSize=2048KB
 #输出格式
#设定appender布局Layout
 #   
%d 输出日志的日期和时间,指定格式:%d{yyyy-MM-dd HH:mm:ss SSS}
 #   
#   
#   
#   
#   
%p 输出的日志级别
%c 输出所属类的全类名
%M 方法名
%m 输出代码中指定消息
%n 一个换行符
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
 log4j.appender.Console.layout.ConversionPattern=%d %p %c.%M() --%m%n
 log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
 log4j.appender.logfile.layout.ConversionPattern=%d %p %c.%M() --%m%n

2.7 编写测试程序 (在test路径下)

运行测试类;

java 复制代码
/**
 * 测试程序
 */
public class MybatisTest {
    @Test
    public void test() throws IOException {
        //1.创建读取全局配置文件的流
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        //2.通过配置文件流创建会话工厂
        SqlSessionFactory factory = builder.build(in);
        //3.通过会话工厂创建会话对象(SqlSession)
        SqlSession session = factory.openSession();
        //4.通过会话对象操作数据
        /**
         * 查询单条记录
         * selectOne(String statementId, Object param)
         * 参数1:映射文件中的statementId,命名空间名.statementId
         * 参数2:向sql语句中传入的数据,注意:传入的数据类型必须与映射文件中配置的
parameterType保持一致
         * 返回值:就是映射文件中配置的resultType的类型
         * 查询多条记录
         * selectList()
         */
        Emp emp = session.selectOne("emp.selectById", 7369);
        System.out.println(emp);
        //5.关闭资源
        session.close();
    }
}

3 增删改查的基本操作的实现

实现以下功能: 1. 查询所有员工信息; 2. 添加员工; 3. 更新员工; 4. 删除员工; 5. 根据员工名模糊查询。

3.1 查询所有员工信息

编写mapper文件

xml 复制代码
<!--
查询到数据返回多条记录,每一条封装在一个实体类对象中,所有的实体类对象封装在List集合中
resultType:指定的并不是集合的类型,而是单条数据所对应实体类类型
resultType="java.util.List" 错误的配置方式-->
<select id="select" resultType="com.gs.entity.Emp">
  select empno, ename, job, mgr, hiredate, sal, comm, deptno from emp order by 
  empno desc
</select>

在测试类编写

java 复制代码
public class CURDTest {
    @Test
    public void testSelect() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        SqlSession session = factory.openSession();
    }
}
/**
 * 查询多条记录
* selectList(statement-sql语句的id, parameter-参数)
 * 表示将单条记录都存储输出映射对象中,每条记录的映射对象存放在List集合中
*/
List<Emp> list = session.selectList("emp.select");
for (Emp emp : list) {
    System.out.println(emp);
}

3.2 添加员工

编写mapper文件

xml 复制代码
<!--
添加操作使用insert标签;
增删改操作没有resultType,只有查询有resultType;
因为增删改操作返回值都是int类型,所以我们不需要指明;
注意:给占位符赋值,#{}中编写的内容为实体类型参数中的成员变量名称;
 #{empno}    
Mybatis会从传递过来的参数对象里面得到emono字段的值-->
 <insert id="insert" parameterType="com.gs.entity.Emp">
 insert into emp(ename,job,mgr,hiredate,sal,comm,deptno)
 values(#{ename},#{job},#{mgr},#{hiredate},#{sal},#{comm},#{deptno})
 </insert>

在测试类编写

java 复制代码
public class CURDTest {
    @Test
    public void testSelect() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        SqlSession session = factory.openSession();
    }
}
 Emp emp = new Emp();
 emp.setEname("TOM");
 emp.setJob("CLARK");
 emp.setMgr(1);
 emp.setHiredate(new Date());
 emp.setSal(6500.0);
 emp.setComm(1200.0);
 System.out.println("新增之前的主键值为:" + emp.getEmpno());
 int result = session.insert("emp.insert", emp);
 }
 System.out.println("影响数据库的条数为:" + result);
 /**
 * mybatis中的事务是jdbc的事务机制,mybatis里面默认是手动提交
*/
 session.commit();
 System.out.println("新增之后的主键值为:" + emp.getEmpno());
 session.close();

3.3 更新员工

编写mapper文件

xml 复制代码
<update id="update" parameterType="com.gs.entity.Emp">
 update emp set ename=#{ename},job=#{job},mgr=#{mgr},hiredate=#
 {hiredate},sal=#{sal},comm=#{comm},deptno=#{deptno} where empno=#{empno}
 </update>

在测试类编写

java 复制代码
public class CURDTest {
    @Test
    public void testSelect() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        SqlSession session = factory.openSession();
    }
}
 Emp emp = new Emp();
 emp.setEmpno(7936);
 emp.setEname("JERRY");
 emp.setJob("MANAGER");
 emp.setMgr(7698);
 emp.setHiredate(new Date(new Date().getTime() + 1000*60*60*24));
emp.setSal(7800.0);
 emp.setComm(800.0);
 }
 int result = session.update("emp.update", emp);
 System.out.println("影响数据库的条数为:" + result);
 session.commit();
 session.close();
}

3.4 删除员工

编写mapper文件

xml 复制代码
<delete id="delete" parameterType="java.lang.Integer">
 delete from emp where empno=#{empno}
 </delete>

在测试类编写

java 复制代码
public class CURDTest {
    @Test
    public void testSelect() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        SqlSession session = factory.openSession();
    }
}
 int result = session.delete("emp.delete", 7935);
 System.out.println("影响数据库的条数为:" + result);
 session.commit();
 }
 session.close();
}

3.4 根据员工名模糊查询

编写mapper文件

xml 复制代码
<!--
条件查询:模糊查询
1、#{}占位符,防止sql注入
需要在Java中将传入数据的前后拼接%符号
where ename like #{ename}
 2、使用字符串拼接函数
where ename like concat('%',#{ename},'%')
 3、${}拼接符号,实现sql的拼接
where ename like '%${value}%'
注意:${}不是占位符,如果输入参数为简单类型,${}中的内容必须为value-->
<select id="selectByEname1" parameterType="java.lang.String" 
resultType="com.gs.entity.Emp">
 select empno,ename,job,mgr,hiredate,sal,comm,deptno from emp
 where ename like #{ename}
 </select>

在测试类编写

java 复制代码
public class CURDTest {
    @Test
    public void testSelect() throws IOException {
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        SqlSession session = factory.openSession();
    }
}
  String ename = "S";
 List<Emp> list = session.selectList("emp.selectByEname1", "%"+ename+"%");
 for (Emp emp : list) {
 System.out.println(emp);
 }
 session.close();
}

4 总结

4.1 parameterType和resultType

  1. parameterType:指定输入参数类型,MyBatis通过OGNL从输入对象中获取参数值设置在Sql中。
  2. resultType:指定输出结果类型,MyBatis将Sql查询结果的一行记录数据映射为resultType指定类型的 对象。

4.2 #{} 和 ${}

  1. #{}:
    1. #{}表示一个占位符号,#{}接收输入参数,类型可以是简单类型、实体类类型、HashMap;
    2. #{}接收简单类型,#{}中可以写成value或其它名称;
    3. #{}接收实体类对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属 性值。
  2. ${}:
    1. 表示一个拼接符号,有 S q l 注入风险,所以不建议使用 {}表示一个拼接符号,有Sql注入风险,所以不建议使用 表示一个拼接符号,有Sql注入风险,所以不建议使用{};
    2. ${}接收输入参数,类型可以是简单类型、实体类类型、HashMap;
    3. 接收简单类型, {}接收简单类型, 接收简单类型,{}中只能写成value;
    4. ${}接收实体类对象值,通过OGNL读取对象中的属性值,通过属性.属性.属性...的方式获取对象属 性值。

4.3 selectOne和selectList

  1. selectOne表示查询出一条记录进行映射; 如果使用selectOne可以实现使用selectList也可以实现(list中只有一个对象)。
  2. selectList表示查询出一个列表(多条记录)进行映射; 如果使用selectList查询多条记录,不能使用selectOne。
  3. 如果使用selectOne报错:org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4

4.4 MyBatis和Hibernate区别

  1. Hibernate:是一个标准ORM框架(对象关系映射)。 入门门槛较高的,不需要程序写Sql,Sql语句自动生成,对Sql语句进行优化、修改比较困难的; 应用场景:适用与需求变化不多的中小型项目,比如:后台管理系统,erp、crm、oa...;
  2. MyBatis:专注于Sql本身,需要程序员自己编写Sql语句,Sql修改、优化比较方便。 MyBatis是一个不完全的ORM框架,虽然程序员自己写Sql,MyBatis也可以实现映射(输入映射、 输出映射); 应用场景:适用与需求变化较多的项目,比如:互联网项目;

4.5 Mybatis解决JDBC编程的问题

    1. 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问 题。
    2. 解决:在mybatis-config.xml中配置数据链接池,使用连接池管理数据库链接。
  1. Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变Java代码。
    1. 解决:将 Sql语句配置在XXXXmapper.xml文件中与Java代码分离。
  2. 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数一 一 对应
    1. 解决:Mybatis自动将Java对象映射至sql语句,通过statement中的parameterType定义输入参数 的类型。
  3. 对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo对象解析比较方便。
    1. 解决:Mybatis自动将sql执行结果映射至Java对象,通过statement中的resultType定义输出结果 的类型
相关推荐
m0_748255656 小时前
DuckDB:pg_duckdb集成DuckDB和PostgreSQL实现高效数据分析
数据库·postgresql·数据分析
阿雄不会写代码6 小时前
数据库如何清空重置索引,MySQL PostgreSQL SQLite SQL Server
数据库·mysql·postgresql
猿小喵6 小时前
redo和binlog区别
数据库·mysql
潇湘秦8 小时前
Oracle CDB自动处理表空间不足脚本
数据库·oracle
梓沂8 小时前
Oracle中与 NLS(National Language Support,国家语言支持) 相关的参数
数据库·oracle
angen20188 小时前
mysql 存储过程和自定义函数 详解
数据库·mysql
m0_7482495410 小时前
DRGDIP 2.0时代下基于PostgreSQL的成本管理实践与探索(上)
数据库·postgresql·区块链
q5673152310 小时前
无法在Django 1.6中导入自定义应用
android·开发语言·数据库·django·sqlite
茂桑11 小时前
Redis的数据过期策略和数据淘汰策略
java·数据库·redis
dxt_snow13 小时前
Centos7系统安装redis
数据库·redis·缓存