MySql+JAVA 笔记

261 阅读14分钟

Sql语言分类:

  • DML 增 删 改 查
  • DCL 用户 权限 事务
  • DDl 逻辑库 数据表 视图 索引

规范

1.Sql语句不区分大小写, 但字符串区分;
2.且必须加上结束语句 "分号"

SELECT "HelloWorld";

CREATE DATABASE 逻辑库名称; # 创建逻辑库
SHOW DATABASES; # 查看有多少逻辑空间
DROP DATABASES 逻辑库名称; # 物理删除逻辑库 

创建数据表


USE test; # 操作test表
CREATE TABLE student(
    id INT UNSIGNED PRIMARY KEY,  # 定义 id 为主键 primaryKey, 且为无符号的整形
    name VARCHAR(20) NOT NULL ,# 定义 name,长度不固定,但是不得超过20 VARCHAR(20) ,必填字段
    sex CHAR(1) NOT NULL, 	# 定义性别 ,长度固定为1 , 非空必填	
    birthday DATE NOT NULL, # 生日非空必填, 类型为日期 必填 
    tel CHAR(11) NOT NULL, # 手机号码固定长度 11 必填 
    remark VARCHAR(200)   #备注 长度小于200 , 非必填
); 
#添加数据
INSERT INTO student VALUES(1,"金城武","男","1977-4-6","13988733645",NULL);

SHOW tables; # 查看有哪些数据表
DESC student; # 查看student 表结构
SHOW CREATE TABLE student; # 查看创建学生表的指令
-- DROP TABLE student; # 彻底删除表
-- 使用 ALTER TABLE 表名 
-- ADD 字段名 约束    来为表添加一些字段
-- CHNAGE oldName newName 来修改字段名称
-- MODIFY 字段名  修改字段属性(长度类型等)
-- DROP 字段名  直接删除字段(删除列)


-- ALTER TABLE student
-- ADD home_address VARCHAR(200) NOT NULL,
-- ADD home_tel VARCHAR(13) NOT NULL
-- 
-- DESC student; 
DESC student; # 查看student 表结构



-- 使用 MODIFY 来修改字段 ,其余与ADD 一致

ALTER TABLE student
MODIFY home_address VARCHAR(100);  # 此次home_address 被设置为非必填,

ALTER TABLE student
CHANGE home_address new_home_address VARCHAR(150) NOT NULL; 

-- ALTER TABLE student  # 删除列名(字段)
-- DROP new_home_address, # 使用上出多个时,使用逗号分隔
-- DROP name; # 使用分号结尾
DESC student; # 查看student 表结构(看看有没有成功)


-- 使用 CHANGE 来为 字段更换名字
-- CHANGE oldName newName 数据类型  [约束] [COMMENT 注释],
CREATE TABLE t_message(
    id INT PRIMARY KEY AUTO_INCREMENT, # 定义为主键并自增
    name VARCHAR(20) NOT NULL UNIQUE, # 名字定义为唯一值(不可重复)
    type ENUM("公告","通报","个人信息") NOT NULL,
    married BOOLEAN NOT NULL DEFAULT FALSE, # 是否结婚, 不可为空, 若不填则默认为false
    INDEX t_type (type) 
);

INSERT INTO t_message VALUES(0,"张三","公告");
INSERT INTO t_message VALUES(1,"李四","个人信息");
INSERT INTO t_message VALUES(2,"王五","公告");
INSERT INTO t_message VALUES(3,"赵六","个人信息");

DROP INDEX t_type on t_message; # 删除索引 - t_type 在 t_message上的
CREATE INDEX index_name on t_message(name); # 创建索引 "index_name" 在表 t_message 的 name 字段上
ALTER TABLE t_message ADD INDEX t_type(type); # 操作数据表 t_message 添加索引 t_type(type 字段)
SHOW INDEX FROM t_message; # 查询t_message的所有数据表


简单的数据查询

SELECT * FROM t_emp;
SELECT empno,sal *12 AS "INCOME" FROM t_emp LIMIT 3,5 ; # 从索引3(第四条)开始查询 紧随的5条数据;
# ORDER BY 字段名 (DESC);  查询出来的结果使用 某字段进行排序, 若添加上DESC 则 倒序. 
# 我们使用 ORDER BY 规定首要的排序条件和次要的排序条件. 数据库会在首要条件相同的情况下,依次比较后续的条件.

-- SELECT  empno, sal FROM  t_emp LIMIT 10 ORDER BY sal DESC, empnoDESC; 


-- 去除查询出来的重复记录 
SELECT DISTINCT job from t_emp;  
-- 注意 : (1) DISTINCT 关键字在查询多列数据的时候 会失效(除非出来的每一条数据都相同)
--       (2) DISTINCT 只能在SELECT字句中使用一次, 且必须紧跟在 SELECT 后面
-- 错误示例 SELECT ename ,DISTINCT job
 
-- WHERE 执行 条件选择  , 可使用 AND 或者 OR 来进行追加筛选 
-- 为了提升性能, 应当将筛选掉记录最多的条件放在最左侧, 依次往右 以减少压力 (重点)
SELECT empno, ename ,sal 
FROM t_emp
WHERE (deptno = 10 OR deptno = 20) AND sal >= 2000;

-- IFNULL( A, B )   使用Sql 自带的 IFNULL 函数 ,如果A为null, 那么就将 B作为返回数据
-- DATEDIFF(expr1, expr2)   使用Sql 自带函数 去比较 expr1 和 expr2 的天数差
-- IN(A,B,c)  包含
-- IS NULL  为空;  comm IS NULL
-- IS NOT NULL 不为; comm IS NOT NULL
-- BETWEEN AND  范围; sal BETWEEN 2000 AND 4000
-- LIKE 模糊查询; ename LIKE "A%" 
-- REGEXP 正则表达式; ename REGEXP "[a-zA-Z]{4}"
-- AND 与关系 age > 18 AND sex = "男"
-- OR  或关系 empno = 8000 OR depino = 20 
-- NOT 非关系 NOT deptno = 20
-- XOR 异或关系 age > 18 XOR sex = "男" (一个成立 一个不成立 就为true ; 否则 为false)

复杂数据查询

-- 聚合函数
-- 在数据的查询分析中, 应用时分管饭. 距和函数可以对数据求和, 求极值, 平均值等

# AVG([DISTINCT] expr) 求平均值
SELECT AVG(sal + IFNULL(comm,0)) AS "AVERAGE" FROM t_emp;

# SUM(expr) 求和
SELECT SUM(sal) FROM t_emp WHERE deptno IN(10, 20); # 求部门编号为10 和 20的部门 的 薪资总和
 
# MAX([DISTINCT] expr)   求最大值
# MIN([DISTINCT] expr)   最小值
SELECT MAX(sal + IFNULL(comm,0)) FROM t_emp WHERE deptno IN(10,20); # 求部门编号为10,20的部门所有员工薪资最高的员工


-- 上述两个sql 可以合二为一: 
SELECT SUM(sal) AS "SUM",MAX(sal + IFNULL(comm,0)) AS "MAX" FROM t_emp WHERE deptno IN(10, 20);

# LENGTH(str) 长度
SELECT  MAX(LENGTH(ename)) from t_emp;

# ROUND(expr) 四舍五入

# COUNT(DISTINCT expr,[expr...]) 获得包含空值的记录数 
SELECT COUNT(*) FROM t_emp; # 返回所有数据的条数 无论是否有效
SELECT COUNT(comm) FROM t_emp; # 返回该列为 null 的数据条数

# 查询10和20部门中, 底薪超过20且工龄超过15年的人数
SELECT COUNT(*) FROM t_emp WHERE deptno IN (10,20) AND sal>2000 AND DATEDIFF(NOW(),hiredate)*365 >15;


-- 数据分组
#查询1985年以后入职的员工, 底薪超过公司平均底薪的员工数量
SELECT COUNT(*) FROM t_emp  where sal > AVG()

查询语句中 ,各种语句的执行顺序

FROM -> WHERE -> GROUP BY -> SELECT -> ORDER By-> LIMIT

from 选择某张表, where 筛选数据 , 被选中的数据交由groupby 进行分组 ,再使用select 的 聚合函数来做汇总计算, 接着使用ORDER By 进行排序, 最终使用LIMIT切割

查询部分平均底薪超过2000的部门编号

-- 传统写法 SELECT deptno FROM t_emp where AVG(sal)>=2000 GROUP BY deptno; -- 上述写法会报错, 因为 where的 优先级会高于 GROUP BY ,这就导致还没有进行分组 就开始筛选. 因此报错 SELECT deptno FROM t_emp GROUP BY deptno HAVING AVG(sal)>=2000;

    -- 传统写法 SELECT deptno FROM t_emp where AVG(sal)>=2000  GROUP BY deptno;
    -- 上述写法会报错, 因为 where的 优先级会高于 GROUP BY ,这就导致还没有进行分组  就开始筛选. 因此报错
    解决方案: 使用HAVING 语句替代, having语句的执行顺序在group By 之后,这样一来数据肯定已经分组完毕了
    SELECT deptno FROM t_emp  GROUP BY deptno HAVING AVG(sal)>=2000;

高级查询

-- 聚合函数
-- 在数据的查询分析中, 应用时分管饭. 距和函数可以对数据求和, 求极值, 平均值等

# AVG([DISTINCT] expr) 求平均值
SELECT AVG(sal + IFNULL(comm,0)) AS "AVERAGE" FROM t_emp;

# SUM(expr) 求和
SELECT SUM(sal) FROM t_emp WHERE deptno IN(10, 20); # 求部门编号为10 和 20的部门 的 薪资总和
 
# MAX([DISTINCT] expr)   求最大值
# MIN([DISTINCT] expr)   最小值
SELECT MAX(sal + IFNULL(comm,0)) FROM t_emp WHERE deptno IN(10,20); # 求部门编号为10,20的部门所有员工薪资最高的员工


-- 上述两个sql 可以合二为一: 
SELECT SUM(sal) AS "SUM",MAX(sal + IFNULL(comm,0)) AS "MAX" FROM t_emp WHERE deptno IN(10, 20);

# LENGTH(str) 长度
SELECT  MAX(LENGTH(ename)) from t_emp;

# ROUND(expr) 四舍五入

# COUNT(DISTINCT expr,[expr...]) 获得包含空值的记录数 
SELECT COUNT(*) FROM t_emp; # 返回所有数据的条数 无论是否有效
SELECT COUNT(comm) FROM t_emp; # 返回该列为 null 的数据条数

# 查询10和20部门中, 底薪超过20且工龄超过15年的人数
-- SELECT COUNT(*) FROM t_emp WHERE deptno IN (10,20) AND sal>2000 AND DATEDIFF(NOW(),hiredate)*365 >15;

--  使用聚合函数的时候,
SELECT deptno,ROUND(AVG(sal))
FROM t_emp GROUP BY deptno;

-- 注意:
-- 查询语句中如果含有 GROUP BY子句 ,那么SELECT 子句中的内容就必须遵守规定:
-- SELECT 子句仲可以包含聚合函数,或者GROUP BY 子句的分组列 , 其余内容均不可以出现在SELECT 子句中

-- 错误示范 错误示范 错误示范 错误示范
SELECT deptno ,AVG(sal),COUNT(*) ,sal FROM t_emp GROUP BY deptno; # 错误示范
-- 错误示范 错误示范 错误示范 错误示范 
-- 因为 聚合函数返回的数据一定是一条数据 ,而类似sal 这种的返回的则是一系列的多条数据, 那么就无法在一条记录里显示, 因此报错了. 

# 查询部分平均底薪超过2000的部门编号
-- 传统写法 SELECT deptno FROM t_emp where AVG(sal)>=2000  GROUP BY deptno;
-- 上述写法会报错, 因为 where的 优先级会高于 GROUP BY ,这就导致还没有进行分组  就开始筛选. 因此报错
SELECT deptno FROM t_emp  GROUP BY deptno HAVING AVG(sal)>=2000;

-- 查询每个部门中, 1981年之后入职的且部门人数大于2 的部门编号,以及名称与薪水
SELECT deptno,GROUP_CONCAT(ename,":",sal)  from t_emp WHERE hiredate >'1981-1-1' GROUP BY deptno HAVING COUNT(*)>2 ORDER BY deptno DESC;
-- 数据分组
#查询1985年以后入职的员工, 底薪超过公司平均底薪的员工数量
-- SELECT COUNT(*) FROM t_emp  where sal > AVG()

-- with ROLLUP 在查询出来的数据末尾处,再添加一条数据, 数据的每一项是当前的 筛选条件

-- select job from t_emp GROUP BY job;

select deptno,GROUP_CONCAT( ename),GROUP_CONCAT( mgr),COUNT(*) from t_emp WHERE sal>2000 GROUP BY deptno;

JAVA

拆箱 装箱

    
     int num = Integer.parseInt("2"); // 转换成 int 类型
     String str = Integer.toString();

当我们使用Integer four = Integer.valueOf(num) 的时候, num刚好处于-128~127 之间的区间, 此时变量会被放置在缓存区中, 因此 :

Integer h1 = Integer.valueOf(100);
Integer h2 = Integer.valueOf(100); // h1 == h2

Integer t1 = Integer.valueOf(200);
Integer t2 = Integer.valueOf(200); // h1 != h2

image.png

JDBC

String dbDriver = "com.mysql.cj.jdbc.Driver"; // JDBC驱动类  (不同版本JDBC驱动类不同)

String dbUrl = "jdbc:mysql://localhost:3306/imooc"; // 链接字符串

String dbUsername = "root"; // 数据库用户名

String dbPassword = "123456"; // 数据库密码

1. 加载并初始化JDBC驱动 Class.forName(dbDriver);

2. 创建数据库连接

Connection connection = DriverManager.getConnection(dbURL, dbUsername, dbPassword);

3. 使用conn 创建 statement 对象 来完成 sql 语句

stmt = conn.createStatement();

4. ResultSet 结果集收集结果 res = stmt.executeQuery("select * from employee where dname = '" + depName + "'");

5. 关闭各种连接

    if (conn != null && !conn.isClosed()) {
        conn.close();
    }
    if (stmt != null) {
        stmt.close();
    }
    if (res != null) {
        res.close();
    }
 

DriverManager

  • 是在jdbc中提供的最重要的一个类, 用于对jdbc的驱动进行注册和管理.
  • DriverManager.getConnection(链接字符串, 用户名, 密码)
  • 返回值为Connection 对象, 对应着数据库的物理网络连接(也对应着java与数据库之间的网络桥梁)

Connection

  • Conection 对象用于JDBC与数据库的网络通信对象
  • java.sql.Connection是一个接口, 具体由驱动厂商实现「一个是接口, 一个是实现」 (存放在java.sql包中,而 java.sql包是jdbc的核心包, 它存放着大量的接口)
  • 所有数据库的操作都建立在Connection基础之上(无论是发送Sql语句,还是接受查询结果,都要建立在此物理接口之上)

MySql连接字符串 「具体格式由厂商自行定义」

  • 格式: jdbc:mysql://「主机ip或域名」「:端口」/数据库名?参数列表
  • 主机ip与端口: 主机ip与端口是可选设置, 默认为:127.0.0.1 与 3306 (不过默认场景并不多见,因为安全考虑我们不会使用默认端口 并且 实际项目中往往数据库独立部署,并不会访问本机)
参数名建议参数值说明
useSSlBoolean (生产用true,开发用false)表示是否禁用ssl. ssl是Secure Sockets Layer 安全套接字协议,配置在服务器上,在网络传输过程中使用非对称加密进行传输
useUnicodetrue启动unicode编码传输数据, 用以规避中文带来的干扰
characterEncodingUTF-8使用utf-8编码传输中文编码
serverTimezoneAsia/Shanghai使用东8时区时间, UTC+8
allowPublicKeyRetrievaltrue允许从客户端获取公钥加密传输

参数列表: 采用url编码

Sql注入

在入参中混入sql语句, 蒙蔽数据库, 使其 返回入侵者希望看到的数据 或 执行入侵者的指令

PreparedStatement

为了阻隔Sql注入而出现

  • 预编译Statement, 是Statement的子接口
    // 进行连接
    Connection conn = DriverManager.getCOnnection("jdbc:mysql://localhost:3306/...","root","root");
    // 连接数据库后, 准备执行 sql 语句
    String sql = "select * from employee where dname =?";
    PreparedStatement  pstmt =  conn.prepareStatement(sql);
    // 填充sql语句中的变量
    pstmt.setString(1, dname); // 1 表示为第一个参数进行赋值  根据传入数据的类型, 来选择使用setString 还是 setDouble 等    
    // 执行sql 并返回结果给ResultSet 对象
    ResultSet res = pstmt.excuteQuery();
    while(res.next()){// do something};
    
    
    // 如果是更新操作, 那么 使用executeUpdate , 来查看更新的条数
    Integer cnt = pstmt.executeUpdate(); 
    if(cnt<1){sout("信息更新失败!")}

反射 reflect

  • 反射是在运行时动态访问类与对象的技术
  • 反射是JDK1.2后提供的高级特性,隶属于java.lang.reflect

Class类

  • Class类是jvm中特定为 "类和接口" 的 类 (在此的释义)
  • Class对象包含了某个类的具体信息
  • 通过Class对象可以获取该类 具体 的构造方法 成员方法 与 成员参数

image.png

Constructor类

  • 用于对java类中构造方法的抽象
  • 能得到构造方法的种类,每种构造方法

image.png

注意

  • 前者class 也有一个newInstance() 方法, 但class 的 newInstance() 指代的是默认构造函数
  • 此处的newInstacne() 指定特定的构造函数, 这代表着我们要传入特定的参数才可以
  • 但是需要先使用 classObj.getConstructor() 得到对应的构造方法对象,接下来再使用newInstance()来调用特定的构造方法 才可以! image.png
package com.imooc.reflect;

import com.imooc.reflect.entity.Employee;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ConstructSample {
    public static void main(String[] args) {
        try { 
            Class employeeClass = Class.forName("com.imooc.reflect.entity.Employee");
//            定义抽象类,并申明参数类型以及参数个数
            Constructor constructor = employeeClass.getConstructor(new Class[]{
                    Integer.class, String.class, Float.class, String.class
            });
            Employee employee = (Employee) constructor.newInstance(new Object[]{10004, "张大炮", 0.5f, "狂魔部"});
            System.out.println(employee.getEname());
        } catch (ClassNotFoundException e) {
//          未找到类, Class.forName("....") 的参数出现问题
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
//          未找到与之对应的格式的方法
            e.printStackTrace();
        } catch (InvocationTargetException e) {
//          当被调用的方法内部抛出了异常而没有被捕获到时 ,就使用 InstantiationException 进行捕获
            e.printStackTrace();
        } catch (InstantiationException e) {
//          实例化异常
            e.printStackTrace();
        } catch (IllegalAccessException e) {
//          权限拒绝访问, 比如调用私有的成员变量或方法
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return super.toString();
    }
}

Field成员变量类

  • Field 对应某个类中成员变量的申明
  • Filed对象可使用类对象.getFiled()方法获取 : classObj.getField()
  • Field对象可在运行时使用对某个对象的成员变量进行赋值或取值 (核心点)

image.png

getDeclared 系列方法

  • getDeclearedConstructor(s)|Method(s)|FIled(s) (s)表示获取集合->构造方法集合,方法集合,属性集合

反射的使用(概念代码)

image.png

lambda 表达式(匿名)

package com.imooc.lambda.com.imooc.lambada;

import com.imooc.lambda.MathOperation;

public class LambdaSample {
    public static void main(String[] args) {
        /**
         * lambda 约束条件(Lambda表达式只能实现有且只有一个抽象方法的接口, java称之为"函数式接口")
         * 有很多种 下面一一举列
         * 1. 标准式
         * 2. 忽略参数类型模式
         * 3. 单行模式省略大括号与return (若参数只有一枚, 那么装载参数的括号也可以直接省略)
         * */
//        标准式
        MathOperation adition = (Integer a, Integer b) -> {
            return a + b + 0f;
        };
        System.out.println(adition.operate(5, 6));
/**         不使用lambda的模式
 *         class Adition implements MathOperation{
 *
 *             @Override
 *             public Float operate(Integer a, Integer b) {
 *                 return a+b+0f;
 *             }
 *         };
 *
 *         Adition aditionObj = new Adition();
 *         System.out.println(aditionObj.operate(5,9));
 * */


//   2. 忽略参数类型模式
        MathOperation substraction = (a, b) -> {
            return a - b + 0f;
        };
        System.out.println("忽略参数模式 :"+substraction.operate(3,4));



//   3. 单行模式
        MathOperation multiplication = (a,b)->a*b+0f;
        System.out.println( "单行模式 :"+multiplication.operate(4,5));
    }
}

函数式编程

  • 基于函数式接口 并使用lambda表达的 编程方式
  • 函数式编程理念是将代码作为可重用数据代入到程序里面
  • 函数式编程侧重于要做什么,而不是怎么做

核心就是依托于函数接口, 将代码片段作为判断条件,运行时动态嵌入程序, 使得程序更加灵活

package com.imooc.lambda;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;


public class PredicateSample {
    public static void main(String[] args) {
        Predicate<String> predicate = str->str.length()>3;
//        System.out.println(predicate.test("99999"));
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
        PredicateSample.filter(list,num->num%2==0);

    }

    public  static void filter(List<Integer> list, Predicate<Integer> predicate){
        for(Integer item : list){
            System.out.println(predicate.test(item));
        }
    }
}

函数式接口

  • 函数式接口是有且只有一个抽象方法的接口
  • java中用有大量函数式接口, 如 java.lang.Runnable
  • JDK8后提供了一系列新的函数式接口位于 java.util.function

image.png

Consumer 接口只是传入一个参数, 在内部定义具体的业务逻辑去操作, 纯工具人一个  -.- 汗颜...
package com.imooc.lambda;

import java.util.Random;
import java.util.function.Function;

public class FunctionSample {
    public static void main(String[] args) {
        Function<Integer, String>  randomStr = str->{
//          创建可动态修改长度的 stringBuffer 对象字符串
          StringBuffer strbuf = new StringBuffer();
//          定义 用于取随机数据的字符串
          String chars = "1234567890qwertyuiop";
          Random random = new Random();
//          str 是 传入的长度(希望返回多长的字符串)
            for (int i = 0; i < str; i++) {
                int index = random.nextInt(chars.length());
//                字符串拼接
                strbuf.append(chars.charAt(index));
            }
//            返回处理完毕的字符串
            return strbuf.toString();
        };

//        使用function方法, 生成 N 位长度的随机字符串 (N 自行传入)
        String result = randomStr.apply(16);
        System.out.println(result);
    }
}
Tips: 可以使用@FunctionalInterface 来告知编译器,此class为一个函数式接口,令其在静态编译的时候检查抽象方法是否合法

image.png

stream 流(有点js链式调用的意思)

image.png

image.png 示例

image.png

文件流

image.png

image.png

SqlSessionFactory

  • SqlSessionFactory 是MyBatis 的核心对象
  • 用于初始化MyBatis, 创建SqlSession对象
  • 保证SqlSession在应用中唯一
package com.imooc.mybatis;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;

public class MyBatisTestor {
    @Test

    public void testSqlSessionFactory() throws IOException {

//        文本文件按照字符流的方式读取 mybatis-config.xml
//        会默认从当前classPath 类路径 下 去加载xml 文件

        Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
//            初始化SqlSessionFactory 对象, 同时解析mybatis-config.XML 文件
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        System.out.println("sqlSessionFactory加载成功");
//          创建SqlSession对象, SqlSession是JDBC扩展类, 用于数据库交互
        SqlSession sqlSession = null;
        try {
//            sqlSessionFactory.openSession - 创建一个session对象
            sqlSession = sqlSessionFactory.openSession();
            // 创建数据库连接(测试使用)   sqlSession.getConnection 得到底层的数据库对象
            Connection connection = sqlSession.getConnection();
            System.out.println(connection);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null) {
//                注意 在myBatis-config 中, 如果设置
                /**
                 * 注意 在myBatis-config 中, 如果设置 <dataSource type="POOLED">
                 *     则代表使用连接池,  close则是将连接会收到连接池中
                 *
                 * 而type="POLLED" ,代表使用直连, close会直接调用Connection.close() 方法关闭连接
                 * */
                sqlSession.close();
            }
        }
    }
}

image.png

Mybatis 写入数据

说起写操作,自然离不开数据库事务; 事务的存在实际上是 保证数据库的完整性 的基础.在客户端操作后, 所有操作会临时存放在日志中,直到客户端继续发起commit操作,那么日志中的操作才会真正写入到数据库. 只要有一条操作没有成功, 那么就会执行rollBack, 一旦rollBack, 所有存放在日志中的临时操作, 都会被清空.

image.png

写操作有三种
image.png

新增较为复杂的一点是: 主键Id是自增的,这就意味着我们无法直接将primaryId赋值, 所以我们需要在 insert 标签内部,写一个selectKey, 其有一个select last_insert_id()方法, 调用其来获取最新加入对象的Id,再赋值给我们自己定义的类对象中.从而得到一个完整的对象

image.png

selectKey 与 useGenerateKeys 的区别

<!--    inert标签并无resultMap 与 resultType对象, 因为只是写数据,不必关注也没有返回的类型-->
    <insert id="insert" parameterType="com.imooc.mybatis.entity.Goods">
        INSERT INTO t_goods(title,sub_title,original_cost,current_price,discount,is_free_delivery)
        VALUES(#{title},#{subTitle},#{originalCost},#{currentPrice},#{discount},#{isFreeDelivery})
        -- 进行主键回添 after 代表sql语句执行后再进行selectKey 的操作
        <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
            select last_insert_id()
        </selectKey>
    </insert>

<!--    相应的 除了selectKey之外, 还有一个 useGeneratedKeys 我们需要如此操作
         <insert id="insert"
            parameterType="com.imooc.mybatis.entity.Goods"
            useGenerateKey="true"  // 是否自动获取主键 , 默认为false
            keyProperty="goodsId"  // 为哪个属性获取
            keyColumn="goods_id"   // 表中的字段名
         >
                INSERT INTO t_goods(title,sub_title,original_cost,current_price,discount,is_free_delivery)
                VALUES(#{title},#{subTitle},#{originalCost},#{currentPrice},#{discount},#{isFreeDelivery})
        </insert>
-->

1.显式与隐式

  • selectKey标签需要明确编写 获取主键的sql语句 -> select last_insert_id()
  • useGeneratedKeys属性会自动根据驱动生成对应的SQL语句 2.场景
  • selectKey标签 支持所有关系型数据库
  • useGeneratedKeys 只支持 "自增主键" 类型的数据库

3.总结

  • selectKey是通用方案, 适合所有数据库, 但编写麻烦(因为每一种数据库获取的语句都是不同的,一旦发生数据迁移,那么所有的selectKey语句都要发生变化)
  • useGeneratedKeys 只支持自增数据库, 但使用简单(只需要简单配置, 就会根据驱动类型来完成相应操作)

更新与删除

image.png

image.png