概念 : 是现在流行的开源的,免费的关系型数据库
历史 : 由瑞典MySQL AB 公司开发,目前属于Oracle旗下产品
特点 :
- 免费 , 开源数据库
- 小巧 , 功能齐全
- 使用便捷
- 可运行于Windows或Linux *** 作系统
- 可适用于中小型甚至大型网站应用
- 在官网下载MySQL5.7的压缩包。官网:https://www.mysql.com/
- 将安装包解压到安装目录。
- 配置环境变量。
新建完系统变量后,在path中添加%MYSQL_HOME%bin。 - 编辑 my.ini 文件 ,注意替换路径位置。
[mysqld] #端口号 port = 3306 #数据库的安装路径 basedir=D:Program Filesmysql-5.7.26 #数据库的安装路径+data(数据库存储路径) datadir=D:Program Filesmysql-5.7.26data #最大连接数 max_connections=200 #编码 character-set-server=utf8 default-storage-engine=INNODB sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES #使MySQL跳过密码验证。 skip-grant-tables
- 管理员启动CMD,并将路径切换至mysql下的bin目录,然后输入mysqld –install来安装mysql。
mysqld –instal
- 再输入 mysqld --initialize-insecure --user=mysql 初始化数据文件。
mysqld --initialize-insecure --user=mysql
- 然后再次启动mysql 然后用命令 mysql –u root –p 进入mysql管理界面(密码为空)。
- 通过以下SQL语句更改root密码。
update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';
- 刷新权限
flush privileges;
- 修改 my.ini文件删除最后一句skip-grant-tables 。恢复登录密码验证。
- 重启mysql即可正常使用
net stop mysql net start mysql
- 使用root用户名和设置的密码登录MySQL。
mysql -uroot -p1234563.MySQL的数据类型
(1) 数值类型
无符号值:0~ 2 8 2^{8} 28-11
无符号值:0~ 2 16 2^{16} 216-12
无符号值:0~ 2 24 2^{24} 224-13
无符号值:0~ 2 32 2^{32} 232-14
无符号值:0~ 2 64 2^{64} 264-18
无符号值:0和1.175494351E-38~3.402823466E+384
无符号值:0和2.2250738585072014E-308~1.7976931348623157E+3088
(2) 字符串类型
0<=M<=255M字符
0<=M<=65535可变长度
(3) 日期类型
(4)NULL值
- 理解为“没有值”或“未知值”
- 不要用NULL进行算术运算,结果仍为NULL
- UnSigned:无符号的,声明该数据列不能为负数。
- ZEROFILL:0填充的,不足的位数用0来填充。
- Auto_InCrement:自增,每添加一条数据,自动在上一个记录数上加1。
- NULL和NOT NULL:默认为NULL,即没有插入该列的数值;如果设置为NOT NULL,则该列必需有值。
- DEFAULT:默认的,用于设置默认值。
- 创建数据库
create database [if not exists] 数据库名;
- 删除数据库
drop database [if exists] 数据库名;
- 查看数据库
show databases;
- 使用数据库
use 数据库名;2.数据表的基本 *** 作 (1)创建表
属于DDL的一种,语法:
create table [if not exists] `表名`( '字段名1' 列类型 [属性][索引][注释], '字段名2' 列类型 [属性][索引][注释], #... '字段名n' 列类型 [属性][索引][注释] )[表类型][表字符集][注释];
创建一个学生表示例:
create table student( id int not null comment auto_increment '学号', name varchar(20) default null comment '学生姓名', gender default null comment '性别', birthday date, PRIMARY KEY (`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='学生表'(2)修改表
- 修改表名
ALTER TABLE 旧表名 RENAME AS 新表名
- 添加字段
ALTER TABLE 表名 ADD字段名 列属性[属性]
- 修改字段
ALTER TABLE 表名 MODIFY 字段名 列类型[属性] ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[属性]
- 删除字段
ALTER TABLE 表名 DROP 字段名(3)删除表
DROP TABLE [IF EXISTS] 表名三、数据管理 1.外键
- 概念:
如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见,外键表示了两个关系之间的相关联系。以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表。
在实际 *** 作中,将一个表的值放入第二个表来表示关联,所使用的值是第一个表的主键值(在必要时可包括复合主键值)。此时,第二个表中保存这些值的属性称为外键(foreign key)。外键可以使两张表形成关联。 - 创建外键
(1)创建外键的方式一 : 创建子表同时创建外键
-- 年级表 (id年级名称) CREATE TABLE `grade` ( `gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级ID', `gradename` VARCHAR(50) NOT NULL COMMENT '年级名称', PRIMARY KEY (`gradeid`) ) ENGINE=INNODB DEFAULT CHARSET=utf8 -- 学生信息表 (学号,姓名,性别,年级,手机,地址,出生日期,邮箱,身份z号) CREATE TABLE `student` ( `studentno` INT(4) NOT NULL COMMENT '学号', `studentname` VARCHAR(20) NOT NULL DEFAULT '匿名' COMMENT '姓名', `gradeid` INT(10) DEFAULT NULL COMMENT '年级', PRIMARY KEY (`studentno`), KEY `FK_gradeid` (`gradeid`), CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`) ) ENGINE=INNODB DEFAULT CHARSET=utf8
(2)创建外键方式二 : 创建子表完毕后,修改子表添加外键
ALTER TABLE `student` ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`);
- 删除外键
ALTER TABLE student DROP FOREIGN KEY FK_gradeid; -- 发现执行完上面的,索引还在,所以还要删除索引 -- 注:这个索引是建立外键的时候默认生成的 ALTER TABLE student DROP INDEX FK_gradeid;2.添加数据
语法:
INSERT INTO 表名[(字段1,字段2,字段3,...)] VALUES('值1','值2','值3')3.修改数据
语法:
UPDATE 表名 SET column_name=value [,column_name2=value2,...] [WHERe condition];
- column_name 为要更改的数据列
- value 为修改后的数据 , 可以为变量 , 具体指 , 表达式或者嵌套的SELECT结果
- condition 为筛选条件 , 如不指定则修改该表的所有列数据
where条件子句:有条件地从表中筛选数据
语法:
DELETE FROM 表名 [WHERe condition];
TRUNCATE [TABLE] table_name; -- 清空年级表 TRUNCATE grade
TRUNCATE与DELETe的不同之处:
(1)使用TRUNCATE TABLE 重新设置AUTO_INCREMENT计数器。
(2)使用TRUNCATE TABLE不会对事务有影响
SELECT [ALL | DISTINCT] {* | table.* | [table.field1[as alias1][,table.field2[as alias2]][,...]]} FROM table_name [as table_alias] [left | right | inner join table_name2] -- 联合查询 [WHERe ...] -- 指定结果需满足的条件 [GROUP BY ...] -- 指定结果按照哪几个字段来分组 [HAVINg] -- 过滤分组的记录必须满足的次要条件 [ORDER BY ...] -- 指定查询记录按一个或多个条件排序 [LIMIT {[offset,]row_count | row_countOFFSET offset}]; -- 指定查询的记录从哪条至哪条(2)AS子句
作用:
可给数据列取一个新别名
可给表取一个新别名
可把经计算或总结的结果用另一个新名称来代替
-- 使用as可以为列和表取别名 SELECt studentno AS 学号,studentname AS 姓名 FROM student AS s;(3)where条件语句
作用:为SELECt语句提供检索条件。
检索条件一般由一个或多个逻辑表达式组成,结果为真或假。
如:
SELECT * FROM student where id = 1;(4)模糊查询:
-- 查询姓刘的同学的学号及姓名 -- like结合使用的通配符 : % (代表0到任意个字符) _ (一个字符) SELECt studentno,studentname FROM student WHERe studentname LIKE '刘%'; -- 查询学号为1000,1001,1002的学生姓名 SELECt studentno,studentname FROM student WHERe studentno IN (1000,1001,1002) -- 查询出生日期没有填写的同学 -- 不能直接写=NULL , 这是代表错误的 , 用 is null SELECt studentname FROM student WHERe BornDate IS NULL; -- 查询出生日期填写的同学 SELECt studentname FROM student WHERe BornDate IS NOT NULL;(5)连接查询
- 内连接:
是一种最常用的连接类型。内连接查询实际上是一种任意条件的查询。使用内连接时,如果两个表的相关字段满足连接条件,就从这两个表中提取数据并组合成新的记录,也就是在内连接查询中,只有满足条件的元组才能出现在结果关系中。 - 外连接:
内连接的查询结果都是满足连接条件的元组。但有时我们也希望输出那些不满足连接条件的元组信息。比如,我们想知道每个学生的选课情况,包括已经选课的学生(这部分学生的学号在学生表中有,在选课表中也有,是满足连接条件的),也包括没有选课的学生(这部分学生的学号在学生表中有,但在选课表中没有,不满足连接条件),这时就需要使用外连接。外连接是只限制一张表中的数据必须满足连接条件,而另一张表中的数据可以不满足连接条件的连接方式。
(1)左外连接:以左表作为基准,右边表来一一匹配,匹配不上的,返回左表的记录,右表以NULL填充。
(2)右外连接:以右表作为基准,右边表来一一匹配,匹配不上的,返回左表的记录,右表以NULL填充。
什么是子查询?
- 在查询语句中的WHERe条件子句中,又嵌套了另一个查询语句
- 嵌套查询可由多个子查询组成,求解的方式是由里及外;
- 子查询返回的结果一般都是集合,故而建议使用IN关键字;
(1)什么是事务:
事务就是将一组SQL语句放在同一批次内去执行,如果一个SQL语句出错,则该批次内的所有SQL都将被取消执行。
(2)事务的ACID特性:
- 原子性(Atomic)–所有 *** 作要么全部正确反应,要么全部不反应。
- 一致性(Consist)–事务的执行不会让数据库出现不一致。
- 隔离性(Isolated)–事务之间是隔离的,每个事务都感觉不到系统中其他事务在并发的进行。
- 持久性(Durable)–事务完成后对数据库的改变是永久的。
(3)基本语法
-- 使用set语句来改变自动提交模式 SET autocommit = 0; SET autocommit = 1; -- 注意: --- 1.MySQL中默认是自动提交 --- 2.使用事务时应先关闭自动提交 -- 开始一个事务,标记事务的起始点 START TRANSACTION -- 提交一个事务给数据库 COMMIT -- 将事务回滚,数据回到本次事务的初始状态 ROLLBACK -- 还原MySQL数据库的自动提交 SET autocommit =1; -- 保存点 SAVEPOINT 保存点名称 -- 设置一个事务保存点 ROLLBACK TO SAVEPOINT 保存点名称 -- 回滚到保存点 RELEASE SAVEPOINT 保存点名称 -- 删除保存点7.MySQL函数
-- 数值函数 abs(x) -- 绝对值 abs(-10.9) = 10 format(x, d) -- 格式化千分位数值 format(1234567.456, 2) = 1,234,567.46 ceil(x) -- 向上取整 ceil(10.1) = 11 floor(x) -- 向下取整 floor (10.1) = 10 round(x) -- 四舍五入去整 mod(m, n) -- m%n m mod n 求余 10%3=1 pi() -- 获得圆周率 pow(m, n) -- m^n sqrt(x) -- 算术平方根 rand() -- 随机数 truncate(x, d) -- 截取d位小数 -- 时间日期函数 now(), current_timestamp(); -- 当前日期时间 current_date(); -- 当前日期 current_time(); -- 当前时间 date('yyyy-mm-dd hh:ii:ss'); -- 获取日期部分 time('yyyy-mm-dd hh:ii:ss'); -- 获取时间部分 date_format('yyyy-mm-dd hh:ii:ss', '%d %y %a %d %m %b %j'); -- 格式化时间 unix_timestamp(); -- 获得unix时间戳 from_unixtime(); -- 从时间戳获得时间 -- 字符串函数 length(string) -- string长度,字节 char_length(string) -- string的字符个数 substring(str, position [,length]) -- 从str的position开始,取length个字符 replace(str ,search_str ,replace_str) -- 在str中用replace_str替换search_str instr(string ,substring) -- 返回substring首次在string中出现的位置 concat(string [,...]) -- 连接字串 charset(str) -- 返回字串字符集 lcase(string) -- 转换成小写 left(string, length) -- 从string2中的左边起取length个字符 load_file(file_name) -- 从文件读取内容 locate(substring, string [,start_position]) -- 同instr,但可指定开始位置 lpad(string, length, pad) -- 重复用pad加在string开头,直到字串长度为length ltrim(string) -- 去除前端空格 repeat(string, count) -- 重复count次 rpad(string, length, pad) --在str后用pad补充,直到长度为length rtrim(string) -- 去除后端空格 strcmp(string1 ,string2) -- 逐字符比较两字串大小 -- 聚合函数 count() sum(); max(); min(); avg(); group_concat() -- 其他常用函数 md5(); default()四、JDBC 1.JDBC介绍
SUN公司为了简化、统一对数据库的 *** 作,定义了一套Java *** 作数据库的规范(接口),称之为JDBC(Java Data base Connectivity)。这套接口由数据库厂商去实现,这样,开发人员只需要学习jdbc接口,并通过jdbc加载具体的驱动,就可以 *** 作数据库。
一个基本的JDBC程序如下所示:
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=true"; String username = "root"; String password = "123456"; //3.连接成功,数据库对象 Connection代表数据库 Connection connection = DriverManager.getConnection(url,username,password); //4.执行SQL的对象 statement代表执行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("password="+resultSet.getObject("PASSWORD")); System.out.println("email="+resultSet.getObject("email")); System.out.println("birthday="+resultSet.getObject("birthday")); System.out.println("==================================="); } //6.释放链接 resultSet.close(); statement.close(); connection.close(); }3.使用jdbc对数据库增删改查
在JDBC *** 作中,我们可以将数据库的驱动等信息存入一个配置文件中,并创建一个util工具类,存入一些方法,以后每次对数据库 *** 作调入该方法即可。这样可以使我们的代码更加简洁。具体步骤如下:
(1)创建db.properties文件。
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/jdbcStudy? useUnicode=true&characterEncoding=utf8&useSSL=true username=root password=123456
(2)新建一个Util工具类。
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("driver"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); //1.驱动只加载一次 Class.forName(driver); }catch (IOException | ClassNotFoundException 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(); } } } }
(3)对数据进行增删改查 *** 作。
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 = "UPDATe users SET `NAME`='zhangsansan',`email`='1231414@163.com' WHERe id=1"; int i = st.executeUpdate(sql); if(i > 0){ System.out.println("更新成功!"); } } catch (SQLException e) { e.printStackTrace(); } finally { JdbcUtils.release(conn,st,rs); } }4.SQL注入问题
我们编写如下代码,模拟一个登录 *** 作。
public static void main(String[] args) { //login("lisi","123456");//正常登录 login("'or '1=1","'or '1=1");//技巧 } public static void login(String username,String password){ Connection conn = null; Statement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); st = conn.createStatement(); String sql = "select * from users where `NAME` ='"+username+"'AND `PASSWORD`='"+password+"'"; rs = st.executeQuery(sql);//查询完毕会返回一个结果集 while(rs.next()){ System.out.println(rs.getString("NAME")); System.out.println(rs.getString("PASSWORD")); } } catch (SQLException e) { e.printStackTrace(); } finally { JdbcUtils.release(conn,st,rs); } }
上述代码中,正常输入用户名和密码:lisi 123456即可访问数据库中数据。但是如果用户在输入用户名和密码的位置输入代码中login()的参数:'or '1=1","'or '1=1,也可以实现访问数据库,这就导致了我们的程序不够安全。
login方法传入的这个参数和sql语句拼接后的结果为:
select * from users where NAME = ’ ’ or ‘1=1’ AND PASSWORD= ’ 'or '1=1’。这个语句是可以查询成功的。
即通过巧妙的技巧来拼接字符串,造成SQL短路,从而获取数据库数据。这就是SQL注入问题。
解决办法:
PreperedStatement是Statement的子类,它的实例对象可以通过调用Connection.preparedStatement()方法获得,相对于Statement对象而言:PreperedStatement可以避免SQL注入的问题。
Statement会使数据库频繁编译SQL,可能造成数据库缓冲区溢出。PreparedStatement可对SQL进行预编译,从而提高数据库的执行效率。并且PreperedStatement对于sql中的参数,允许使用占位符的形式进行替换,简化sql语句的编写。
使用 PreperedStatemen对象解决注入问题实例如下:
public static void main(String[] args) { login("lisi","111111");//正常登录 //login("'or '1=1","'or '1=1");//技巧无法成功 } public static void login(String username,String password){ Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try { conn = JdbcUtils.getConnection(); //PreparedStatement防止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();//查询完毕会返回一个结果集 while(rs.next()){ System.out.println(rs.getString("NAME")); System.out.println(rs.getString("PASSWORD")); } } catch (SQLException e) { e.printStackTrace(); } finally { JdbcUtils.release(conn,st,rs); } }5.事务
在JDBC中实现对事务的测试,从而加深对事务的理解。
ABC三个人的账户各有1000块,现在模拟A给B转账100块钱。首先需要先从A的账户扣除100元,随后再在B的账户中增加100元。
若全部执行成功,事务提交,两条SQL语句生效。如果中间某个环节出现错误,那么事务回滚,所有的SQL语句都不生效。
(1)新建一个account表:
(2)执行JDBC代码。
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(); //int x = 1/0;//报错 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 e1) { e1.printStackTrace(); } e.printStackTrace(); } finally { JdbcUtils.release(conn,st,rs); } }
代码执行成功,A减少100元,B增加100元。
(3)现在在代码中模拟植入一个错误,在A的账户扣除100元后执行一个会出错的代码,看看A是否会真的扣除100元。
package com.kuang.lesson04; import com.kuang.lesson02.utils.JdbcUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; public class TestTransaction1 { 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(); int x = 1/0;//报错 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 e1) { e1.printStackTrace(); } e.printStackTrace(); } finally { JdbcUtils.release(conn,st,rs); } } }
执行结果如下,程序执行失败。
数据库中A和B的账户都没有发生变化,说明事务回滚成功。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)