摘要
老师上课讲jdbc的时候,发给我们一套连接数据库的代码。很快就讲完了,我们也能直接就拿来做个简单的管理系统了。但是,其中的很多小细节,都没深入去了解。今天就来整起。
正文
最近一直在看数学跟英语,好久没碰电脑了。这次尝试用idea,并且破解了永久版本,使用起来,跟eclipse相比,太好用啦!
今天晚上竟然刷了半个多小时的吴京的采访视频 ,我也是醉咯!不过吴京这人还挺有意思。
先附上java中jar包的maven仓库的国内镜像下载地址 ,关于JDBC也发现了一个比较好的教程网站
一、了解JDBC 概念:Java DataBase Connectivity即java数据库连接,java语言操作数据库
本质:官方(Sun)公司定义的一套操作所有关系型数据库的规则,即接口。然后各个数据库厂商去实现这套接口,提供数据库驱动jar包,开发者可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
展开
二、入门JDBC 下面这就是上课时我们所用到的代码,拿来就用。具体为什么要这么用,我也不知道,老师也说不用知道,都是这个套路。
google一下,发现网上的教程,比老师讲得清楚明了
步骤
导入驱动jar包 注册驱动 获取数据库连接对象(Connection) 定义sql 获取执行sql语句的对象(Statement) 执行sql,接收返回的结果 处理结果 释放资源 代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Demo01JDBC {
public static void main ( String [] args ) throws Exception {
//1.导入驱动jar包
//2.注册驱动
//如果mysql数据库是5+
// Class.forName("com.mysql.jdbc.Driver");
//如果mysql数据库是8+,并且,mysql5之后,是可以省略注册驱动这一代码的,jar包里面是带有自动注册的代码的
Class . forName ( "com.mysql.cj.jdbc.Driver" );
//3.获取数据库的连接对象,如果中途报错timezone的话,可以设置统一时区,UTC时间跟北京时间相差8小时,将ssl设置为false是为了防止开发过程中出现的警告
Connection conn = DriverManager . getConnection ( "jdbc:mysql://localhost:3306/beauty?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false" , "root" , "13838385638" );
//4.定义sql语句
String sql = "select * from memeber" ;
//5.获取执行sql的对象
Statement stmt = conn . createStatement ();
//6.执行sql
ResultSet rs = stmt . executeQuery ( sql );
//7.处理结果
while ( rs . next ()) {
String name = rs . getString ( "name" );
String gender = rs . getString ( "gender" );
System . out . println ( name + "->" + gender );
}
//8.释放资源
stmt . close ();
conn . close ();
}
}
展开
三、详解JDBC 3.1 DriverManager:驱动管理对象 功能
注册驱动 获取数据库连接 注册驱动 注册驱动:告诉程序使用哪一个数据库驱动jar,讲解
在DriverManager里面,有这样一个方法
1
2
static void registerDriver ( Driver driver )
// Registers the given driver with the DriverManager .
Class.forName("全类名"),在源代码阶段,将字节码文件加载进内存,并返回类对象,当然,这个是没有接收的。
通过查看源码,发现在com.mysql.cj.jdbc.Driver类中,调用了静态代码块
1
2
3
4
5
6
7
static {
try {
java . sql . DriverManager . registerDriver ( new Driver ());
} catch ( SQLException E ) {
throw new RuntimeException ( "Can't register driver!" );
}
}
mysql5之后的版本可以省略注册驱动
也就是说,我们其实可以直接使用DriverManager.registerDriver(new Driver())来代替Class.forName("xxx");
获取数据库连接对象 在DriverManager里面有这样一个方法
1
2
3
4
5
6
7
8
9
10
/*
* @param url 指定连接的路径
* jdbc:mysql://ip地址(域名):端口号/数据库名称
* 如果连接的是本机的mysql服务器,并且mysql服务默认端口是3306,则url可以简写为jdbc:mysql:///数据库名称
* @param 用户名
* @param 密码
* @return 返回Connection对象
*/
public static Connection getConnection ( String url ,
String user , String password )
3.2 Connection:数据库连接对象 功能
获取执行sql的对象 管理事务 获取执行sql的对象 Connection中有两个这样的方法
1
2
3
4
Statement createStatement ();
PreparedStatement prepareStatement ( String sql );
管理事务 开启事务
1
2
//传入false,即开启了手动提交
void setAutoCommit ( boolean autoCommit );
提交事务
回滚事务
3.3 Statement:执行sql对象 执行sql 1
2
3
4
5
6
7
8
9
//可以执行任意的sql,但是不常用
//若返回true,则表示结果是一个结果集;若false,表示修改数或没有结果
boolean execute ( String sql );
//执行DDL和DML语句,也就是对库和表进行操作的
//@return 影响的行数
int executeUpdate ( String sql );
//执行DQL语句
//@return 结果集
ResultSet executeQuery ( String sql );
练习 添加记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Demo02JDBC {
public static void main ( String [] args ) {
Connection conn = null ;
Statement stmt = null ;
try {
//1.注册驱动
Class . forName ( "com.mysql.cj.jdbc.Driver" );
//2.定义sql
String sql = "insert into memeber(name,gender,age,grade,p_id) values('武当陈师行','男',40,100,1)" ;
//3.获取Connection对象
conn = DriverManager . getConnection ( "jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai" , "root" , "13838385638" );
//4.获取执行sql的对象
stmt = conn . createStatement ();
//5.执行sql
int count = stmt . executeUpdate ( sql );
//6.打印结果
if ( count > 0 ) {
System . out . println ( "添加成功" );
} else {
System . out . println ( "添加失败" );
}
//7.释放资源
stmt . close ();
conn . close ();
} catch ( ClassNotFoundException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} finally {
//如果在执行getConnection的时候,就报错了,那么返回的对象就是空了
//避免空指针异常
if ( stmt != null ) {
try {
stmt . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
if ( conn != null ) {
try {
conn . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
}
}
}
修改记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Demo03JDBC {
public static void main ( String [] args ) {
Connection conn = null ;
Statement stmt = null ;
try {
//1.注册驱动
Class . forName ( "com.mysql.cj.jdbc.Driver" );
//2.获取连接对象
conn = DriverManager . getConnection ( "jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai" , "root" , "13838385638" );
//3.定义sql
String sql = "update memeber set grade=99 where id=2" ;
//4.获取执行sql的对象
stmt = conn . createStatement ();
//5.执行sql
int count = stmt . executeUpdate ( sql );
//6.输出结果
if ( count > 0 ) {
System . out . println ( "修改成功" );
} else {
System . out . println ( "修改失败" );
}
} catch ( ClassNotFoundException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} finally {
if ( stmt != null ) {
try {
stmt . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
if ( conn != null ) {
try {
conn . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
}
}
}
删除记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Demo04JDBC {
public static void main ( String [] args ) {
Connection conn = null ;
Statement stmt = null ;
try {
// 1.注册驱动
Class . forName ( "com.mysql.cj.jdbc.Driver" );
// 2.定义删除的sql语句
String sql = "delete from memeber where name='武当陈师行'" ;
// 3.获取连接对象
conn = DriverManager . getConnection ( "jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai" , "root" ,
"13838385638" );
// 4.获取执行sql的对象
stmt = conn . createStatement ();
// 5.执行sql语句
int count = stmt . executeUpdate ( sql );
// 6.运行结果
if ( count > 0 ) {
System . out . println ( "删除成功" );
} else {
System . out . println ( "删除失败" );
}
} catch ( ClassNotFoundException e ) {
e . printStackTrace ();
} catch ( SQLException e ) {
e . printStackTrace ();
} finally {
// 避免空指针异常
if ( stmt != null ) {
try {
stmt . close ();
} catch ( SQLException e ) {
e . printStackTrace ();
}
}
if ( conn != null ) {
try {
conn . close ();
} catch ( SQLException e ) {
e . printStackTrace ();
}
}
}
}
}
3.4 ResultSet:结果集 使用 next():游标向下移动一行,并返回boolean类型值。向下移动,若有数据,则是true,否则为false getxxx(参数):获取数据。xxx代表数据类型。 参数可以接收int,表示列的编号,从1开始,不是0,特殊。 参数可以接受String,表示列名称。 使用步骤:
游标向下移动一行 判断是否有数据 获取数据 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class Demo05JDBC {
public static void main ( String [] args ) {
Connection conn = null ;
Statement stmt = null ;
try {
// 1.注册驱动
Class . forName ( "com.mysql.cj.jdbc.Driver" );
// 2.定义删除的sql语句
String sql = "select * from memeber" ;
// 3.获取连接对象
conn = DriverManager . getConnection ( "jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai" , "root" ,
"13838385638" );
// 4.获取执行sql的对象
stmt = conn . createStatement ();
// 5.执行sql语句
ResultSet rs = stmt . executeQuery ( sql );
// 6.运行结果
// //6.1 让游标从标题栏移到第一行
// rs.next();
// //6.2 获取数据
// System.out.println(rs.getString("name")+" "+rs.getString("gender"));
while ( rs . next ()) {
System . out . println ( rs . getString ( "name" ) + " " + rs . getString ( "gender" ));
}
} catch ( ClassNotFoundException e ) {
e . printStackTrace ();
} catch ( SQLException e ) {
e . printStackTrace ();
} finally {
// 避免空指针异常
if ( stmt != null ) {
try {
stmt . close ();
} catch ( SQLException e ) {
e . printStackTrace ();
}
}
if ( conn != null ) {
try {
conn . close ();
} catch ( SQLException e ) {
e . printStackTrace ();
}
}
}
}
}
查询练习 定义一个方法,查询emp表的数据,将其封装为对象,装载集合,返回
初版本 Emp.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 封装Emp表的JavaBean
* @author kit chen
*
*/
public class Emp {
private int id ;
private String ename ;
private int job_id ;
private int mgr ;
private Date joindate ;
private double salary ;
private double bonus ;
private int dept_id ;
public int getId () {
return id ;
}
public void setId ( int id ) {
this . id = id ;
}
public String getEname () {
return ename ;
}
public void setEname ( String ename ) {
this . ename = ename ;
}
public int getJob_id () {
return job_id ;
}
public void setJob_id ( int job_id ) {
this . job_id = job_id ;
}
public int getMgr () {
return mgr ;
}
public void setMgr ( int mgr ) {
this . mgr = mgr ;
}
public Date getJoindate () {
return joindate ;
}
public void setJoindate ( Date joindate ) {
this . joindate = joindate ;
}
public double getSalary () {
return salary ;
}
public void setSalary ( double salary ) {
this . salary = salary ;
}
public double getBonus () {
return bonus ;
}
public void setBonus ( double bonus ) {
this . bonus = bonus ;
}
public int getDept_id () {
return dept_id ;
}
public void setDept_id ( int dept_id ) {
this . dept_id = dept_id ;
}
@Override
public String toString () {
return "Emp {id=" + id + ", ename=" + ename + ", job_id=" + job_id + ", mgr=" + mgr + ", joindate=" + joindate
+ ", salary=" + salary + ", bonus=" + bonus + ", dept_id=" + dept_id + "}" ;
}
}
Demo06JDBC.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
public class Demo06JDBC {
public static void main ( String [] args ) {
List < Emp > list = new Demo06JDBC (). findAll ();
System . out . println ( list );
}
/**
* 查询所有emp对象
*
* @return
*/
public List < Emp > findAll () {
List < Emp > list = null ;
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
// 1.注册驱动
Class . forName ( "com.mysql.cj.jdbc.Driver" );
// 2.获取连接对象
conn = DriverManager . getConnection ( "jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai" , "root" , "13838385638" );
// 3.定义sql
String sql = "select * from emp" ;
// 4.获取执行sql对象
stmt = conn . createStatement ();
// 5.执行sql
rs = stmt . executeQuery ( sql );
// 6.遍历结果集,装载集合
Emp emp = null ;
list = new ArrayList < Emp > ();
while ( rs . next ()) {
int id = rs . getInt ( "id" );
String ename = rs . getString ( "ename" );
int job_id = rs . getInt ( "job_id" );
int mgr = rs . getInt ( "mgr" );
// 这个date是java.sql.Date,他是继承自java.util.Date的。所以往emp中存的时候,是没问题的
Date joindate = rs . getDate ( "joindate" );
double salary = rs . getDouble ( "salary" );
double bonus = rs . getDouble ( "bonus" );
int dept_id = rs . getInt ( "dept_id" );
// 创建对象
//在javaBean中,若参数在4个以上,便不推荐使用有参构造函数
emp = new Emp ();
emp . setId ( id );
emp . setEname ( ename );
emp . setJob_id ( job_id );
emp . setMgr ( mgr );
emp . setJoindate ( joindate );
emp . setSalary ( salary );
emp . setBonus ( bonus );
emp . setDept_id ( dept_id );
// 装载集合
list . add ( emp );
}
} catch ( ClassNotFoundException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} finally {
//一定不要忘记释放资源
if ( rs != null ) {
try {
rs . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
if ( stmt != null ) {
try {
stmt . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
if ( conn != null ) {
try {
conn . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
}
return list ;
}
}
优化版本 目的:简化书写
步骤:
抽取注册驱动 抽取一个方法来获取连接对象需求:不传递参数,同时还要保证工具类的通用性 解决:配置文件,在之前的博客 也有提到使用方法 抽取一个方法来释放资源 util.JDBCUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public class JDBCUtils {
/**
* 只有静态变量才能被静态代码块所访问
*/
private static String url ;
private static String user ;
private static String password ;
private static String driver ;
/**
* 静态代码块,只需要读取一次拿到这些值
*/
static {
//读取资源文件,获取值
//1.创建Properties集合类
Properties pro = new Properties ();
try {
//2.加载文件
// pro.load(new FileInputStream("src/demo45/jdbc.properties"));
//另一种加载方式
//获取src路径下的文件的方式-->ClassLoader类加载器
ClassLoader loader = JDBCUtils . class . getClassLoader ();
URL res = loader . getResource ( "jdbc.properties" );
String path = res . getPath ();
pro . load ( new FileInputStream ( path ));
//3.获取属性赋值
url = pro . getProperty ( "url" );
user = pro . getProperty ( "user" );
password = pro . getProperty ( "password" );
driver = pro . getProperty ( "driver" );
//4.注册驱动
Class . forName ( driver );
} catch ( ClassNotFoundException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( FileNotFoundException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( IOException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
/**
* 获取连接
*
* @return 连接对象
*/
public static Connection getConnection () throws SQLException {
Connection conn = null ;
conn = DriverManager . getConnection ( url , user , password );
return conn ;
}
/**
* 释放资源
*
* @param stmt
* @param conn
*/
public static void close ( Statement stmt , Connection conn ) {
if ( stmt != null ) {
try {
stmt . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
if ( conn != null ) {
try {
conn . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
}
/**
* 释放资源的重载形式
*
* @param rs
* @param stmt
* @param conn
*/
public static void close ( ResultSet rs , Statement stmt , Connection conn ) {
if ( rs != null ) {
try {
rs . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
if ( stmt != null ) {
try {
stmt . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
if ( conn != null ) {
try {
conn . close ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
}
}
jdbc.JDBC.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/**
*
* @author kit chen
*
*/
public class JDBC {
public static void main ( String [] args ) {
List < Emp > list = new JDBC (). findAll ();
System . out . println ( list );
}
/**
* 查询数据,并返回
* @return
*/
public List < Emp > findAll () {
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
Emp emp = null ;
List < Emp > list = null ;
try {
conn = JDBCUtils . getConnection ();
stmt = conn . createStatement ();
String sql = "select * from emp" ;
rs = stmt . executeQuery ( sql );
list = new ArrayList < Emp > ();
while ( rs . next ()) {
int id = rs . getInt ( "id" );
String ename = rs . getString ( "ename" );
int job_id = rs . getInt ( "job_id" );
int mgr = rs . getInt ( "mgr" );
// 这个date是java.sql.Date,他是继承自java.util.Date的。所以往emp中存的时候,是没问题的
Date joindate = rs . getDate ( "joindate" );
double salary = rs . getDouble ( "salary" );
double bonus = rs . getDouble ( "bonus" );
int dept_id = rs . getInt ( "dept_id" );
// 创建对象
//在javaBean中,若参数在4个以上,便不推荐使用有参构造函数
emp = new Emp ();
emp . setId ( id );
emp . setEname ( ename );
emp . setJob_id ( job_id );
emp . setMgr ( mgr );
emp . setJoindate ( joindate );
emp . setSalary ( salary );
emp . setBonus ( bonus );
emp . setDept_id ( dept_id );
// 装载集合
list . add ( emp );
}
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} finally {
JDBCUtils . close ( rs , stmt , conn );
}
return list ;
}
}
domain.Emp.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
/**
* 封装Emp表的JavaBean
* @author kit chen
*
*/
public class Emp {
private int id ;
private String ename ;
private int job_id ;
private int mgr ;
private Date joindate ;
private double salary ;
private double bonus ;
private int dept_id ;
public int getId () {
return id ;
}
public void setId ( int id ) {
this . id = id ;
}
public String getEname () {
return ename ;
}
public void setEname ( String ename ) {
this . ename = ename ;
}
public int getJob_id () {
return job_id ;
}
public void setJob_id ( int job_id ) {
this . job_id = job_id ;
}
public int getMgr () {
return mgr ;
}
public void setMgr ( int mgr ) {
this . mgr = mgr ;
}
public Date getJoindate () {
return joindate ;
}
public void setJoindate ( Date joindate ) {
this . joindate = joindate ;
}
public double getSalary () {
return salary ;
}
public void setSalary ( double salary ) {
this . salary = salary ;
}
public double getBonus () {
return bonus ;
}
public void setBonus ( double bonus ) {
this . bonus = bonus ;
}
public int getDept_id () {
return dept_id ;
}
public void setDept_id ( int dept_id ) {
this . dept_id = dept_id ;
}
@Override
public String toString () {
return "Emp {id=" + id + ", ename=" + ename + ", job_id=" + job_id + ", mgr=" + mgr + ", joindate=" + joindate
+ ", salary=" + salary + ", bonus=" + bonus + ", dept_id=" + dept_id + "}" ;
}
}
jdbc.properties
1
2
3
4
url = jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai
user = root
password = 13838385638
driver = com.mysql.cj.jdbc.Driver
登录练习 需求:
通过键盘录入用户名和密码 判断用户是否登录成功 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class Demo07JDBC {
public static void main ( String [] args ) {
Scanner input = new Scanner ( System . in );
String user = input . nextLine ();
String password = input . nextLine ();
if ( login ( user , password )) {
System . out . println ( "登录成功" );
} else {
System . out . println ( "登录失败" );
}
input . close ();
}
/**
* 登录方法
* @param user
* @param password
* @return
*/
public static boolean login ( String user , String password ) {
if ( user == null || password == null ) {
return false ;
}
//连接数据库判断是否登录成功
Connection conn = null ;
Statement stmt = null ;
ResultSet rs = null ;
try {
//1.获取连接
conn = JDBCUtils . getConnection ();
//2.定义sql
String sql = "select * from user where user='" + user + "' and password='" + password + "'" ;
//3.获取执行sql的对象
stmt = conn . createStatement ();
//4.执行查询
rs = stmt . executeQuery ( sql );
//5.结果
return rs . next ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} finally {
JDBCUtils . close ( rs , stmt , conn );
}
//若出现异常,返回false
return false ;
}
}
但是,这样登录,会出现sql注入的问题。
假如,密码输入的是a' or 'a' = 'a ,那么整个sql语句select * from user where user='fjaljfl' and password='a' or 'a' = 'a'
不论怎么查,都会返回数据
3.5 PreparedStatement:执行sql的对象 sql注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性的问题 解决sql注入问题:使用PreparedStatement对象来解决 静态sql:直接拼接而成的sql,这种会产生sql注入问题
预编译sql:参数使用**?**作为占位符
JDBC的步骤也会相应改变
导入驱动jar包 注册驱动 获取数据库连接对象(Connection) 定义sql,参数使用占位符select * from where user = ? and password = ?; 获取执行sql语句的对象(PreparedStatement) 给占位符赋值方法:setXXX(参数1,参数2) 参数1:位置的编号。从1开始 参数2:值 执行sql,接收返回的结果 处理结果 释放资源 改造上面的登录案例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
public class Demo08JDBC {
public static void main ( String [] args ) {
Scanner input = new Scanner ( System . in );
String user = input . nextLine ();
String password = input . nextLine ();
if ( login ( user , password )) {
System . out . println ( "登录成功" );
} else {
System . out . println ( "登录失败" );
}
input . close ();
}
/**
* 登录方法
* @param user
* @param password
* @return
*/
public static boolean login ( String user , String password ) {
if ( user == null || password == null ) {
return false ;
}
//连接数据库判断是否登录成功
Connection conn = null ;
PreparedStatement pstmt = null ;
ResultSet rs = null ;
try {
//1.获取连接
conn = JDBCUtils . getConnection ();
//2.定义sql
String sql = "select * from user where user= ? and password= ?" ;
//3.获取执行sql的对象
pstmt = conn . prepareStatement ( sql );
//4.赋值
pstmt . setString ( 1 , user );
pstmt . setString ( 2 , password );
//5.执行查询
rs = pstmt . executeQuery ();
//6.结果
return rs . next ();
} catch ( SQLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} finally {
JDBCUtils . close ( rs , pstmt , conn );
}
//若出现异常,返回false
return false ;
}
}
实际开发中经常使用PreparedStatement来完成增删改查操作
防止sql注入 效率更高 四、JDBC管理事务 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
操作
开启事务 提交事务 回滚事务 使用Connection对象来管理事务。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class Demo09JDBC {
public static void main ( String [] args ) {
Connection conn = null ;
PreparedStatement pstmt1 = null ;
PreparedStatement pstmt2 = null ;
try {
// 1.获取连接
conn = JDBCUtils . getConnection ();
//开启事务
conn . setAutoCommit ( false );
// 2.定义sql
// 2.1 张三-500
String sql1 = "update account set balance = balance-? where id=?" ;
// 2.2 李四+500
String sql2 = "update account set balance = balance+? where id=?" ;
//3.获取执行sql对象
pstmt1 = conn . prepareStatement ( sql1 );
pstmt2 = conn . prepareStatement ( sql2 );
//4.设置参数
pstmt1 . setDouble ( 1 , 500 );
pstmt1 . setInt ( 2 , 1 );
pstmt2 . setDouble ( 1 , 500 );
pstmt2 . setInt ( 2 , 2 );
//5.执行sql
pstmt1 . executeUpdate ();
//手动制造异常
int i = 3 / 0 ;
pstmt2 . executeUpdate ();
//提交事务
conn . commit ();
} catch ( Exception e ) {
// TODO: handle exception
//不管出现什么异常,事务都要回滚
try {
if ( conn != null ) {
conn . rollback ();
}
} catch ( SQLException e1 ) {
// TODO Auto-generated catch block
e1 . printStackTrace ();
}
e . printStackTrace ();
} finally {
JDBCUtils . close ( pstmt1 , conn );
JDBCUtils . close ( pstmt2 , null );
}
}
}
五、数据库连接池 概念:本质是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
好处
节约资源 用户访问过程变得高效 实现
标准接口:DataSource,javax.sql包下的官方提供的接口获取连接:getConnection() 归还连接:如果连接对象Connection是从连接池中获取的,那么调用Connection.close()则不会关闭连接,而是归还连接。 一般是由数据库厂商来实现,推荐三种数据库连接池技术 C3P0:比较老 Druid:由阿里巴巴提供的,而且高效。号称全球最好?! DBCP:apache上的java连接池项目,也是tomcat使用的连接池组件。 5.1 C3P0 在这之前,我要强烈吐槽eclipse!
我用的eclipse最新版2019.06,搞这个数据库连接池的时候,eclipse成功把我推向了idea。主要就是在新版jdk中,eclipse是读不到配置文件的,无论你放到哪里,他都读不到。但是如果把jdk降为1.8,就可以了。
同时,还有个坑,我如果一开始建的项目以jdk1.8为准,后期再换成jdk新版,哎,他又可以用了,玄学啊!如果一开始直接建以最新jdk的项目,他是不能导出jar包的,会无缘无故失败。但是换成1.8,再换成新版,又可以了,又是玄学啊!
而idea,不论是哪个版本jdk,都能完美运行,这就是收费跟不收费软件的区别吗?
发现这个问题的,还有一个小伙伴,他的博客
eclipse,我以后不会再用了。idea,他不香吗?
c3p0文档
c3p0配置文件
步骤 导入jar包 c3p0.jar跟mchange.jar 定义配置文件 c3p0.properties或者c3p0-config.xml,直接将文件放置到src下面即可 创建核心对象 数据库连接池对象 ComboPooledDataSource 获取连接 getConnection 实现
c3p0-config.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<c3p0-config>
<!-- 默认配置,如果没有指定则使用这个配置 -->
<default-config>
<!-- 连接参数 -->
<property name= "user" > root</property>
<property name= "password" > 13838385638</property>
<property name= "jdbcUrl" > jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai</property>
<property name= "driverClass" > com.mysql.cj.jdbc.Driver</property>
<!-- 连接池参数 -->
<!-- 初始化数据库连接池时连接的数量 -->
<property name= "initialPoolSize" > 5</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name= "maxPoolSize" > 5</property>
<!-- 超时时间 -->
<property name= "checkoutTimeout" > 3000</property>
</default-config>
<!-- 命名的配置,可以通过方法调用实现 -->
<named-config name= "otherc3p0" >
<!-- 连接参数 -->
<property name= "user" > root</property>
<property name= "password" > 13838385638</property>
<property name= "jdbcUrl" > jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai</property>
<property name= "driverClass" > com.mysql.cj.jdbc.Driver</property>
<!-- 连接池参数 -->
<!-- 初始化数据库连接池时连接的数量 -->
<property name= "initialPoolSize" > 5</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name= "maxPoolSize" > 5</property>
<!-- 超时时间 -->
<property name= "checkoutTimeout" > 3000</property>
</named-config>
</c3p0-config>
当然,也可以用c3p0.properties
1
2
3
4
5
6
7
8
9
c3p0.driverClass = com.mysql.cj.jdbc.Driver
c3p0.jdbcUrl = jdbc:mysql://localhost:3306/beauty?serverTimezone=Asia/Shanghai
c3p0.user = root
c3p0.password = 13838385638
c3p0.acquireIncrement = 5
c3p0.initialPoolSize = 20
c3p0.minPoolSize = 10
c3p0.maxPoolSize = 40
c3p0.checkoutTimeout = 5000
代码 1
2
3
4
5
6
7
8
9
10
11
12
13
public class Demo01c3p0 {
public static void main ( String [] args ) throws SQLException {
//1.创建数据库连接池对象
//使用默认配置
// DataSource ds=new ComboPooledDataSource();
//使用指定配置
DataSource ds = new ComboPooledDataSource ( "otherc3p0" );
//2.获取连接对象
Connection conn = ds . getConnection ();
System . out . println ( conn );
System . out . println ();
}
}
5.2 Druid 步骤 导入jar包 定义配置文件.propertiesproperties格式 任意名称,可以放在任意目录下 加载配置文件 获取数据库连接池对象:通过工厂类DruidDataSourceFactory,来获取 获取连接:getConnection 配置文件
1
2
3
4
5
6
7
8
# Druid配置文件
driverClassName = com.mysql.cj.jdbc.Driver
url = jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai
username = root
password = 13838385638
initialSize = 5
maxActive = 10
maxWait = 3000
代码 1
2
3
4
5
6
7
8
9
10
11
12
public class Demo03Druid {
public static void main ( String [] args ) throws Exception {
//加载配置文件
Properties pro = new Properties ();
pro . load ( Demo03Druid . class . getClassLoader (). getResourceAsStream ( "druid.properties" ));
//获取连接池对象
DataSource ds = DruidDataSourceFactory . createDataSource ( pro );
//获取连接
Connection conn = ds . getConnection ();
System . out . println ( conn );
}
}
5.3 定义连接池工具类 步骤 定义类JDBCPoolUtils 提供静态代码块,来加载配置文件,并且初始化连接池对象 提供方法获取连接方法:getConnection 释放资源:close() 获取连接池的方法:getDataSource 实现 JDBCPoolUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
/**
* Druid连接池的工具类
*/
public class JDBCPoolUtils {
//1.定义一个成员变量
private static DataSource ds ;
//2.静态代码块初始化赋值
static {
//加载配置文件
Properties pro = new Properties ();
try {
pro . load ( JDBCPoolUtils . class . getClassLoader (). getResourceAsStream ( "druid.properties" ));
//初始化DataSource
ds = DruidDataSourceFactory . createDataSource ( pro );
} catch ( IOException e ) {
e . printStackTrace ();
} catch ( Exception e ) {
e . printStackTrace ();
}
}
/**
* @return 获取到的连接对象
*/
public static Connection getConnection () throws SQLException {
return ds . getConnection ();
}
/**
* 释放资源
*/
// public static void close(Statement stmt, Connection conn){
// if(stmt!=null){
// try {
// stmt.close();
// } catch (SQLException e) {
// e.printStackTrace();
// }
// }
// if(conn!=null){
// try {
// conn.close();//归还连接
// } catch (SQLException e) {
// e.printStackTrace();
// }
// }
// }
public static void close ( ResultSet rs , Statement stmt , Connection conn ){
if ( rs != null ){
try {
rs . close ();
} catch ( SQLException e ) {
e . printStackTrace ();
}
}
if ( stmt != null ){
try {
stmt . close ();
} catch ( SQLException e ) {
e . printStackTrace ();
}
}
if ( conn != null ){
try {
conn . close (); //归还连接
} catch ( SQLException e ) {
e . printStackTrace ();
}
}
}
/**
*
* @return 获取连接池对象
*/
public static DataSource getDataSource (){
return ds ;
}
}
DruidMain.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* 使用工具类JDBCPoolUtils
*/
public class DruidMain {
public static void main ( String [] args ) {
Connection conn = null ;
ResultSet rs = null ;
PreparedStatement pstmt = null ;
try {
conn = JDBCPoolUtils . getConnection ();
// String sql = "select * from memeber";
// pstmt = conn.prepareStatement(sql);
// rs = pstmt.executeQuery();
// while (rs.next()) {
// System.out.println(rs.getString("name") + "-->"
// + rs.getString("gender"));
// }
String sql = "update memeber set gender=? where name=?" ;
pstmt = conn . prepareStatement ( sql );
pstmt . setString ( 1 , "男" );
pstmt . setString ( 2 , "唐三" );
int count = pstmt . executeUpdate ();
System . out . println ( count );
if ( count > 0 ){
System . out . println ( "成功" );
} else {
System . out . println ( "失败" );
}
} catch ( SQLException e ) {
e . printStackTrace ();
} finally {
JDBCPoolUtils . close ( rs , pstmt , conn );
}
}
}
六、Spring JDBC Spring框架对JDBC的简单封装。提供了JDBCTemplate来简化JDBC的开发
上学期课程设计的时候,我就觉得这部分比较麻烦,问老师,老师也支支吾吾说不明白。老师大概也就只要个功能,具体的实现优化,他才不管,我猜,他也不会。
报告管理信息系统 ,说实话,写得很幼稚。
步骤 导入jar包 core.jar jdbc.jar tx.jar beans.jar commons-logging.jar 创建JDBCTemplate对象。依赖于数据源DataSourceJDBCTemlate temp=new JDBCTemplate(ds); 调用方法来CRUD(增删改)操作update():执行DML语句。增删改 queryForMap():查询结果,将结果集封装为Map。列名作为key,值作为value,将这条记录封装为map集合。(长度只能为1) queryForList():查询结果,将将结果集封装为List。将每一条Map封装为Map集合,再将Map集合装载到List集合中 query():查询结果,将结果封装为javaBean对象query的参数:RowMapper。一般我们使用BeanPropertyRowMapper实现类,它可以完成数据到javaBean的自动封装 使用方法:new BeanPropertyRowMapper<类名>(类名.Class) queryForObject():查询结果,将结果封装为指定对象一般用于聚合函数的查询。比方说返回的结果是Long类型,就需要将结果封装 1
2
3
4
5
6
7
8
9
10
public class Demo01JDBCTemplate {
public static void main ( String [] args ) {
//创建JdbcTemplate
JdbcTemplate temp = new JdbcTemplate ( JDBCPoolUtils . getDataSource ());
//调用方法
String sql = "update memeber set gender=? where name=?" ;
int count = temp . update ( sql , "男" , "唐三" );
System . out . println ( count );
}
}
使用 需求
修改id为1数据salary为10000 添加一条记录 删除刚才添加的记录 查询id为1的记录,将其封装为Map集合 查询所有记录,封装为List集合 查询所有记录,封装为Emp对象的List集合 查询总记录数 实现
通过Junit单元测试来实现每个需求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/**
* 使用Spring以及Junit单元测试
*
* @author https://github.com/meethigher
*/
public class Demo02JDBCTemplate {
private JdbcTemplate temp = new JdbcTemplate ( JDBCPoolUtils . getDataSource ());
//修改id为1001数据salary为10000
@Test
public void test1 () {
String sql = "update emp set salary=10000 where id=1001" ;
int count = temp . update ( sql );
if ( count > 0 ) {
System . out . println ( "成功" );
} else {
System . out . println ( "失败" );
}
}
//添加一条记录
@Test
public void test2 () {
String sql = "insert into emp(id,ename,dept_id) values(?,?,?)" ;
int count = temp . update ( sql , 1015 , "胡列娜" , 10 );
if ( count > 0 ) {
System . out . println ( "成功" );
} else {
System . out . println ( "失败" );
}
}
//删除刚添加的记录
@Test
public void test3 () {
String sql = "delete from emp where id=?" ;
int count = temp . update ( sql , 1015 );
if ( count > 0 ) {
System . out . println ( "成功" );
} else {
System . out . println ( "失败" );
}
}
//查询id为1001的记录,将其封装为Map集合
@Test
public void test4 () {
String sql = "select * from emp where id=?" ;
Map < String , Object > map = temp . queryForMap ( sql , 1001 );
System . out . println ( map );
}
//查询所有记录,将其封装为List集合
@Test
public void test5 () {
String sql = "select * from emp" ;
List < Map < String , Object >> list = temp . queryForList ( sql );
for ( Map < String , Object > map : list ) {
System . out . println ( map );
}
}
//查询所有记录,将其封装为Emp对象的List集合
//自己实现
@Test
public void test6 () {
String sql = "select * from emp" ;
List < Emp > list = temp . query ( sql , new RowMapper < Emp > () {
@Override
public Emp mapRow ( ResultSet rs , int i ) throws SQLException {
Emp emp = new Emp ();
int id = rs . getInt ( "id" );
String ename = rs . getString ( "ename" );
int job_id = rs . getInt ( "job_id" );
int mgr = rs . getInt ( "mgr" );
Date joindate = rs . getDate ( "joindate" );
double salary = rs . getDouble ( "salary" );
double bonus = rs . getDouble ( "bonus" );
int dept_id = rs . getInt ( "dept_id" );
emp . setId ( id );
emp . setEname ( ename );
emp . setBonus ( bonus );
emp . setDept_id ( dept_id );
emp . setJoindate ( joindate );
emp . setMgr ( mgr );
emp . setSalary ( salary );
emp . setJob_id ( job_id );
return emp ;
}
});
for ( Emp emp : list ) {
System . out . println ( emp );
}
}
//查询所有记录,将其封装为Emp对象的List集合
//用别人提供的实现类
@Test
public void test7 () {
String sql = "select * from emp" ;
/*
此处如果数据库的数据中是null的话,传给int或者double这种基本类型是会报错的
所以,需要修改成引用类型Integer和Double
*/
List < Emp1 > list = temp . query ( sql , new BeanPropertyRowMapper < Emp1 > ( Emp1 . class ));
for ( Emp1 emp : list ) {
System . out . println ( emp );
}
}
//查询总记录数
@Test
public void test8 () {
String sql = "select count(id) from emp" ;
Long total = temp . queryForObject ( sql , Long . class );
System . out . println ( total );
}
}