前言,本篇博客适合初学的java开发者阅读,了解一些底层的原理和思想。实际在工作中这些东西并不会再接触(除C3P0),但是万变不离其宗!底层原理原理和思想非常重要!如需更正请详细指出,相互学习,感谢。
所需jar包
要在Java项目中连接数据库,必须需要mysql驱动包.
C3P0.
DBUtils.
原生五步法
要使用Java代码连接数据库,并操作数据库,就要导入msyql包,原生操作数据库分为以下五步:
1 | // 1.注册驱动 |
分析
第一步:
1 | // 1.注册驱动 |
我们在register驱动时,是new Driver(); 但当我们进入到源码却发现,在静态代码块中已经注册了一次,此时会想,我们在写代码时已经注册了一次,再写一次相当于注册了两次,这样肯定是不合理的,根据源码我们可以发现DriverManage.registerDriver(new Driver); 是写在静态代码块中!那么怎么让这个静态代码块执行一次,不就注册了吗?而且是注册一次!这里牵扯到Java程序运行时先将.Java文件打包成.class文件,然后类加载器处理,所以我们这里只需要将msyql jar 包下的 Driver类加载一次就行了,代码如下:
1 | Class.forName("com.mysql.jdbc.Driver"); // 1.注册驱动 |
mysql 包下Driver源码如下:
第二步:
在配置连接MySQL参数时,直接是写在源码中,在实际开发中如果不使用框架也不会直接写在源码中,而会写在配置文件中,并将其封装成一个utils工具类。写入配置文件,即:.properties结尾的文件,示例如下:
- 注意事项:.properties配置文件中,可以理解为以键值对的形式书写、不能有空格、键值对直接必须以‘=’连接
下面我给大家介绍两种实用的读取配置文件的方式:JDBCUtils是自己封装的一个工具类,蓝色标记处即为读取配置文件方式
方式一:通过I/O流读取,其中有直接通过传入相对路径文件和使用类加载器两种方式
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
61public class JDBCUtil {
private static String password;
private static String user;
private static String url;
private static String driverClassName;
static{
//从配置文件中读取这些成员变量的值,并设置好
Properties properties = new Properties();
//让properties对象和jdbcinfo.properties文件发生关系
InputStream inStream = null;
try {
/*//第一种:将文件转换成字节输入流
InputStream inStream = new FileInputStream("src/jdbcinfo.properties");*/
//第二种:使用类加载器
//1.获取类加载器对象
Class clazz = JDBCUtil.class;
ClassLoader classLoader = clazz.getClassLoader();
inStream = classLoader.getResourceAsStream("jdbcinfo.properties");
//将字节输入流加载到properties对象中
properties.load(inStream);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 读取配置文件
password = properties.getProperty("password");
user = properties.getProperty("user");
url = properties.getProperty("url");
driverClassName = properties.getProperty("driverClassName");
//1.注册驱动,在静态代码块中进行注册,防止了重复注册驱动。
try {
Class.forName(driverClassName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws Exception{
//2.获得连接
//新创建一个连接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
public static void close(Connection conn,Statement statement,ResultSet resultSet) throws SQLException{
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (conn != null) {
conn.close();
}
}
}方式二:使用ResourceBundle类,相比于I/O流还是简单一些
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
36public class JDBCUtils {
static String url;
static String user;
static String password;
static String driver;
static {
// 读取配置文件
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
url = bundle.getString("url");
user = bundle.getString("user");
password = bundle.getString("password");
driver = bundle.getString("driver");
System.out.println(url + user + password + driver);
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 获得连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
// 关闭资源
public static void release(ResultSet resultSet, Statement statement, Connection connection) throws SQLException {
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
}
}
第三步:
在将注册驱动、读取配置文件和获取连接封装成一个工具类之后再看一下连接、操作数据库的代码,这里有个SQL语句注入的问题,所以我将Statement改成类PreparedStatement。PreparedStatement是先记住了SQL语句的格式,然后再通过setString方式传入参数,而传入参数是以1开始:
1 | public class JDBCDemo01 { |
C3P0
这个jar包是对连接数据的一个优化库,内部主要重点是连接池的概念,对连接数据库、操作数据库在性能上有了一定的提升.
- 相信C3P0是大多数Java程序员操作数据库所使用的第一个jar包吧,c3p0包中重点是理解连接池的概念。在操作数据库中,sun公司制定了一套规范,说白了就是接口,c3p0就是实现了DataSource接口。
- 在不是用c3p0包会出现什么问题呢? 每次操作数据库都要获取一个连接对象吗? 假如有10万个用户同时访问数据库,我们需要同时创建10万个connection吗?显然是NO!!!所以就有了连接池的概念,在程序运行时就创建若干个连接对象并放入集合中(LinkedList集合),当我们需要使用时就从集合中拿到第一个连接对象(connection),用完后就还回到集合中(addLast()),这样就大大的减少了内存提高了性能。这时会有一个问题,当访问数大于连接池的count时,What do I do?这时我们就设置一个等待时间,当等待时间都过去了就重新获取一个新的连接,这样就解决了。
- C3P0里面还使用了一个设计模式:装饰者模式
- 在使用C3P0时,有两种方式,一种也是通过配置文件读取连接数据库的参数,另一种是写一个c3p0-config.xml文件,c3p0内部会自己解析此文件:
- 方式一:通过配置文件读取连接数据库的参数
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
34public class C3P0Util {
public static Connection getConnection() throws SQLException, PropertyVetoException{
//从C3P0连接池对象中获取连接
//1.创建c3p0连接池对象
ComboPooledDataSource cpds = new ComboPooledDataSource();
// 读取配置文件
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
String driver = bundle.getString("driver");
//配置连接池,要将用户名、密码、url等等设置一下
cpds.setUser(user);
cpds.setInitialPoolSize(10);
cpds.setPassword(password);
cpds.setJdbcUrl(url);
//设置驱动类的类名
cpds.setDriverClass(driver);
//2.从这个连接池对象中获取连接
Connection connection = cpds.getConnection();
return connection;
}
public static void close(ResultSet resultSet,Statement statement,Connection connection) throws SQLException{
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
}
}
- 方式一:通过配置文件读取连接数据库的参数
- 方式二:配置c3p0-config.xml文件,这里我是直接写死在xml中,工作中推荐把这些配置信息单独写到一个.properties文件中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public class C3P0Utils {
private static ComboPooledDataSource dataSource;
static{
//从C3P0连接池中获取连接对象
dataSource = new ComboPooledDataSource();
}
public static Connection getConnection() throws SQLException, PropertyVetoException{
return dataSource.getConnection();
}
public static void close(ResultSet resultSet,Statement statement,Connection connection) throws SQLException{
if (resultSet != null) {
resultSet.close();
}
if (statement != null) {
statement.close();
}
if (connection != null) {
connection.close();
}
}
}
DBUtils
DBUtils 主要功能是减少了使用JDBC对SQL数据操作的代码,这里仅仅是对代码的优化,在性能上没有提升,DBUtils内部实现原理使用到了反射机制. 对于DBUtils的使用,内部核心我认为是反射机制和内省机制,大家可以去了解一个反射机制与内省机制,你也可以写一个DBUtils出来!这篇博客先贴出如何使用DBUtils包(这里需要与刚刚封装的C3P0Utils类一起使用)
1 | public void test01() throws SQLException{ |