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定义输出结果 的类型
相关推荐
꧁薄暮꧂20 分钟前
kafka如何知道哪个消费者消费哪个分区?
数据库·分布式·kafka
꧁薄暮꧂24 分钟前
kafka消费者组和分区数之间的关系是怎样的?
数据库·分布式·kafka
年薪丰厚27 分钟前
etcd、kube-apiserver、kube-controller-manager和kube-scheduler有什么区别
数据库·kubernetes·k8s·etcd
ZHOUPUYU2 小时前
最新SQL Server 2022保姆级安装教程【附安装包】
java·数据库·python·sql·sqlserver·php·sqlserver2022
阑梦清川2 小时前
JavaEE进阶-----mybatis操作数据库(新手教程)
数据库·java-ee·mybatis
NLP的小Y2 小时前
Neo4j图形数据库-Cypher中常用指令
数据库·neo4j
Mar_mxs3 小时前
Linux环境下配置neo4j图数据库
linux·数据库·neo4j
小狮子安度因3 小时前
Qt数据库操作-QSqlQueryModel 的使用
数据库·qt·oracle
ABILI .3 小时前
Oracle对比表与表之间的结构
数据库·oracle