【database1】mysql:DDL/DML/DQL,外键约束/多表/子查询,事务/连接池

文章目录


1.mysql安装:存储:集合(内存:临时),IO流(硬盘:持久化)

1.1 服务端:双击mysql-installer-community-5.6.22.0.msi

mysql安装包:链接:https://pan.baidu.com/s/18Ctus6BLVrECZP0W-QKtfw 提取码:94s9。



上面next,execute安装后,下面开始配置。



1.2 客户端:命令行输入mysql -u...实际是如下安装路径的bin文件夹的mysql.exe

命令行java -version要配环境变量,看mysql有没有运行,只能从任务管理器看有没有mysql的进程。

win下cmd输入:完整版没有括号:mysql -h(host) 127.0.0.1 -P(port) 3306 -u(username) root -p(password)。简略版:mysql -u root -p。

可视化工具navicat链接:https://pan.baidu.com/s/11qAEA7yEGSIz6J9GIC_S6A 提取码:1w9n。navicat免安装链接:https://pan.baidu.com/s/1zanhYyyNsUXajI-czfW7KA 提取码:om5g。

2.DDL/DML:char不用动态变化,效率高

如下中间蓝框里一个数据库相当于一个excel表,紫色框里一个table相当于excel表里的一个sheet。use数据库相当于双击打开excel表格,select查看函数返回值,desc全称description,like复制表结构,change 旧字段 新字段 新类型

数据库名字不建议修改,用数据库备份改名(新建一个数据库,把原数据库数据复制过来)。mysql中utf-8中-无法识别,用utf8。ISO-8859-1用latin1。

注意(),逗号,分号。

如下数(整小)日字,oracle两个不同:number包含整小,varchar2。

选用timestamp而不用datetime,因为datetime不会自动设置时间。

int(11)默认11位不用写出来,2147483647即21亿10位,如果有负数还有最前面一位是符号位,所以一共11位

如下是表记录,select非常多,删除只是记录,表结构还在。

DDL后面不需要加from等词。

如下单引号可加可不加。

如下要写两个add。





3.DQL:select(相当于System.out)第一个但在倒数第二执行

如下3中第一排序字段相同的里面再进行第二排序。



如下查询id是1或3或5的学生。


d低下降,只要有数字都比null大。


如下有英语成绩有6个,不能代表总人数,缺考没算入。

select后的第一个字段要和group by后同。


如下是select语句执行顺序,红字书写顺序必须这样。

sql只有一种情况报错:语法写错。如下索引越界不会报错。

page和count是前端传来的两个参数,count不用变。

数量(count(*))降序只要一个(limit 1)。

如下将db3数据库整个连数据备份,本质create,insert。记住mysqldump和source。

4.约束和自增长:字段约束(字段的赋值规范)/主键约束(唯一非空且一张表只有一个主键)


如下验证主键唯一和非空,如下两个框都报错。







如下的Null列表示是否允许为空。

5.单表缺点和外键约束:多从外 引出

多表是外键约束的前提,外键约束解决下面多表的2个问题,add...多了一条线。alter table emploee drop foreign key fk_001。

如上是在已有表添加外键,如下建表就添加(开发中常用)。

如果要删除department表的id为001即整个研发部门,需要先在employee表中删除id=001的很多记录,很麻烦。现在想要删除department的001部门时employee为001的行也一起删了,所以用外键级联操作,如下create table ...中省略同上。

6.三种多表关系:中间表两外键:一对一(简历),一对多(员工,部门),多对多(学生,课表)





7.多表查询:表...join表on

sql 复制代码
CREATE DATABASE day03;
USE day03;
-- 创建部门表
CREATE TABLE dept (
  id INT PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR(20)
);
INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');
-- 创建员工表
CREATE TABLE emp ( 
  id INT PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR(10),
  gender CHAR(1),   -- 性别
  salary DOUBLE,   -- 工资
  join_date DATE,  -- 入职日期
  dept_id INT
);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013-02-24',1);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010-12-02',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);

如下要查出有用的记录需要emp.dept_id(emp外键)=dept.id(dept主键)。


因为select * ,所以将两个橙色横框拼接起来,棕色框。

7.1 外连接:左外=左表+内连接

如下注意记住左表不动。

如下第一个内连接没有销售部(销售部没有和员工表任何数据相交),第二个有销售部。


union all不去重,左外连接+右外连接。

8.子查询:优先于主查询产生结果

如下执行两句sql,要连接两次数据库,子查询一般要写在()里。

如下1,2是dept_id,可以把in换成 = any

如下第一行和第二行效果相同。

如下两个select等价。

如下还是写上面的题目,如果用内连接,沙僧会没有,左外或右外连接都可以。

如下将上面的id1列去除(即将dept表的id列去除不显示)。

on比where先执行。

sql 复制代码
-- 11111111111111111111111111111111111111111111111111111111111111111部门表
CREATE TABLE dept (
  id INT PRIMARY KEY PRIMARY KEY, -- 部门id
  dname VARCHAR(50), -- 部门名称
  loc VARCHAR(50) -- 部门位置
);
-- 添加4个部门
INSERT INTO dept(id,dname,loc) VALUES 
(10,'教研部','北京'),
(20,'学工部','上海'),
(30,'销售部','广州'),
(40,'财务部','深圳');

-- 1111111111111111111111111111111111111111111111111111111职务表,职务名称,职务描述
CREATE TABLE job (
  id INT PRIMARY KEY,
  jname VARCHAR(20),
  description VARCHAR(50)
);
-- 添加4个职务
INSERT INTO job (id, jname, description) VALUES
(1, '董事长', '管理整个公司,接单'),
(2, '经理', '管理部门员工'),
(3, '销售员', '向客人推销产品'),
(4, '文员', '使用办公软件');

-- 11111111111111111111111111111111111111111111111111111111111111111111员工表
CREATE TABLE emp (
  id INT PRIMARY KEY, -- 员工id
  ename VARCHAR(50), -- 员工姓名
  job_id INT, -- 职务id
  mgr INT , -- 上级领导
  joindate DATE, -- 入职日期
  salary DECIMAL(7,2), -- 工资
  bonus DECIMAL(7,2), -- 奖金
  dept_id INT, -- 所在部门编号
  CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
  CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
);
-- 添加员工
INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES 
(1001,'孙悟空',4,1004,'2000-12-17','8000.00',NULL,20),
(1002,'卢俊义',3,1006,'2001-02-20','16000.00','3000.00',30),
(1003,'林冲',3,1006,'2001-02-22','12500.00','5000.00',30),
(1004,'唐僧',2,1009,'2001-04-02','29750.00',NULL,20),
(1005,'李逵',4,1006,'2001-09-28','12500.00','14000.00',30),
(1006,'宋江',2,1009,'2001-05-01','28500.00',NULL,30),
(1007,'刘备',2,1009,'2001-09-01','24500.00',NULL,10),
(1008,'猪八戒',4,1004,'2007-04-19','30000.00',NULL,20),
(1009,'罗贯中',1,NULL,'2001-11-17','50000.00',NULL,10),
(1010,'吴用',3,1006,'2001-09-08','15000.00','0.00',30),
(1011,'沙僧',4,1004,'2007-05-23','11000.00',NULL,20),
(1012,'李逵',4,1006,'2001-12-03','9500.00',NULL,30),
(1013,'小白龙',4,1004,'2001-12-03','30000.00',NULL,20),
(1014,'关羽',4,1007,'2002-01-23','13000.00',NULL,10);

-- 1111111111111111111111111111111111111111111111111111111111111111工资等级表
CREATE TABLE salarygrade (
  grade INT PRIMARY KEY,
  losalary INT,
  hisalary INT
);
-- 添加5个工资等级
INSERT INTO salarygrade(grade,losalary,hisalary) VALUES 
(1,7000,12000),
(2,12010,14000),
(3,14010,20000),
(4,20010,30000),
(5,30010,99990);




9.事务:一组操作,要么同时成功,要么同时失败





如下若在执行一次sql前执行一次start transaction,若不commit,刷新数据库不显示。

10.四个接口:左java.sql包下的DCSR,右com.mysql.jdbc.Driver


如下mchange. .和c3p0. .一起。第一个导入的是mysql-connector...。

如下代码第一行new Driver()是导入com.mysql.jdbc(用mysql实现好的,就是上面导入的mysql-connector..jar包)而不是java.sql(自己不会重写抽象方法)下,参数是接口类型需要传入接口的实现类对象即new Driver()。registerDriver相当于set方法,get获取的是mysql.Driver.connect方法返回的Connection类即com.mysql.jdbc.JDBC4Connection(有mysql.的都是导入的jar包)。

加载DriverManager这个类用到打破双亲类加载:DriverManager是jdk自带的类,DriverManager类使用的是bootstrap引用类加载器。数据库是用户类用bootstrap加载不合适,所以DriverManager去加载h2的Driver需要把当前引用类加载器替换为当前系统或当前线程的应用app类加载器

java 复制代码
package com.itheima01.jdbc;
import com.mysql.jdbc.Driver;
import java.sql.*;

public class JdbcDemo { 
    public static void main(String[] args) throws SQLException, ClassNotFoundException, IllegalAccessException, InstantiationException {
        //1. 注册驱动 (注意: 导入的mysql的驱动)
        /*
        *   A. 查看mysql.Driver源码: 点new Driver()中Driver看源码
        *       发现static 代码块里 已经注册了驱动 -> 驱动自注册 相当于set一次就行
        *       带来问题: 外部的注册没有意义(重复注册)
        * 
        *       解决: 保证mysql.Driver类被加载(静态代码块就会执行),如下两种方案:
        *          1. 创建对象
        *          2. 反射:a. 节省内存
        *                  b. 跟驱动包的关联只剩一个字符串:"com.mysql.jdbc.Driver"
        *          待会将字符串写入配置文件,只要改配置文件就行 就会跟 mysql驱动包的彻底解耦 
        */

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111
// DriverManager.registerDriver(new Driver()); //这行重复注册,下行new了就会加载 源码里的静态代码块,所以这行=下行

//new Driver(); // Class对象 + 普通实例 //只要用了这个类,这个类就会被加载内存中方法区 //自动导包
        // new Driver()的内存消耗等价于下面两行: 其实只需要calss对象,不需要实例                  
		// Class<?> clazz = Class.forName("com.mysql.jdbc.Driver"); //获取这个类的class对象
		// Object obj = clazz.newInstance(); //利用class对象调用其空参构造来创建一个实例

        Class.forName("com.mysql.jdbc.Driver"); //获取Class对象,没有普通实例,因为普通实例没有意义 //用反射,com.mysql.jdbc.Driver这个类也会被加载

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111
		 //2. 获取连接
        /*
        *   只要涉及两个软件通信 : 网络三要素(必要非充分:一定要,但是有它们三不一定够如多了资源位置)
        *       1. 协议 :       jdbc:mysql  (主协议:子协议)
        *       2. ip :         数据库所在的计算机(自己:localhost或127.0.0.1)
        *       3. port :       mysql数据库3306
        *       资源位置:   数据仓库的名称
        *       
        *       协议://ip:port/资源位置
        *       https://www.baidu.com:443
        */
//        String url = "jdbc:mysql://localhost:3306/day03";  //day03是数据库
        String url = "jdbc:mysql:///day03"; //ip:localhost port:3306 可以省略
        String user = "root";
        String pwd = "1234";
        Connection conn = DriverManager.getConnection(url, user, pwd);
        System.out.println("conn:" + conn); //引用类型打印地址
        
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
		 //3. 创建执行sql的语句对象
        /*
        * Connection 接口的一些方法
        *   <1>. Statement createStatement();   创建执行sql的语句对象,相当于创建一个流
        *   <2>. PreparedStatement prepareStatement(sql); 创建一个预编译sql的语句对象
        *   <3>. 事务操作相关
        */
        Statement statement = conn.createStatement();
        System.out.println("statement:" + statement);
        
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
		  //4. 执行sql,返回结果
		 /*
        * Statetment 接口的api
        *   1. ResultSet statement.executeQuery(sql);
        *           执行的查询语句 : DQL
        *           返回的查询结果: 结果集
        * 
        *   2. int executeUpdate(sql) :
        *           执行的增删改语句: DML
        *           返回的结果: 被影响的行数
        * 
        *   3. boolean execute(sql);  -> 不需要掌握,知道即可
        *               万能 :  DDL等 如create成功或失败是和异常相关,和返回值无关
        *               返回值: 非查询语句返回false,查询语句返回true
        */
        String sql = "select * from emp";
        ResultSet resultSet = statement.executeQuery(sql);
        
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111 
		//5. 处理结果
        while(resultSet.next()){
            String name = resultSet.getString("name");            
// String id = resultSet.getString("id"); //也可以,java程序以外的所有数据对java来说都是字符串
            int id = resultSet.getInt("id"); //底层先调用getString再parse int
            System.out.println(id+ ":" + name);
        }
 
//11111111111111111111111111111111111111111111111111111111111111111111111111111111111
		 //6. 释放资源
        resultSet.close();
        statement.close();
        conn.close();
    }
}

resultset不是返回一行数据,而是带有id=...。因为返回一行数据如 11孙悟空男,不知道怎么解析。

如下hasNext和next区别。

11.释放资源:finally,工具类封装

java 复制代码
package com.itheima04.release;
import com.itheima05.utils.JdbcUtil;
import java.io.Closeable;
import java.io.IOException;
import java.sql.*;

public class ReleaseDemo {
    public static void main(String[] args)  {
        ResultSet resultSet = null;
        Statement statement = null;
        Connection conn = null;
        try {
            /*Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/day03";
            String user = "root";
            String pwd = "1234";
            conn = DriverManager.getConnection(url, user, pwd);*/
            
            conn = JdbcUtil.getConnection(); //查询要获取连接,getConnection方法调用多遍,所以getConnection方法不写try catch,查询提示 查询失败,删除提示 删除失败,封装时不知道是查询还是删除,不好提示,所以往外抛。
            conn = JdbcUtil.getConnection(); //增删改也要获取连接

//1111111111111111111111111111111111111111111111111111111111111111111111111            
            statement = conn.createStatement();
            String sql = "select * from emp"; 
                                   
            resultSet = statement.executeQuery(sql);            
            while(resultSet.next()){
                String name = resultSet.getString("name");
                System.out.println(name);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally{
           /* if(resultSet != null){
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(statement != null){
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }*/
            
		   // closeIo(resultSet,statement,conn);
            JdbcUtil.release(conn,statement,resultSet);
        }
    }

//1111111111111111111111111111111111111111111111111111111111111111111111
    private static void closeIo(AutoCloseable... ios) { //AutoCloseable接口位于java.lang包下,不用导包
        for (AutoCloseable io : ios) {
            if(io != null){
                try {
                    io.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
java 复制代码
package com.itheima05.utils;
import java.sql.*;
/**
*   工具类:  0. 拥有很多 工具方法(重复的代码封装) 的类
*           	命名规范: utils 包 -> xxUtil 类 (xx : 某个模块的名称)
*          		Objects,Arrays,Collections...(JDK提供的)
* 
*          1. 一般工具类中方法是静态的,不用实例化,节省内存
* 
*      	   2. 封装方法的步骤
*         	  1. 先把要把封装的代码写出来
*        	  2. 观察不断重复的部分
*         	  3. 定义方法,然后直接复制过来
*         	  4. 设置参数和返回值
* 
*       注意点: 1. 扩展性 : 不要导入mysql包中的类, 要导入java包中的类(这样换成oracle也可用)
*               2. 工具类中的异常一般是往外抛 : 一般异常是要在业务中处理
*/
public class JdbcUtil {
    static{
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

//1111111111111111111111111111111111111111111111111111111111111111111111111111111
    public static Connection getConnection() throws SQLException {
        //此方法会被多次调用,注册驱动只需要一次 -> 所以用静态代码块 如上
//        Class.forName("com.mysql.jdbc.Driver");
        String url = "jdbc:mysql://localhost:3306/day03";
        String user = "root";
        String pwd = "1234";
        Connection conn = DriverManager.getConnection(url, user, pwd);
        return conn;
    }
    
//1111111111111111111111111111111111111111111111111111111111111111111111111111111
    /**
     * 文档注释: a. 写在类上面 : 描述类的用途
     *      	 b. 写在方法上面 : 描述方法的用途 (返回值,参数)
     */
   public static void release(Connection conn, Statement statement, ResultSet resultSet){
   	   //java.sql.Connection
       if(resultSet != null){
           try {
               resultSet.close();
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
       if(statement != null){
           try {
               statement.close();
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
       if(conn != null){
           try {
               conn.close();
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
   }
}

如下改进上面工具类。

java 复制代码
/*
*   问题: 驱动,url,用户名和密码等信息 是有可能会变的, 变动频率比较低
*       1. 如果不变,直接写死在代码中
*       2. 变,但是频率高 : 一般设置成参数
*       3. 变,但是频率不高: 放在配置文件
*           1. 解耦 : 信息要改变的话,只要改配置文件,代码不用改,程序不需要重新编译和部署
*           2. 代码简化 : 无需调用的时候传参了
*/
java 复制代码
//jdbc.properties文件,每个月改一次 //文件里没有关键字,也没有双引号,本来就是字符串
driverName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/day03
user = root
pwd = 1234
java 复制代码
package com.itheima05.utils;
import java.io.FileInputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtil02 {
    static String driverName;
    static String url;
    static String user;
    static String pwd;
    static{
        try {
            Properties p = new Properties();
            p.load(new FileInputStream("src/jdbc.properties"));
                        
            driverName = p.getProperty("driverName");
            url = p.getProperty("url");
            user = p.getProperty("user");
            pwd = p.getProperty("pwd");
            
            Class.forName(driverName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }   

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111
    public static Connection getConnection() throws SQLException {
        Connection conn = DriverManager.getConnection(url, user, pwd);
        return conn;
    }

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111 
   public static void release(Connection conn, Statement statement, ResultSet resultSet){
       if(resultSet != null){
           try {
               resultSet.close();
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
       if(statement != null){
           try {
               statement.close();
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
       if(conn != null){
           try {
               conn.close();
           } catch (SQLException e) {
               e.printStackTrace();
           }
       }
   }
}

12.JDBC事务操作:conn.setAutoCommit(false)

java 复制代码
package com.itheima01.transaction;
import com.itheima.utils.JdbcUtil;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
// JDBC : 写一个转账案例
public class Demo {
    public static void main(String[] args)  {       
        System.out.println("请输入转出的账户:"); 
        Scanner sc = new Scanner(System.in); // 控制台: 模拟页面
        String outUser = sc.nextLine();
                
        System.out.println("请输入转入的账户:");
        String inUser = sc.nextLine();   
             
        System.out.println("请输入转账的金额:");
        double money = sc.nextDouble();
        
       //sql里面最好写单引号 , 1000和jack改为 两个双引和两个+号
       //String sql1 = "update account set money = money-1000 where name = 'jack'";
        String sql1 = "update account set money = money-"+money+" where name = '"+outUser+"'";
        String sql2 = "update account set money = money+"+money+" where name = '"+inUser+"'";

//11111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        Connection conn = null;
        try {
                /*
                *   事务操作: Connection
                *       1. setAutoCommit(false); 开启事务
                *       2. commit(); 提交事务
                *       3. rollback(); 事务回滚
                *   注意点: 在事务中, 开启事务的连接才具有手动提交事务的功能
                *           一组操作都必须要同一个 连接conn  要执行
                */            
            conn = JdbcUtil.getConnection(); //访问数据库,try外面定义conn
 		   // Connection conn2 = JdbcUtil.getConnection();      		        
            conn.setAutoCommit(false); //开启事务,禁止自动提交  

            Statement statement = conn.createStatement(); 
            statement.executeUpdate(sql1);   //转出                      
            // int i = 1/0;  // ArithmeticException 算术异常 模拟银行爆炸                
            Statement statement2 = conn.createStatement(); 
            statement2.executeUpdate(sql2);   //转入

//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111                  			
            conn.commit(); //提交事务(和事务回滚只有其一执行) 
            System.out.println("转账成功~~");
        } catch (Exception e) { // 注意: 提升异常级别(用于捕获算术异常)
            e.printStackTrace();
            if(conn != null){ //Connection conn放外面,这边访问的到
                try {
                    conn.rollback(); //事务回滚
                } catch (SQLException e1) {
                    e1.printStackTrace();
                }
            }
            System.out.println("转账失败");
        }
    }
}


13.登陆案例预编译改造:PreparedStatement,setString,executeQuery

java 复制代码
package com.itheima02.login;
import com.itheima.utils.JdbcUtil;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/*
    前提: 用户能够登录,说明已经注册过了
         注册成功的时候, 程序会把用户的信息保存到数据库
          
*   登录案例: 逻辑: 请输入用户名和密码(用户)
*            我们: 校验数据库
*            sql :  select * from account where name = ? and pwd = ?; (name用户名唯一)
*           预测结果:  1. 0条 : 用户名不存在或密码错误
*                     2. 1条 : 登录成功
*/
public class LoginDemo {
    public static void main(String[] args) throws SQLException {
        System.out.println("请输入用户名:");
        Scanner sc = new Scanner(System.in);
        String name = sc.nextLine();  
              
        System.out.println("请输入密码:");
        String pwd = sc.nextLine();
        
        String sql = "select * from account where name = '"+name+"' and pwd = '"+pwd+"'";
        System.out.println(sql); //将参数(上面键盘输入)直接拼接进sql
        
//1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
        Connection conn = JdbcUtil.getConnection();
        Statement statement = conn.createStatement();           
        ResultSet resultSet = statement.executeQuery(sql); //将sql发送给数据库去处理
        if(resultSet.next()){ //有一条则为true
            System.out.println("登录成功~~");
        }else{
            System.out.println("用户名不存在或密码错误");
        }
    }
}

根据(用户名和密码)或(1=1永真)为条件查询数据库,what可以随便写。

预编译知道了sql的语法结构了,已经把关键字认全了,后面再包含关键字or就不认了(当成字符串处理),可以防止sql注入

java 复制代码
package com.itheima02.login;
import com.itheima.utils.JdbcUtil;
import java.sql.*;
import java.util.Scanner;

public class LoginDemo02 {
    public static void main(String[] args) throws SQLException {
        System.out.println("请输入用户名:");
        Scanner sc = new Scanner(System.in);
        String name = sc.nextLine();  
        System.out.println("请输入密码:");
        String pwd = sc.nextLine();

            // 1. 改造sql       
        String sql = "select * from account where name = ? and pwd = ?";
        System.out.println(sql);
        Connection conn = JdbcUtil.getConnection();

           // 2. 预编译sql   // PreparedStatement 是 Statement的子接口
        PreparedStatement statement = conn.prepareStatement(sql);

          // 3. 设置参数
          // setString(int parameterIndex, String x)
          // parameterIndex : sql中的?的索引(从1开始,从左开始)  // String x: 参数
        statement.setString(1,name);
        statement.setString(2,pwd);

          // 4. 传参执行
        ResultSet resultSet = statement.executeQuery();
        if(resultSet.next()){
            System.out.println("登录成功~~");
        }else{
            System.out.println("用户名不存在或密码错误");
        }
    }
}

如下手动在数据库中增加一行。

java 复制代码
package com.itheima03.prepare;
import com.itheima.utils.JdbcUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
/*
*   预编译的好处:  1. 防止sql注入
*                 2. 阅读性强(sql)
*                 3. 批量处理sql,效率高 (节省了sql重复编译过程)
*/
public class PrepareDemo {
    public static void main(String[] args) throws SQLException {
//        method01(); //用预编译,如下不用预编译
        String name = "ww";
        int money = 200;
        String sql1 = "insert into account values(null,'"+name+"',"+money+",null)";

        String name2 = "www";
        int money2 = 2002;
        String sql2 = "insert into account values(null,'"+name2+"',"+money2+",null)";     

        Connection conn = JdbcUtil.getConnection();
        Statement statement = conn.createStatement(); //不用预编译
        statement.executeUpdate(sql1);// 编译+运行
        statement.executeUpdate(sql2);//编译+运行,和上行一共两次编译        
        statement.close();
        conn.close();
    }

//1111111111111111111111111111111111111111111111111111111111111111111111111
    private static void method01() throws SQLException {
        String sql = "insert into account values(null,?,?,null)";        
        Connection conn = JdbcUtil.getConnection();
        PreparedStatement pstm = conn.prepareStatement(sql);   
              
        pstm.setString(1,"zs");
        pstm.setDouble(2,1000);
        pstm.executeUpdate();//运行
        
        pstm.setString(1,"ls");
        pstm.setDouble(2,2000);
        pstm.executeUpdate();//运行        
        pstm.close();
        conn.close();
    }
}

14.c3p0连接池:jdbc2.0才引进连接池,不是线程池(连接池的技术标准就是DataSource替代DriverManager)

java 复制代码
package com.itheima04.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class C3p0Demo {  
    public static void main(String[] args) throws PropertyVetoException, SQLException {
//        method01(); //这行方法和下面xml文件无关
        /*
        * 配置文件方式 :   默认情况下, c3p0将会在 类加载器路径(在当前的工程下, src路径)下
        *     寻找一个名为c3p0-config.xml 配置文件(xml文件是复杂的Properties文件,也是key-value)
        * 
        *   套路: 1. 在src下创建名为c3p0-config.xml配置文件(内容直接复制)
        *        2. 创建ComboPooledDataSource核心类      
        */
        ComboPooledDataSource cpds = new ComboPooledDataSource(); 
    //1. 底层自动会去类加载器路径(写代码:src下) 去寻找一个名为c3p0-config.xml 配置文件
   // 2. 自动解析: 读取xml中配置信息 , 设置给c3p0即cpds    

        String sql = "select * from account";  // 同下面              
        Connection conn = cpds.getConnection();  
        PreparedStatement pstm = conn.prepareStatement(sql);
        ResultSet resultSet = pstm.executeQuery();
        while(resultSet.next()){
            String name = resultSet.getString("name");
            System.out.println(name + "--");
        }
        conn.close();// 将连接还给连接池
        cpds.close(); // 销毁
    }

//111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
    private static void method01() throws PropertyVetoException, SQLException {
        // 如下硬编码: 用代码来实现参数的设置。一般不用硬编码,用配置文件
        ComboPooledDataSource cpds = new ComboPooledDataSource();//ComboPooledDataSource是DataSource实现类
        cpds.setDriverClass( "com.mysql.jdbc.Driver"); //需要mysql-connector-java-x.x.x-bin.jar
        cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/day05" );
        cpds.setUser("root");
        cpds.setPassword("1234");
        
        String sql = "select * from account";              
        Connection conn = cpds.getConnection();   
        PreparedStatement pstm = conn.prepareStatement(sql);
        ResultSet resultSet = pstm.executeQuery();
        while(resultSet.next()){
            String name = resultSet.getString("name");
            System.out.println(name); //System.err.println(name);//红色
        }
        conn.close();// 将连接还给连接池
        cpds.close(); // 销毁
    }
}
xml 复制代码
//c3p0-config.xml
<c3p0-config>    
    <default-config>   <!-- 使用默认的配置读取连接池对象 -->
        <!--  如下连接参数 --> 
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day05</property>
        <property name="user">root</property>
        <property name="password">1234</property>
        <!-- 如下连接池参数 -->
        <!--
            initialPoolSize : 初始化连接数  3
            maxPoolSize: 最大连接数  5
            checkoutTimeout : 连接超时时间 2000ms(默认10s,访问数超过最大连接数, 有人必须要等,2秒连不上给个提示或报错)                        
            maxIdleTime :最大的闲置时间,连接超过maxIdleTime没人使用闲置就销毁maxPoolSize中连接,留到minPoolSize中数量,因为费内存
        -->
        <property name="initialPoolSize">3</property>
        <property name="maxPoolSize">5</property>
        <property name="minPoolSize">2</property>
        <property name="checkoutTimeout">2000</property>
        <property name="maxIdleTime">1000</property>
    </default-config>

<!--111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111-->
    <named-config name="xx">
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/beitai</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <!-- 连接池参数 -->
        <property name="initialPoolSize">5</property>
        <property name="maxPoolSize">15</property>
        <property name="checkoutTimeout">2000</property>
        <property name="maxIdleTime">1000</property>
    </named-config>
</c3p0-config>
java 复制代码
package com.itheima04.c3p0;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class ParseDemo {
    @Test
    public void method01() throws IOException {
        Properties p = new Properties(); //Properties就是一个map            
        p.load(new FileInputStream("src/jdbc.properties")); //相对路径:当前工程src下
        String url = p.getProperty("url"); //.properties文件中有url=...
        System.out.println(url);
    }    

    @Test
    public void method02() throws IOException {
        /*
        *     1. 类加载器 classloader 【底层: 输入流】
        *  作用: 将 .class文件(硬盘) 加载进内存(兼职把jdbc.properties加载进来)。
        * 
        *     2. classloader怎么知道.class文件在哪里?
        *  classloader有个默认加载路径:out路径下项目名路径(src和out/production/项目名路径里文件全一样)。
        * 
        *  相比method01,一般用method02更通用,因为每个工程都会有类加载器路径,但是每个工程的相对路径不一定是当前工程src下。
        *  如web阶段main方法不在项目里,每个项目的入口是main方法,main方法在tomcat里,
        *  所以工程的相对路径会变,但是类加载器的路径不变,一直指向.class文件路径。
        */
        ClassLoader classLoader = ParseDemo.class.getClassLoader(); //利用当前类获取类加载器路径
        InputStream is = classLoader.getResourceAsStream("jdbc.properties"); //获取资源转成流 
        Properties p = new Properties();
        p.load(is);
        String url = p.getProperty("url");
        System.out.println(url + "-2");
    }
}
java 复制代码
package com.itheima04.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
//c3p0-config.xml验证
public class C3p0Demo02 { 
    public static void main(String[] args) throws SQLException {            
        ComboPooledDataSource ds = new ComboPooledDataSource(); //使用默认配置即c3p0-config.xml中<default-config>            
       // ComboPooledDataSource ds = new ComboPooledDataSource("xx"); //使用命名配置即c3p0-config.xml中<named-config name="xx"> //备胎项目用。               
        for (int i = 0; i < 6; i++) { //循环模拟: 有几多个用户
            Connection conn = ds.getConnection();
            System.out.println(conn); //没有下面close的话,5个连接6个人拿超过2s,会发生timed out报错
            if(i == 3){ //i=3打印了下又还给连接池,所以i=3打印了两次。             
                conn.close(); //验证了确实连接还给连接池
            }
        }
    }
}

5个连接6个人拿是不够的。如下最后@2d...打印了2次并且没有发生timed out连接超时错误。

15.druid连接池:自动ds.set

java 复制代码
package com.itheima05.druid;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;

public class DruidDemo {
    public static void main(String[] args) throws Exception {
//        method01();   
        //如下配置文件的方式
        InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
        Properties p = new Properties();
        p.load(is);         
        DataSource ds = DruidDataSourceFactory.createDataSource(p);  //自动解析.properties文件
        
        String sql = "select * from account";        
        Connection conn = ds.getConnection(); //获取连接
        PreparedStatement pstm = conn.prepareStatement(sql);
        ResultSet resultSet = pstm.executeQuery();
        while(resultSet.next()){
            String name = resultSet.getString("name");
            System.out.println(name + "-++");
        }
        conn.close();  //将连接还给连接池
    }

//111111111111111111111111111111111111111111111111111111111111111111111111
    private static void method01() throws SQLException {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl("jdbc:mysql:///day05");
        ds.setUsername("root");
        ds.setPassword("1234");
        
        String sql = "select * from account";               
        Connection conn = ds.getConnection();  
        PreparedStatement pstm = conn.prepareStatement(sql);
        ResultSet resultSet = pstm.executeQuery();
        while(resultSet.next()){
            String name = resultSet.getString("name");
            System.out.println(name + "--");
        }
        conn.close();// 将连接还给连接池
//       ds.close(); // 销毁 //实际开发服务器不关,连接池不会销毁
    }
}
java 复制代码
//druid.properties文件,如下key固定写法
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///day05
username=root
password=1234

封装druid连接池工具类:ds = DruidDataSourceFactory。File-New-Project-Java。

java 复制代码
package com.itheima.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class JdbcUtil {
    private static DataSource ds; //下面getConn()也能访问到
    static{ //静态代码块只运行一次
        try {
            Properties p = new Properties();
            InputStream is = JdbcUtil.class.getClassLoader().getResourceAsStream("druid.properties");            
            p.load(is);            
            ds = DruidDataSourceFactory.createDataSource(p); //DataSource ds = 移到最上面了
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static DataSource getDs() { //属性私有化后提供对外公开的方法
        return ds;
    }
    public static Connection getConn() throws SQLException {
//        Connection connection =  ds.getConnection();
//        return connection;
        return ds.getConnection(); //等同上面两行
    }
    public static void release(AutoCloseable... ios){
        for (AutoCloseable io : ios) {
            if(io != null){
                try {
                    io.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
java 复制代码
package com.itheima.utils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
//测试上面JdbcUtil类
public class TestDemo {
    @Test
    public void method01() throws SQLException {
        String sql = "select * from account";    
        Connection conn = JdbcUtil.getConn();
        PreparedStatement pstm = conn.prepareStatement(sql);
        ResultSet resultSet = pstm.executeQuery();
        while(resultSet.next()){
            String name = resultSet.getString("name");
            System.out.println(name);
        }
        JdbcUtil.release(resultSet,pstm,conn); //注意: conn的close是将连接还给连接池
    }
}

如下是数据库中name这一列,不是.properties文件的key。

16.execute/update方法:template =

java 复制代码
package com.itheima01.template;
import com.itheima.utils.JdbcUtil;
import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
/*
*  JdbcTemplate:是spring的框架的一部分,spring框架是工具箱。作用: 简化jdbc代码编写
*       使用:1. 数据库的操作:DCMQ
*            2. 核心类: JdbcTemplate。构造方法:JdbcTemplate(DataSource ds)
*               核心方法: A. void execute : 理论上可以执行任意sql,适合执行DDL,因为void无返回值
*                        B. int update  : 适合执行DML
*                        C. 多种多样 query : 适合执行DQL,返回多种多样
*/
public class TemplateDemo01 {
    @Test
    public void execute(){
        String sql = "create table student(id int primary key auto_increment,name varchar(20),age int)";
        DataSource ds = JdbcUtil.getDs(); //拿到连接池
        JdbcTemplate template = new JdbcTemplate(ds);        
        template.execute(sql); //无返回值
        System.out.println("执行结束");
    }

    @Test
    public void update01(){
        String sql = "insert into student values(null,?,?),(null,?,?)";
        JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());
        /*
        * int update(String sql, Object... args)
        *   Object... args:
        *       1. Object原因是参数类型是不确定的 -> Object
        *       2. ... 原因是参数个数不确定
        *   返回值: 被影响的行数
        */
        int count = template.update(sql, "zs", 18, "ls", 19);
        System.out.println(count); //2
    }

    @Test
    public void update02(){
        String sql = "update student set age = ? where id = ?";
        JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());        
        Object[] args = {99,1}; //可变参数本质是数组
        int update = template.update(sql, args); 
        System.out.println(update);
    }
}

17.queryForXX/query方法:Map.Entry < String, Object > ,Map < String, Object > 两个数据类型

一行map是一个对象,query方法用的是多个对象这个。

java 复制代码
package com.itheima01.template;
import com.itheima.utils.JdbcUtil;
import org.junit.Test;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
import java.util.Map;
import java.util.Set;
/*
*   C. 多种多样 query : 适合执行DQL
*       1. queryForXX : XX表示返回值类型
*           a. queryForObject
*           b. queryForMap
*           c. queryForList
*       2. query(RowMapper 行映射器)
*/
public class TemplateDemo02 {
    JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());
    @Test
    public void queryForObject01(){
        String sql = "select count(*) from student";
        /*
        * <T> T queryForObject(String sql, Class<T> requiredType)
        *     requiredType : 返回值类型 -> Class对象
        *
        *     EmptyResultDataAccessException : 空结果异常
        *       查询不到任何数据,会报这个错
        */
        Integer count = template.queryForObject(sql, Integer.class);
        System.out.println(count);
    }

    @Test
    public void queryForObject02(){
        String sql = "select name from student where id = ?";
        String s = null;
        try {
            s = template.queryForObject(sql, String.class,3); //3传入上面?
        } catch (DataAccessException e) {
            e.printStackTrace();
            System.out.println("查询不到任何结果");
        }
        System.out.println(s);
    }
   
    @Test
    public void queryForMap(){
        String sql = "select * from student where id = ?";
        Map<String, Object> map = template.queryForMap(sql, 1);
//        System.out.println(map);  //{id=1,name=zs,age=99}

        Set<Map.Entry<String, Object>> entrySet = map.entrySet();
        for (Map.Entry<String, Object> entry : entrySet) { //一条
            String key = entry.getKey();
            Object value = entry.getValue();
            System.out.println(key + "=" + value); //竖着打印id=1 name=zs age=99
        }
    }

    @Test
    public void queryForList(){
        String sql = "select * from student";
        List<Map<String, Object>> list = template.queryForList(sql);       
        for (Map<String, Object> map : list) {
            System.out.println(map); //{id=1,name=zs,age=99} 换行 {id=2,name=ls,age=19}
        }
    }
}
java 复制代码
package com.itheima01.template;
/*
*  JavaBean三要素(必须要有):
* 	    1. private属性,属性名和表中字段名是一致的!!! 都是引用类型(因为数据库中空为null,不是0)
*       2. public get set方法
*       3. public 空参构造
*/
public class Student {
    private Integer id;
    private String name;
    private Integer age;
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
}
java 复制代码
package com.itheima01.template;
import com.itheima.utils.JdbcUtil;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

public class TemplateDemo03 {
    JdbcTemplate template = new JdbcTemplate(JdbcUtil.getDs());
    @Test
    public void query01(){
        String sql = "select * from student";        
        /*
        * List<T> query(String sql, RowMapper<T> rm)
        *  RowMapper : 行映射器 (接口)。方法参数中有接口类型, 那么调用的时候必须传入接口的实现类对象
        */
        RowMapper<Student> rowMapper = new RowMapper<Student>() {
            /*
            *  如下Student mapRow(ResultSet resultSet, int i)   映射行: resultSet转换为Student   
            *       1. resultSet: 结果集(每行)
            *       2. i: 当前的行索引(没什么用)
            */
            @Override
            public Student mapRow(ResultSet resultSet, int i) throws SQLException {
                String name = resultSet.getString("name");
                int id = resultSet.getInt("id");
                int age = resultSet.getInt("age");
                Student s = new Student();
                s.setId(id);
                s.setName(name);
                s.setAge(age);
                System.out.println(i);
                return s;
            }
        };                     
 // RowMapper行映射器 像 动态代理中 InvocationHandler,mapRow方法像invoke方法
        List<Student> list = template.query(sql, rowMapper);
        System.out.println(list); //打印出,list里是student类
        // [Student{id=1,name='zs',age=99},Student{id=2,name='ls',age=19}]                
    }

    @Test
    public void query02(){
        String sql = "select * from student";
        /*
            BeanPropertyRowMapper: 类
                1. RowMapper接口的实现类
                2. BeanPropertyRowMapper(xx.class); 返回值的泛型
        */
        /*
        * BeanPropertyRowMapper (底层反射),思路如下:
        *    1. 实现RowMapper接口
        *    2. 重写mapRow方法 : 每行ResultSet -> javaBean
        *          1. 获取结果集中的数据
        *          	  知道结果集有哪些字段 -> 结果集元数据
        *          	  值 = resultSet.get(字段);
        *             id值 = id
        * 
        *      	   2. 设置到javabean中去 (需要传参: Student.class)
        *          	  clazz = Student.class //获取类
        *          	  Student s = clazz.newInstance(); // javabean规范: 默认调用空参构造	
        *			 // Student s = new Student(); //等同于上面两行
        *	      
        *		      //并不知道Student对象有哪些方法,通过反射如下
        *          	  setIdMethod = clazz.getMethod("setId",int.class); 
        * 
        *             //怎么知道Student对象中有setId方法呢?
        *			  //因为javabean规范 : 必有set方法。额外要求:set+名字(必须要和表中的字段名一致)
        *             setIdMethod.invoke(s,id值);
        */
        RowMapper<Student> rowMapper = new BeanPropertyRowMapper<>(Student.class);
        List<Student> list = template.query(sql, rowMapper);
        System.out.println(list); //打印出同query01()
    }
}
相关推荐
夜泉_ly2 小时前
MySQL -安装与初识
数据库·mysql
qq_529835354 小时前
对计算机中缓存的理解和使用Redis作为缓存
数据库·redis·缓存
月光水岸New6 小时前
Ubuntu 中建的mysql数据库使用Navicat for MySQL连接不上
数据库·mysql·ubuntu
狄加山6756 小时前
数据库基础1
数据库
我爱松子鱼6 小时前
mysql之规则优化器RBO
数据库·mysql
chengooooooo6 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Rverdoser7 小时前
【SQL】多表查询案例
数据库·sql
Galeoto7 小时前
how to export a table in sqlite, and import into another
数据库·sqlite
人间打气筒(Ada)8 小时前
MySQL主从架构
服务器·数据库·mysql
leegong231118 小时前
学习PostgreSQL专家认证
数据库·学习·postgresql