摘要

老师上课讲jdbc的时候,发给我们一套连接数据库的代码。很快就讲完了,我们也能直接就拿来做个简单的管理系统了。但是,其中的很多小细节,都没深入去了解。今天就来整起。

正文

最近一直在看数学跟英语,好久没碰电脑了。这次尝试用idea,并且破解了永久版本,使用起来,跟eclipse相比,太好用啦!

今天晚上竟然刷了半个多小时的吴京的采访视频,我也是醉咯!不过吴京这人还挺有意思。

先附上java中jar包的maven仓库的国内镜像下载地址,关于JDBC也发现了一个比较好的教程网站

一、了解JDBC

概念:Java DataBase Connectivity即java数据库连接,java语言操作数据库

本质:官方(Sun)公司定义的一套操作所有关系型数据库的规则,即接口。然后各个数据库厂商去实现这套接口,提供数据库驱动jar包,开发者可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类

1.png

二、入门JDBC

下面这就是上课时我们所用到的代码,拿来就用。具体为什么要这么用,我也不知道,老师也说不用知道,都是这个套路。

google一下,发现网上的教程,比老师讲得清楚明了

步骤

  1. 导入驱动jar包
  2. 注册驱动
  3. 获取数据库连接对象(Connection)
  4. 定义sql
  5. 获取执行sql语句的对象(Statement)
  6. 执行sql,接收返回的结果
  7. 处理结果
  8. 释放资源

代码

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
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();
	}
}

2.png

三、详解JDBC

3.1 DriverManager:驱动管理对象

功能

  1. 注册驱动
  2. 获取数据库连接

注册驱动

注册驱动:告诉程序使用哪一个数据库驱动jar,讲解

在DriverManager里面,有这样一个方法

java
1
2
static void	registerDriver (Driver driver)	
//Registers the given driver with the DriverManager.

Class.forName("全类名"),在源代码阶段,将字节码文件加载进内存,并返回类对象,当然,这个是没有接收的。

通过查看源码,发现在com.mysql.cj.jdbc.Driver类中,调用了静态代码块

java
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里面有这样一个方法

java
 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:数据库连接对象

功能

  1. 获取执行sql的对象
  2. 管理事务

获取执行sql的对象

Connection中有两个这样的方法

java
1
2
3
4

Statement createStatement();

PreparedStatement prepareStatement(String sql);

管理事务

开启事务

java
1
2
//传入false,即开启了手动提交
void setAutoCommit(boolean autoCommit);

提交事务

java
1
void commit()

回滚事务

java
1
void rollback()

3.3 Statement:执行sql对象

执行sql

java
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);

练习

添加记录

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
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();
				}
			}
		}
	}
}

修改记录

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
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();
				}
			}
		}
	}
}

删除记录

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
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:结果集

使用

  1. next():游标向下移动一行,并返回boolean类型值。向下移动,若有数据,则是true,否则为false
  2. getxxx(参数):获取数据。
    • xxx代表数据类型。
    • 参数可以接收int,表示列的编号,从1开始,不是0,特殊。
    • 参数可以接受String,表示列名称。

使用步骤:

  1. 游标向下移动一行
  2. 判断是否有数据
  3. 获取数据
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
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

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

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;
	}
}

优化版本

目的:简化书写

步骤:

  1. 抽取注册驱动
  2. 抽取一个方法来获取连接对象
    • 需求:不传递参数,同时还要保证工具类的通用性
    • 解决:配置文件,在之前的博客也有提到使用方法
  3. 抽取一个方法来释放资源

util.JDBCUtils.java

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

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

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

properties
1
2
3
4
url=jdbc:mysql:///beauty?serverTimezone=Asia/Shanghai
user=root
password=13838385638
driver=com.mysql.cj.jdbc.Driver

登录练习

需求:

  1. 通过键盘录入用户名和密码
  2. 判断用户是否登录成功
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
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的对象

  1. sql注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性的问题
  2. 解决sql注入问题:使用PreparedStatement对象来解决

静态sql:直接拼接而成的sql,这种会产生sql注入问题

预编译sql:参数使用**?**作为占位符

JDBC的步骤也会相应改变

  1. 导入驱动jar包
  2. 注册驱动
  3. 获取数据库连接对象(Connection)
  4. 定义sql,参数使用占位符
    • select * from where user = ? and password = ?;
  5. 获取执行sql语句的对象(PreparedStatement)
  6. 给占位符赋值
    • 方法:setXXX(参数1,参数2)
    • 参数1:位置的编号。从1开始
    • 参数2:值
  7. 执行sql,接收返回的结果
  8. 处理结果
  9. 释放资源

改造上面的登录案例

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
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来完成增删改查操作

  1. 防止sql注入
  2. 效率更高

四、JDBC管理事务

事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

操作

  1. 开启事务
  2. 提交事务
  3. 回滚事务

使用Connection对象来管理事务。

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
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);
		}
	}
}

五、数据库连接池

概念:本质是一个容器(集合),存放数据库连接的容器。

当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。

好处

  1. 节约资源
  2. 用户访问过程变得高效

实现

  1. 标准接口:DataSource,javax.sql包下的官方提供的接口
    • 获取连接:getConnection()
    • 归还连接:如果连接对象Connection是从连接池中获取的,那么调用Connection.close()则不会关闭连接,而是归还连接。
  2. 一般是由数据库厂商来实现,推荐三种数据库连接池技术
    • 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配置文件

步骤

  1. 导入jar包 c3p0.jar跟mchange.jar
  2. 定义配置文件 c3p0.properties或者c3p0-config.xml,直接将文件放置到src下面即可
  3. 创建核心对象 数据库连接池对象 ComboPooledDataSource
  4. 获取连接 getConnection

实现

c3p0-config.xml

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

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

代码

java
 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

步骤

  1. 导入jar包
  2. 定义配置文件.properties
    • properties格式
    • 任意名称,可以放在任意目录下
  3. 加载配置文件
  4. 获取数据库连接池对象:通过工厂类DruidDataSourceFactory,来获取
  5. 获取连接:getConnection

配置文件

properties
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

代码

java
 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 定义连接池工具类

步骤

  1. 定义类JDBCPoolUtils
  2. 提供静态代码块,来加载配置文件,并且初始化连接池对象
  3. 提供方法
    • 获取连接方法:getConnection
    • 释放资源:close()
    • 获取连接池的方法:getDataSource

实现

JDBCPoolUtils.java

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

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的开发

上学期课程设计的时候,我就觉得这部分比较麻烦,问老师,老师也支支吾吾说不明白。老师大概也就只要个功能,具体的实现优化,他才不管,我猜,他也不会。

报告管理信息系统,说实话,写得很幼稚。

步骤

  1. 导入jar包 core.jar jdbc.jar tx.jar beans.jar commons-logging.jar
  2. 创建JDBCTemplate对象。依赖于数据源DataSource
    • JDBCTemlate temp=new JDBCTemplate(ds);
  3. 调用方法来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类型,就需要将结果封装
java
 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);
    }
}

使用

需求

  1. 修改id为1数据salary为10000
  2. 添加一条记录
  3. 删除刚才添加的记录
  4. 查询id为1的记录,将其封装为Map集合
  5. 查询所有记录,封装为List集合
  6. 查询所有记录,封装为Emp对象的List集合
  7. 查询总记录数

实现

通过Junit单元测试来实现每个需求

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
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);
    }
}