Mysql基础学习

MYSQL

INNODB 和 MYISAM的区别

常规使用操作:

  • MYISAM 节约空间,速度较快
  • INNODB 安全性高,事务的处理,多表多用户操作

在物理空间存在的位置

所有的数据库文件都在data目录下,本质还是文件的存储!

MySQL 引擎在物理文件上的区别

  • INNODB 在数据库表中只有一个*.frm文件,以及上级目录下的 ibdata1文件
  • MYISAM 对应文件
    • *.frm -表结构的定义文件
    • *.MYD 数据文件 (data)
    • *.MYI 索引文件 (index)

设置数据库的字符集编码

CHARSET=utf8

不设置的话,会是mysql默认的字符集编码(不支持中文!)

一、事务

事务的四大特性

原子性

要么都成功,要么都失败

一致性

事务前后的数据完整性要保证一致

持久性

事务一旦提交则不可逆,被持久化到数据库中!

隔离性

事务的隔离性事多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,事务之间要相互隔离

隔离所导致的一些问题

1、脏读:

指一个事务读取了另外一个事务未提交的数据

2、不可重复读:

在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)

3、虚读(幻读)

是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

-- mysql 是默认开启事务自动提交的
set autocommit = 0 /* 关闭 */
set autocommit = 1 /* 开启 */

-- 手动处理事务
start transaction --标记一个事务的开始,从这个之后的sql都在一个事务内
insert xxx
insert xxx
-- 提交 : 持久化(成功!)
commit
-- 回滚 : 回到原来的样子(失败!)
rollback
-- 事务结束
set autocommint = 1 -- 开启自动提交
-- 了解
savepoint 保存点名 -- 设置一个事务的保存点
rollback to savepoint 保存点名 -- 回滚到保存点
release savepoint 保存点名 -- 撤销保存点

模拟场景

-- 模拟转账:事务
SET autocommit = 0; -- 关闭自动提交
START TRANSACTION -- 开启一个事务(一组事务)

UPDATE account SET money=money-500 WHERE `name` = 'A'  -- A减500
UPDATE account SET money=money+500 WHERE `name` = 'B'  -- B加500

COMMIT; -- 提交事务,数据就会被持久化!
ROLLBACK; -- 回滚

SET autocommit = 1; -- 恢复默认值

二、索引

在一个表中主键索引只能有一个,唯一索引可以有多个

1、索引分类

  • 主键索引 (PRIMARY KEY)
    • 唯一的标识,主键不可重复,只能有一个列作为主键
  • 唯一索引 (UNIQUE KEY)
    • 避免重复的列出现,唯一索引可以重复多个列标识为唯一索引
  • 常规索引 (KEY/INDEX)
    • 默认的,index,key 关键字来设置
  • 全文索引 (FULLTEXT)
    • 在特定的数据库引擎下才有,MyISAM
    • 快速定位数据
-- 索引的使用

-- 显示所有的索引信息
SHOW INDEX FROM teacher

-- 增加一个全文索引 (索引名) 列名
ALTER TABLE school.`teacher` ADD FULLTEXT INDEX `email` (`email`) ;


-- id_表名_字段名
-- create INDEX 索引名 on 表(字段)
CREATE INDEX id_app_user ON app_user(`name`); 

-- EXPLAIN 分析sql执行的状况

EXPLAIN SELECT * FROM teacher; -- 非全文索引
EXPLAIN SELECT * FROM teacher WHERE MATCH(email)  AGAINST('@qq.com')


2、测试索引

CREATE TABLE `app_user` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) DEFAULT'' COMMENT'用户昵称',
`email` VARCHAR(50) NOT NULL COMMENT'用户邮箱',
`phone` VARCHAR(20) DEFAULT'' COMMENT'手机号',
`gender` TINYINT(4) UNSIGNED DEFAULT '0'COMMENT '性别(0:男;1:女)',
`password` VARCHAR(100) NOT NULL COMMENT '密码',
`age` TINYINT(4) DEFAULT'0'  COMMENT '年龄',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT = 'app用户表'

DROP TABLE app_user;

SET GLOBAL log_bin_trust_function_creators=1; -- 开启创建函数功能
/*
  第一个语句 delimiter 将 mysql 解释器命令行的结束符由”;” 改成了”$$”,
  让存储过程内的命令遇到”;” 不执行
*/
DELIMITER $$
CREATE FUNCTION mock_data()
RETURNS INT
DETERMINISTIC
BEGIN
 DECLARE num INT DEFAULT 1000000;
 DECLARE i INT DEFAULT 0;
 WHILE i < num DO
  INSERT INTO app_user(`name`,`email`,`phone`,`gender`,`password`,`age`)VALUES(CONCAT('用户',i),'24736743@qq.com',CONCAT('18',FLOOR(RAND()*(999999999-100000000)+100000000)),FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
  SET i = i + 1;
 END WHILE;
 RETURN i;
END $$
SELECT mock_data();

 EXPLAIN SELECT * FROM app_user  WHERE `name` = '用户9999';
-- id_表名_字段名
-- create INDEX 索引名 on 表(字段)
CREATE INDEX id_app_user ON app_user(`name`); 

SELECT * FROM app_user  WHERE `name` = '用户9999';

添加索引后大大提升了

索引在小数据量的时候差别不大,但在大数据的时候,区别十分明细明显

3、索引原则

  • 索引不是越多越好
  • 不要对进程变动数据加索引
  • 小数据量的表不需要加索引
  • 索引一般加在常用来查询的字段上!

索引的数据结构

Hash类型的索引

Btree:InnoDB的默认数据结构!

阅读相关资料:http://blog.codinglabs.org/articles/theory-of-mysql-index.html

三、JDBC

1、第一个JDBC程序

创建测试数据库

CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;

USE jdbcStudy;

CREATE TABLE `users`(
	id INT PRIMARY KEY,
	NAME VARCHAR(40),
	PASSWORD VARCHAR(40),
	email VARCHAR(60),
	birthday DATE
);

INSERT INTO `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`)
VALUES(1,'zhangsan','123456','zs@sina.com','1980-12-04'),
(2,'lisi','123456','lisi@sina.com','1981-12-04'),
(3,'wangwu','123456','wangwu@sina.com','1979-12-04')

1、创建一个普通项目

2、导入数据库驱动

3、编写测试代码

1、sql建表语句

CREATE TABLE `users` (
  `id` int(11) NOT NULL,
  `NAME` varchar(40) DEFAULT NULL,
  `PASSWORD` varchar(40) DEFAULT NULL,
  `email` varchar(60) DEFAULT NULL,
  `birthday` date DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

/*Data for the table `users` */

insert  into `users`(`id`,`NAME`,`PASSWORD`,`email`,`birthday`) values (1,'zhangsan','123456','zs@sina.com','1980-12-04'),(2,'lisi','123456','lisi@sina.com','1981-12-04'),(3,'wangwu','123456','wangwu@sina.com','1979-12-04');

2、IDEA连接数据库

package com.kuang.lesson01;

//我的第一个jdbc程序

import java.sql.*;

public class JdbcFirstDemo {

    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2.用户信息和url
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
        String username = "root";
        String password = "123456";
        //3.连接成功,数据库对象   去执行SQL
        Connection connection = DriverManager.getConnection(url,username,password);
        //connection 代表数据库	数据库设置自动提交  事务自动提交 事务回滚
        //4.执行SQL对象
        Statement statement = connection.createStatement();
        //5.执行SQL的对象 去执行SQL,可能存在结果,查看返回结果
        String sql = "SELECT * FROM users";
        ResultSet resultSet = statement.executeQuery(sql);  //返回的结果集,结果集中封装了我们全部查询出来的结果
        while (resultSet.next()) {
            System.out.println("id="+resultSet.getObject("id"));
            System.out.println("name="+resultSet.getObject("NAME"));
            System.out.println("pwd"+resultSet.getObject("PASSWORD"));
            System.out.println("email"+resultSet.getObject("email"));
            System.out.println("birth"+resultSet.getObject("birthday"));
        }

        //6.释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }


}

步骤总结:

1、加载驱动

2、连接数据库DriverManager

3、获得执行sql的对象 Statement

4、获得返回的结果集

5、释放连接

Statement 执行sql的对象 PrepareStatement 执行SQL对象

        statement.executeQuery();//查询操作返回 ResultSet
        statement.execute();	//执行任何SQL
        statement.executeUpdate()//更新、插入、删除,都是用这个,返回一个受影响的行数

ResultSet 查询的结果集;封装了所有的查询结果

获得指定的数据类型

resultSet.getObject();//在不知道的情况下使用
//如果知道列的类型就使用指定的类型
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
....

遍历 指针

resultSet.beforeFirst();//移动到最前面
resultSet.afterLast();	//移动到最后面
resultSet.next();	//移动到下一个数据
resultSet.previous();//移动到前一行
resultSet.absolute(row);//移动到指定hang

释放资源

//6.释放连接
resultSet.close();
statement.close();
connection.close();

2、statement对象

jdbc中的statement对象用于向数据库SQL语句,想完成数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。

Statement对象的executeUpdate方法,用于向数据库发送增、删、改、的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改语句告知了数据库几行数据发生了变化)。

Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象

代码实现

1、编写工具类

package com.kuang.lesson01;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;
    static {

        try{
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("key");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //1、驱动之用加载一次
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    //获取链接
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, username, password);
    }

    //释放连接资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if (rs !=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st != null){
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn !=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

2、编写增删改的方法,executeUpdate

package com.kuang.lesson02.utils;

import com.kuang.lesson01.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestDelete {
    public static void main(String[] args) {
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();//获取数据库连接
            st = conn.createStatement();//获取SQL执行对象
            String sql ="DELETE FROM users WHERE id = 4" ;

            int i  = st.executeUpdate(sql);
            if (i>0) {
                System.out.println("删除成功!");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }

}

3、查询 executeQuery

package com.kuang.lesson02.utils;

import com.kuang.lesson01.JdbcUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestSelect {
    public static void main(String[] args) throws SQLException {
        Connection conn = null ;
        Statement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            st = conn.createStatement();
            //SQL
            String sql  = "SELECT * FROM users where id = 4";
            rs = st.executeQuery(sql);//查询完毕会返回结果集
            if (rs.next()) {
                System.out.println(rs.getString("NAME"));
                System.out.println(rs.getString("email"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }

    }
}

3、PrepareStament对象

1、插入

package com.kuang.lesson02.utils;

import com.kuang.lesson01.JdbcUtils;

import java.util.Date;

import java.sql.*;

public class TestInsert {

    public static void main(String[] args) {
        Connection conn = null;

        ResultSet rs = null;

        PreparedStatement st = null;
        try {
            conn = JdbcUtils.getConnection();
            //区别
            //使用   占位符替代参数
            String sql  = "insert into users(`id`,`name`,`PASSWORD`,`email`,`birthday`)values(?,?,?,?,?) ";
            st = conn.prepareStatement(sql);    //预编译SQL,先写sql,然后不执行

            //手动给参数赋值
            st.setInt(1,5);
            st.setString(2,"liuxing");
            st.setString(3,"123456");
            st.setString(4,"123456789@qq.com");
            //注意点:  sql.Date     数据库    java.sql.Date()
            //         util.Date    Java    new Date().getTime()  获得时间戳
            st.setDate(5, new java.sql.Date(new Date().getTime()));
            //执行
            int i = st.executeUpdate();

            if (i>0) {
                System.out.println("插入成功");
            }
            }catch(SQLException e) {
            e.printStackTrace();
            }finally {
            JdbcUtils.release(conn,st,null);
            }
    }

}

2、查询

package com.kuang.lesson03;

import com.kuang.lesson01.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TestSelect {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;


        try {
            conn = JdbcUtils.getConnection();

            String sql  ="select * from users where id =?";//编写SQL
            st = conn.prepareStatement(sql);    //预编译

            //传递参数
            st.setInt(1,4);


            rs = st.executeQuery();//执行

            if (rs.next()) {
                System.out.println(rs.getString("NAME"));
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }


    }
}

3、删除

package com.kuang.lesson03;

import com.kuang.lesson01.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;

public class TestDelete {

        public static void main(String[] args) {
            Connection conn = null;

            ResultSet rs = null;

            PreparedStatement st = null;
            try {
                conn = JdbcUtils.getConnection();
                //区别
                //使用   占位符替代参数
                String sql  = "DELETE from users where id=?";
                st = conn.prepareStatement(sql);    //预编译SQL,先写sql,然后不执行

                //手动给参数赋值
                st.setInt(1,5);
                //执行
                int i = st.executeUpdate();

                if (i>0) {
                    System.out.println("删除成功");
                }
            }catch(SQLException e) {
                e.printStackTrace();
            }finally {
                JdbcUtils.release(conn,st,null);
            }
        }



}

4、修改

package com.kuang.lesson03;

import com.kuang.lesson01.JdbcUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class TestUpdate {
    public static void main(String[] args) {
        Connection conn = null;

        ResultSet rs = null;

        PreparedStatement st = null;
        try {
            conn = JdbcUtils.getConnection();
            //区别
            //使用   占位符替代参数
            String sql  = "update users set `name` = ? where id =?";
            st = conn.prepareStatement(sql);    //预编译SQL,先写sql,然后不执行

            //手动给参数赋值
            st.setString(1,"hahaha");
            st.setInt(2,5);
            //执行
            int i = st.executeUpdate();

            if (i>0) {
                System.out.println("更新成功");
            }
        }catch(SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,null);
        }
    }
}

sql注入问题

sql存在漏洞,会被攻击存在漏洞

package com.kuang.lesson02.utils;

import com.kuang.lesson01.JdbcUtils;

import java.sql.*;

public class SQL注入 {


    public static void main(String[] args) {
        //正常情况下
//        login("xiaoxiao","123456");
        login("' or '1=2","'or '1=2");
    }




    public static void login(String username,String password){
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();

            //SQL
            String sql  = "SELECT * FROM users where `NAME`='"+username+"'AND `password` ='"+password+"'";
            rs = st.executeQuery(sql);//查询完毕会返回结果集

            st = conn.prepareStatement(sql);//预编译


            if (rs.next()) {
                System.out.println(rs.getString("NAME"));
                System.out.println(rs.getString("password"));
                System.out.println("==================================");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

SQL注入方式大致分为两类:数字型注入、字符型注入。

					login("' or '1=2","'or '1=2");

解决sql注入问题

package com.kuang.lesson02.utils;

import com.kuang.lesson01.JdbcUtils;

import java.sql.*;

public class SQL注入 {


    public static void main(String[] args) {
        //正常情况下
//        login("hahaha","123456");
        login("'' or '1=1","123456");
    }
    public static void login(String username,String password){
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();
            //SQL
            //PrepareStatement 防止SQL注入的本质,把传递进来的参数当作字符
            //假设其中存在转义字符,比如说 ' ,会被直接转义
            String sql  = "select * from users where `NAME` =? and `PASSWORD` =?";

            st = conn.prepareStatement(sql);//预编译

            st.setString(1,username);
            st.setString(2,password);

            rs = st.executeQuery();//查询完毕会返回结果集

            if (rs.next()) {
                System.out.println(rs.getString("NAME"));
                System.out.println(rs.getString("password"));
                System.out.println("==================================");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

四、使用IDEA连接数据库

第一步:打开右边的Database

第二步:输入数据库信息

第三步: 连接成功后,打开对应的数据库

第四步:若在数据库中修改信息,需要提交

第五步 : 若要输入sql语句,需要打开sql控制台

第六步 : 若要切换数据库

事务

要么都成功,要么都失败

ACID 原则

1、原子性 :要么全部完成,要不都不完成

2、一致性 :总数不变

3、隔离性 :多个进程互不干扰

4、持久性 :一旦提交不可逆,持久化到数据库

隔离性的问题:

脏读: 一个事务读取了另一个没有提交的事务

不可重复读:在同一事务内,重复读取表中的数据,表数据发生了改变

虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来的结果不一致

代码实现

1、开启事务 conn.setAutoCommit(false);

2、一组业务执行完毕,提交事务

3、可以在catch语句中定义回滚语句,但默认失败就会回滚

CREATE TABLE account(
    id  int PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(40),
    money FLOAT
);
/* 插入测试数据 */
insert into account(name,money) values('A',1000);
insert into account(name,money) values('B',1000);
insert into account(name,money) values('C',1000);
package com.kuang.lesson04;
import com.kuang.lesson01.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestTransation1 {
	//事务的执行
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtils.getConnection();
            //关闭数据库的自动提交,自动会开启事务
            conn.setAutoCommit(false);  //开启事务
            String sql1 = "update account set money =money - 100 where name = 'A'";
            st  =  conn.prepareStatement(sql1);
            st.executeUpdate();
            
            String sql2 = "update account set money =money + 100 where name = 'B' ";
            st  =  conn.prepareStatement(sql2);
            st.executeUpdate();
            //业务完毕,提交事务
            conn.commit();
            System.out.println("成功!");
        } catch (SQLException e) {
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

事务失败案例

可以在事务中间加上 int x= 1/0;事务失败后,默认会回滚

 			//关闭数据库的自动提交,自动会开启事务
            conn.setAutoCommit(false);  //开启事务
            String sql1 = "update account set money =money - 100 where name = 'A'";
            st  =  conn.prepareStatement(sql1);
            st.executeUpdate();
            int x= 1/0;
            String sql2 = "update account set money =money + 100 where name = 'B' ";
            st  =  conn.prepareStatement(sql2);
            st.executeUpdate();

五、数据库连接池

数据库连接---执行完毕---释放

在 连接---释放这个过程中十分浪费系统资源

针对于这种情况,可以通过池化技术解决资源浪费的问题

池化技术 : 准备一些预先的资源,过来就连接预先准备好的

最小连接数、最大连接数,等待超时

编写连接池,实现一个接口DataSource

开源数据源实现

DBCP

C3P0

Druid: 阿里巴巴

使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了!

六、MySQL数据库语言

DQL(查询)

对于连表查询的一些注意点

inner join:查询两个表共有+左边单独有+右边单独有的数据 - left join:查询两个表共有+左边单独有的数据 - right join:查询两个表共有+右边表单独有的数据

posted @ 2022-03-07 15:06  星仔呀  阅读(44)  评论(0)    收藏  举报