MySQL是一种关系型数据库管理系统,关系数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
关系型:(SQL)
非关系型:(NoSQL)
DBMS(数据库管理系统)
数据库xxx语言
DDL 定义
DML 操作
DQL 查询
DCL 控制
如果表名或者字段名是特殊字符,需要带~ ~
数据库引擎
MYISAM | INNODB | |
---|---|---|
事务支持 | 不支持 | 支持 |
数据行锁定 | 不支持 | 支持 |
外键约束 | 不支持 | 支持 |
全文索引 | 支持 | 不支持 |
表空间大小 | 小 | 大,约为MYISAM2倍 |
常规操作:
方式一:在创建表的时候,增加约束
CREATE TABLE IF NOT EXISTS `student`{
`gradeid` INT(10) NOT NULL COMMENT '学生年级',
KEY `FK_gradeid` (`gradeid`),
CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`)
}ENGINE=INNODB DEFAULT CHARSET=utf-8
删除外键关系的表,先删除从表,再删除主表
方式二:创建表之后添加外键约束
ALERT TABLE `student`
ADD CONSTRAINT `FK_gradeid` FOREIGN KEY(`gradeid`) REFERENCES `grade`(gradeid)
--ALERT TABLE 表 ADD CONSTRAINT 约束名 FOREIGN KEY(作为外键的列) REFERENCES 被引用的表(被引用的字段)
以上为物理外键。
最佳实践:不用物理外键
insert
语法:insert into 表名([字段1,字段2,字段3])values('值1'),('值2'),('值3');每个值对应一组数据中该字段对应的值
insert into student (`name`,`pwd`,`gender`) VALUES ('张三','aaa','男')
--语法
--update 表名 set column_name = value,[column_name=value,...] where [条件]
delete 命令
语法:delete from 表名 [where 条件]
Truncate 命令
完全删除一个数据库表,表的结构和索引不会变化
语法:truncate table 表名
delete与truncate的区别
相同:删除数据库数据,不影响表结构
不同:
delete删除的问题
重启数据库后的现象
数据查询语言
select 字段名 from table
SELECT语法
SELECT[ALL|DISTINCT|DISTINCTROW|TOP]
{
*|talbe.*|[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 m(起始位置),n(偏移长度)]
SELECT studentno as 学号,studentname as 学生姓名 FROM student as s;
-- 拼接函数: Concat(a,b)
SELECT CONCAT('姓名:',studentname) as 新名字 FROM student;
去重
SELECT DISTINCT studentno FROM result;
数据库中的表达式
--查询自增的步长
SELECT @@auto_increment_increment;
--考试成绩+1分查看
SELECT studentno,studentresult+1 as 提分后 FROM result;
-- != not
SELECT studentNo,studentresult From result
WHERE NOT studentNo=1000;
模糊查询
运算符 | 语法 | 描述 |
---|---|---|
Like | a like b | 如果a匹配b,则结果为真 |
In | a in (a1,a2,…) | a在()中的某个,结果为真 |
-- 张后面只有一个字符的
SELECT studentno,studentname FROM student
WHERE studentname LIKE '张_';
-- 查询为空的
SELECT studentno,studentname FROM student
WHERE address='' or address is NULL;
-- 查询不为空的
SELECT studentno,studentname FROM student
WHERE borndate is NOT NULL;
/*
思路:
1.分析查询字段来自哪些表,(则要使用连接查询)
2.确定使用哪种连接
3.确定交叉点(两个表中哪个数据相同)
如:学生表的 studentno=成绩表的 student
*/
--join 连接的表 on 判断条件//连接查询
--where //等值查询
使用left/right join时,on和where的区别
--eg:查询时逐行查询,当符合条件时,查询出重复teacherno相对应studentno的同一条记录
SELECT s.studentno,teacherno
FROM student s
LEFT JOIN teacher t
on s.studentno=t.teacherno;
--inner join 两表的共有部分才查询出来
SELECT r.studentno,studentname,subjectno,studentResult
FROM student as s
INNER JOIN result as r
WHERE s.studentno=r.studentno;
--left join 将左表全部查询出来
SELECT s.studentno,studentname,subjectno,studentResult
FROM student s
LEFT JOIN result r
on s.studentno=r.studentno;
--right join 将右表全部查询出来,如果右表的条件字段在左表中没有,则查询结果不显示右表中特有的条件字段(参加考试的所有信息)
SELECT s.studentno,studentname,subjectno,studentResult
FROM student s
RIGHT JOIN result r
on s.studentno=r.studentno;
--查询缺考的学生
SELECT s.studentno,studentname,subjectno,studentResult
FROM student as s
LEFT JOIN result as r
on s.studentno=r.studentno
WHERE studentResult is NULL;
--查询参加考试的同学信息:学号,学生姓名,科目名,分数
SELECT s.studentno,studentname,subjectname,studentResult
FROM student as s
RIGHT JOIN result as r
on s.studentno=r.studentno
INNER join `subject` as sub
on r.subjectno=sub.subjectno;
分析查询步骤,将多次查询阶段拆分成多次
思路:
自连接
表和自身连接,把一张表拆成两张表
父类(顶级id)
categoryid | categoryName |
---|---|
2 | 信息技术 |
3 | 软件开发 |
5 | 美术设计 |
子类
pid | categoryid | categoryName |
---|---|---|
3 | 4 | 数据库 |
2 | 8 | 办公信息 |
3 | 6 | web开发 |
5 | 7 | 美术设计 |
操作:查询父类对应的子类关系
父类 | 子类 |
---|---|
信息技术 | 办公信息 |
软件开发 | 数据库 |
软件开发 | web开发 |
美术设计 | ps技术 |
--查询父子信息,把一张表看成两张表
SELECT a.categoryname as 父栏目,b.categoryname as 子栏目
FROM category as a,category as b
WHERE a.categoryid=b.pid;
limit语法:limit(起始值,pagesize)
-- LIMIT 0,5从记录0开始,显示5条记录
SELECT s.studentno,studentname,subjectname,studentresult
FROM student s
INNER JOIN result r
on s.studentno=r.studentno
INNER JOIN `subject` sub
on r.subjectno=sub.subjectno
WHERE subjectname='数据库结构-1'
ORDER BY studentresult ASC
LIMIT 0,5;
order by:
-- ORDER BY:升序ASC,降序DESC
SELECT s.studentno,studentname,subjectname,studentresult
FROM student s
INNER JOIN result r
on s.studentno=r.studentno
INNER JOIN `subject` sub
on r.subjectno=sub.subjectno
WHERE subjectname='数据库结构-1'
ORDER BY studentresult ASC;
where(条件的值是查询得到的)
即:where(select * from)
将子查询得到的结果作为条件在主查询中查询
-- 查询数据库结构-1的所有考试结果(学号,科目编号,成绩),降序排列
--方式一:联表查询
--在on连接条件查询出的结果中,筛选subjectname为指定结果的数据
SELECT r.studentno,r.subjectno,studentresult
FROM result r
INNER JOIN `subject` sub
on r.subjectno=sub.subjectno
WHERE subjectname='数据库结构-1';
--方式二:子查询
SELECT studentno,subjectno,studentresult
FROM result
WHERE subjectno=(
SELECT subjectno FROM `subject`
WHERE subjectname='数据库-1'
)
ORDER BY studentresult DESC;
--练习:
-- 查询课程为高等数学-2 且分数不小于80的同学的学号和姓名
--联表查询
SELECT s.studentno,studentname
FROM student s
INNER JOIN result r
on s.studentno=r.studentno
INNER JOIN `subject` sub
on r.subjectno=sub.subjectno
where subjectname='高等数学-2'&& studentresult>=80;
-- 最后一个联表查询改造成子查询
SELECT s.studentno,studentname
FROM student s
INNER JOIN result r
on s.studentno=r.studentno
WHERE studentresult>=80 AND subjectno=(
SELECT subjectno FROM `subject`
WHERE subjectname='高等数学-2';
-- 将两个联表查询都改为子查询
SELECT s.studentno,studentname
FROM student s
WHERE studentno IN (
SELECT studentno FROM result
WHERE studentresult>=80 AND subjectno = (
SELECT subjectno FROM `subject`
WHERE subjectname='高等数学-2'
)
);
-- 查询按课程分组的>80的平均分,最高分,最低分,只显示平均分大于80分的
SELECT subjectname,AVG(studentresult),MAX(studentresult),min(studentresult)
FROM result r
INNER JOIN `subject` sub
on r.subjectno=sub.subjectno
GROUP BY subjectname
HAVING AVG(studentresult)>=80;
SELECT CURRENT_DATE;-- 日期
SELECT CURDATE();-- 日期
SELECT now();-- 时间
SELECT LOCALTIME;
SELECT SYSDATE();
SELECT SYSTEM_USER();-- 用户
SELECT USER();
SELECT VERSION()-- Mysql版本
SELECT COUNT(字段名)--等其他函数
--加密
UPDATE tb1 set pwd=MD5(pwd);
--插入时候加密
INSERT INTO tb1 VALUES(1,'Tom',MD5('123456'))
--校验
SELECT * FROM tb1 WHERE name='Tom' AND pwd=MD5('123456');
一组SQL放在一个批次中执行
事务原则(ACID)
原子性:要么都成功,要么都失败。
一致性:事务前后的数据完整性保证一致。总和不变
隔离性:不同用户同时访问数据库,每个用户单独一个事务,不能被其他事务操作干扰。
持久性:事务一旦提交就持久化到数据库,不可逆。
隔离导致的问题
脏读:一个事务读取了另外一个事务未提交的数据。
不可重复读:一个事务多次读取某一行记录,多次读取的结果不一致。
幻读:一个事务内读取到了其他事务插入的数据,前后读取结果不一致。
-- mysql默认开启事务自动提交
SET autocommit=0 /*关闭*/
SET autocommit=1 /*开启*/
-- 转账
-- 建数据库
CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci
use shop
-- 建表
CREATE TABLE account(
id INT(3) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(30) NOT NULL,
money DECIMAL(9,2) NOT NULL,
PRIMARY KEY(id)
)ENGINE=INNODB DEFAULT CHARSET=utf8-- innodb支持事务
-- 插入数据
INSERT INTO account(`name`,money)
VALUES ('A',1000.00),('B',2000.00)
-- 模拟转账的事务
SET autocommit=0; --关闭自动提交
START TRANSACTION
UPDATE account SET money=money-500 WHERE `name`='A';
UPDATE account set money=money+500 WHERE `name`='B';
COMMIT
ROLLBACK
SET autocommit=1 --开启自动提交
索引帮助mysql快速获取数据的数据结构。
语法
-- 显示索引信息
SHOW INDEX FROM tb1
--增加一个全文索引 (索引名) 列名
ALTER TABLE school.student ADD FULLTEXT INDEX studentname(studentname);
-- 法二:CREATE INDEX 索引名 on 表(字段)
-- 索引名:id_表名_字段名
CREATE INDEX id_app_user_name ON app_user(`name`)
--explain 分析sql,全文索引,找出建立全文索引的字段和指定字符进行匹配
EXPLAIN SELECT * FROM student WHERE MATCH(studentname) AGAINST('刘')
java中的事务代码
try(){
业务代码
commit();
}catch{
rollback();
}
索引效果:
CREATE TABLE `app_user` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT '',
`email` varchar(50) NOT NULL,
`phone` varchar(20) DEFAULT '',
`gender` tinyint(4) unsigned DEFAULT '0',
`password` varchar(100) NOT NULL DEFAULT '',
`age` tinyint(4) DEFAULT NULL,
`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
-- 插入100万数据.
DELIMITER $$
-- 写函数之前必须要写,标志
CREATE FUNCTION mock_data()
RETURNS INT
BEGIN
DECLARE num INT DEFAULT 1000000;
DECLARE i INT DEFAULT 0;
WHILE i<num DO
INSERT INTO `app_user`(`name`,`email`,`phone`,`gender`)VALUES(CONCAT('用户',i),'[email protected]','123456789',FLOOR(RAND()*2));
SET i=i+1;
END WHILE;
RETURN i;
END;
DROP FUNCTION mock_data;
SELECT mock_data() -- 执行此函数 生成一百万条数据
SELECT * FROM app_user WHERE `name`='用户10000' -- 2.034s
CREATE INDEX id_app_user_name on app_user(`name`); -- 10.910s
SELECT * FROM app_user WHERE `name`='用户10000' -- 0.084s
索引的数据结构
btree:InnoDB默认的数据结构
sql命令操作
用户表:mysql.user
权限管理的本质:对用户表进行crud
用户管理:
-- 创建用户
CREATE USER plancer IDENTIFIED BY '123456';
-- 修改密码
SET PASSWORD FOR plancer =PASSWORD('111111')
-- 重命名用户名
RENAME USER plancer TO plancer1
-- 用户授权 on *.*表示对所有库.表授权
GRANT ALL PRIVILEGES ON *.* TO plancer1
-- 查询权限
SHOW GRANTS FOR plancer1
SHOW GRANTS FOR root@localhost
-- 撤销权限
REVOKE ALL PRIVILEGES ON *.* FROM plancer1
-- 删除用户
DROP USER plancer1
备份原因:
备份方式
可视化工具导出(Navicat Premium)
命令行导入
#导出表
mysqldump -hlocalhost -uroot -p123456 school student >d:/a.sql
#导出多个表
mysqldump -hlocalhost -uroot -p123456 school student result >d:/b.sql
#导出数据库
mysqldump -hlocalhost -uroot -p123456 school >d:/c.sql
#导入,在登录状态下,切换到指定的数据库
source d:/a.sql
#导入方式二
mysql -u用户名 -p密码 库名 <文件物理地址
数据库比较复杂时,就需要设计
软件开发中,关于数据库的设计
设计数据库的步骤:
例(个人博客)
分析需求(要哪些表):
标识实体(把需求落地到每个表的字段)
标识实体之间的关系
为什么需要规范化?
三大范式
第一范式
原子性:每个字段的值不能再拆分
第二范式(在1NF基础上)
针对联合主键(多个字段构成的主键):所有字段必须依赖于主键,而不能依赖于主键的一部分
每张表只描述一件事情
第三范式(在1NF和2NF基础上)
规范性和性能不可兼得
关联查询的表不超过三张表
通过数据库的驱动把数据库和Java程序连通在一起。
在数据库驱动和应用程序之间的规范,工程师只需要面向JDBC接口编程。
1.创建测试数据库
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,'zhansan','123456','[email protected]','1980-12-04'),
(2,'lisi','123456','[email protected]','1981-12-04'),
(3,'wangwu','123456','[email protected]','1979-12-04')
2.引入jar包:mysql-connector-java-5.1.47.jar
3.编写java程序
public class jdbcFirstDemo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");//加载后,Driver放到类模板,激活Driver的静态代码块,就注册了一个Driver
//2.用户信息和url
String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
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("pwd=" + resultSet.getObject("PASSWORD"));
System.out.println("email=" + resultSet.getObject("email"));
System.out.println("birth=" + resultSet.getObject("birthday"));
System.out.println("=============");
}
//6.释放连接
resultSet.close();
statement.close();
connection.close();
}
}
JDBC程序步骤总结:
1.加载驱动
2.url,username,password
3.获取数据库对象 DriverManager
4.新建执行sql的statement对象
5.新建sql语句,并用statement执行sql,显示返回的resultSet
6.释放连接
所用类简介
DriverManager
Class.forName("com.mysql.jdbc.Driver");//加载驱动
Connection connection = DriverManager.getConnection(url, username, password);
//connection 代表数据库
//功能:数据库设置自动提交;事务提交;事务回滚
connection.rollback();
connection.commit();
connection.setAutoCommit();
URL
String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=false";
//mysql --3306
// 格式:协议://主机地址/数据库?参数1&参数2&参数3
Statement 执行SQL的对象 PrepareStatement 执行sql的对象
statement.executeQuery();//执行查询
statement.execute();//执行增删改查
statement.executeUpdate();//执行增,删,改
ResultSet 查询的结果集:返回了所有的查询结果
获得指定数据类型
resultSet.getObject();//不知道列类型情况下使用,知道列类型就使用具体的列类型
遍历,指针
resultSet.next();//移动到下一个数据
resultSet.previous();//前一个
resultSet.beforeFirst();//最前面
resultSet.afterLast();//最后面
resultSet.absolute(row);//移动到指定行
释放资源
按照资源声明倒序依次关闭
resultSet.close();
statement.close();
connection.close();
提取公共方法,将连接到数据库和获取连接,释放连接抽取到公共工具类
1、提取公共参数driver、url、username、password
2、通过类加载器导入配置文件流getResourceAsStream,参数载入文件流properties.load(in),为每个公共参数赋值
3、加载驱动
4、获取连接和释放连接的函数
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 con, 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 (con!=null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
对于不同的sql任务,只需要改变sql语句,不同任务的业务代码用同一个模板。
1、提取公共参数Connection、Statement、ResultSet
2、建立连接和创建statement
3、写sql,并用statement执行sql,对产生的ResultSet进行显示
4、调用工具类的方法释放连接
测试插入数据:
public class TestInsert {
public static void main(String[] args) {
Connection con=null;
Statement st=null;
ResultSet rs=null;
try {
con= JdbcUtils.getConnection();//第一次调用类静态方法,就读取了数据库配置文件(执行静态代码块)
st=con.createStatement();
String sql="INSERT INTO users(id,NAME,PASSWORD,email,birthday)" +
"VALUES(4,'plancer','123456','[email protected]','1997-10-10')";
int i = st.executeUpdate(sql);
if (i>0){
System.out.println("插入数据成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(con,st,rs);
}
}
}
测试删除数据:
public class TestDelete {
public static void main(String[] args) {
Connection con=null;
Statement st=null;
ResultSet rs=null;
try {
con= JdbcUtils.getConnection();//第一次调用类静态方法,就读取了数据库配置文件(执行静态代码块)
st=con.createStatement();
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(con,st,rs);
}
}
}
更改数据:
public class TestUpdate {
public static void main(String[] args) {
Connection con=null;
Statement st=null;
ResultSet rs=null;
try {
con= JdbcUtils.getConnection();//第一次调用类静态方法,就读取了数据库配置文件(执行静态代码块)
st=con.createStatement();
String sql="UPDATE users set NAME ='lancer',email='[email protected]' where id=3";
int i = st.executeUpdate(sql);
if (i>0){
System.out.println("更改数据成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(con,st,rs);
}
}
}
查询数据:
public class TestSelect {
public static void main(String[] args) {
Connection con=null;
Statement st=null;
ResultSet rs=null;
try {
con=JdbcUtils.getConnection();
st = con.createStatement();
//SQL
String sql="select * from users where id=1";
rs = st.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getString("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(con,st,rs);
}
}
}
SQL注入
sql存在漏洞,被攻击导致数据泄露,本质:sql会被拼接,拼接为name=' ' or '1=1'
1=1为true,这样,非合法的用户名和密码也能查询数据库中所有的结果,导致数据泄露。
sql注入格式:pwd输入为:'or’1=1
测试代码:
public class SQL注入 {
public static void main(String[] args) {
login(" 'or'1=1"," 'or '1=1");
}
//登录业务
public static void login(String name,String password){
Connection con=null;
Statement st=null;
ResultSet rs=null;
try {
con= JdbcUtils.getConnection();
st = con.createStatement();
//SQL
String sql="select * from users where `name` ='"+name+"' and `password`='"+password+"'";
rs = st.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getString("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(con,st,rs);
}
}
}
PreparedStatement可以防止SQL注入,且效率更高。
select测试代码:
public class TestSelect {
public static void main(String[] args) {
Connection con=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
con = JdbcUtils.getConnection();
String sql="select * from users where id = ?";
st= con.prepareStatement(sql);
st.setInt(1,4);
rs= st.executeQuery();
while (rs.next()){
System.out.println(rs.getString("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(con,st,rs);
}
}
}
防止sql注入
public class 防止SQL注入 {
public static void main(String[] args) {
login(" 'or'1=1"," 'or '1=1");
}
//登录业务
public static void login(String name,String password){
Connection con=null;
PreparedStatement st=null;
ResultSet rs=null;
try {
/*
预编译防止sql注入的本质:
把传递过来的参数当做字符(个别),而不是字符串(整体)
如果有转义字符',则将之转义
*/
con= JdbcUtils.getConnection();
String sql="select * from users where `name` =? and `password`=?";
st = con.prepareStatement(sql);
st.setString(1,name);
st.setString(2,password);
rs = st.executeQuery();
while (rs.next()){
System.out.println(rs.getString("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUtils.release(con,st,rs);
}
}
}
1.连接数据库
2.配置用户名,密码,测试连接
连接错误情况:
3.选择对应数据库
public class TestTransaction {
public static void main(String[] args) {
Connection con=null;
try {
con= JdbcUtils.getConnection();
con.setAutoCommit(false);//关闭自动提交,自动开启事务
String sql1="update account set money=money-100 where name ='A'";
PreparedStatement st1 = con.prepareStatement(sql1);
st1.executeUpdate();
int x=1/0;
String sql2="update account set money=money+100 where name ='B'";
PreparedStatement st2 = con.prepareStatement(sql2);
st2.executeUpdate();
con.commit();
System.out.println("success");
} catch (SQLException e) {
//默认自动回滚
e.printStackTrace();
}
}
}
池化技术:由于连接-释放浪费系统资源,所以准备预先资源,过来就连接准备好的
最小连接数
最大连接数
等待超时:
编写连接池,实现DataSource接口
开源数据源实现
DBCP
C3P0
Druid
DBCP
用到的jar包:
commons-dbcp-1.4、commons-pool-1.6
操作步骤:
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&uesSSL=true
username=root
password=123456
#
initialSize=10
#最大连接数量
maxActive=50
#
maxIdle=20
#
minIdle=5
#
maxWait=60000
#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:【属性名=property;】
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=utf8
#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true
#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_COMMITTED
3、创建工具类,读取配置文件,工厂模式创建数据源
public class JdbcUtils_DBCP {
private static DataSource dataSource=null;
static{
try {
//由那个类的加载器加载配置文件到输入流都可以,JdbcUtils可替换
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("dbcpdb.properties");
Properties properties = new Properties();
properties.load(in);
//工厂模式创建数据源,而不需要如自定义配置中在代码中手动设置url,name,pwd等
dataSource=BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();//自定义配置是用DriverManager.getConnection();
}
//释放连接
public static void release(Connection con, 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 (con!=null){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
4、测试DBCP,通过JdbcUtils_DBCP获取Connection
public class TestDBCP {
public static void main(String[] args) {
Connection con=null;
PreparedStatement st=null;
try {
con= JdbcUtils_DBCP.getConnection();
//编写sql,得到预编译statement
String sql="insert into users(id,`NAME`,`PASSWORD`,`email`,`birthday`) values(?,?,?,?,?)";
st=con.prepareStatement(sql);
//参数赋值
st.setInt(1,5);
st.setString(2,"lancer1");
st.setString(3,"123456");
st.setString(4,"[email protected]");
st.setDate(5,new java.sql.Date(new java.util.Date().getTime()));
//执行
int i = st.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
C3P0
用到的jar包
c3p0-0.9.55、mchange-commons-java-0.2.19
1、添加两个jar包到lib目录
2、src目录下添加c3p0.xml配置文件
3、建立c3p0Util,通过new ComboPooledDataSource(“MySQL”)的方式创建数据源(MySQL为配置文件中某个配置的名称)
4、测试c3p0,只需要通过c3p0Util获取Connection(本质上是DataSource返回的connection)和释放连接。
总结
更换数据源,DataSource接口不会变,方法不变,本质上只是更换数据源的获取方式。