一、在Maven项目中,原生态手动–连接数据库并实行CRUD的流程
1.在pom.xml中导入数据库驱动的依赖包
<!--数据库驱动依赖(匹配与自己安装的MySQL版本)-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.24</version>
</dependency>
2.在resources目录下,创建db.properties配置类文件并编写
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/school?useSSL=true&useUnicode=true&characterEncoding=UTF-8
username=root
password=123456
3.在com.GQ.Xxx包下,创建并编写工具类JdbcUtils
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
//1.属性资源导入
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");
//2.驱动只用加载一次
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
//3.获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
//4.释放连接资源
public static void release(Connection connection, Statement statement, ResultSet resultSet){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
4.在com.GQ.Yyy包下,编写测试类JdbcTest01(使用Statement类型对象,会存在Sql注入问题)
public class JdbcTest01 {
public static void main(String[] args) {
//getUserById("'张三'"); //正常情况
getUserById("'' or 1=1"); //非正常情况,出现sql注入问题
}
//CRUD业务代码
public static void getUserByName(String name){
//5.提升作用域:为了能在finally中使用
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection(); //1.获取数据库的连接
statement = connection.createStatement(); //2.获取SQL的执行对象(使用Statement类型对象,存在Sql注入问题)
//核心:sql语句
String sql = "SELECT * FROM student WHERE studentname = " + name ;
//3.获取结果(集)
resultSet = statement.executeQuery(sql);
while (resultSet.next()){
System.out.println(resultSet.getString("studentno"));
System.out.println(resultSet.getString("loginpwd"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//4.释放资源
JdbcUtils.release(connection, statement, resultSet);
}
}
}
4.1使用PreparedStatement类型对象,防止Sql注入问题,同时提高效率
public class JdbcTest02 {
public static void main(String[] args) {
getUserById("张三"); //正常情况
//getUserById("'' or 1=1"); //没有返回值,预防sql注入问题
//prepareStatement防止sql注入的本质:把传递进来的参数当作字符,若其中存在转义字符,就会直接忽略(如'会被直接转义)
}
//CRUD业务代码
public static void getUserByName(String name){
//5.提升作用域:为了能在finally中使用
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils.getConnection();
//使用?占位符,来代替参数
String sql = "SELECT * FROM student WHERE studentname = ?";
statement = connection.prepareStatement(sql); //1.使用prepareStatement来预编译sql(先写sql但先不执行)
//2.手动给参数赋值
statement.setString(1,name); //index默认从1开始
//3.执行
resultSet = statement.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getString("studentno"));
System.out.println(resultSet.getString("loginpwd"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//4.释放资源
JdbcUtils.release(connection, statement, resultSet);
}
}
}
二、在Maven项目中,通过数据库连接池–连接数据库并实行CRUD的流程
- 数据库连接池的基本思想:
- 为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。
- 为什么需要数据库连接池?
- 资源重用:由于数据库连接得以重用,避免了频繁创建和释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。
- 更快的系统反应速度:数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间。
- 新的资源分配手段:对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源。
- 统一的连接管理,避免数据库连接泄漏:在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。
- 多种开源的数据库连接池:
- JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:
- DBCP:是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。
- C3P0:是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。hibernate官方推荐使用。
- Druid:是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,速度快,又稳定,但是速度不确定是否有BoneCP快
- DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池。
- DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。
- 特别注意:
- 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
- 当数据库访问结束后,程序还是像以前一样关闭数据库连接:connnection.close(); 但connnection.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。
4.DBCP的使用:
1.在pom.xml中导入dbcp数据库连接池的依赖包
<!--前提:先导入数据库驱动依赖-->
<!--dbcp数据库连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.9.0</version>
</dependency>
2.在resources目录下,创建dbcp-config.properties配置类文件并编写
#连接设置(driverClassName是DBCP数据源中定义好的固定名字)
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
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_UNCOMMITTED
3.在com.GQ.Xxx包下,创建并编写工具类JdbcUtils_DBCP
public class JdbcUtils_DBCP {
//4.提升作用域
private static BasicDataSource dataSource = null;
//1.获取数据源 dataSource
static {
try {
InputStream in = JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcp-config.properties"); //获取输入流
Properties properties = new Properties(); //创建空属性列表
properties.load(in); //加载流资源
//创建数据源 工厂模式--->创建对象
//在java中,编写数据库连接池需实现java.sql.DataSource接口,每一种数据库连接池都是DataSource接口的实现,而DBCP连接池就是java.sql.DataSource接口的一个具体实现
dataSource = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
//2.获取连接 (数据源dataSource自带连接)
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//3.释放连接资源
public static void release(Connection connection, Statement statement, ResultSet resultSet){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
4.在com.GQ.Yyy包下,编写测试类JdbcTestDBCP
public class JdbcTestDBCP {
public static void main(String[] args) {
getUserByName("张伟");
}
//CRUD业务代码
public static void getUserByName(String name){
//5.提升作用域:为了能在finally中使用
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils_DBCP.getConnection(); //利用数据源改变的地方一
//使用?占位符,来代替参数
String sql = "SELECT * FROM student WHERE studentname = ?";
statement = connection.prepareStatement(sql); //1.使用prepareStatement来预编译sql(先写sql但先不执行)
//2.手动给参数赋值
statement.setString(1,name); //index默认从1开始
//3.执行
resultSet = statement.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getString("studentno"));
System.out.println(resultSet.getString("loginpwd"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//4.释放资源
JdbcUtils_DBCP.release(connection, statement, resultSet); //利用数据源改变的地方二
}
}
}
5.C3P0的使用:
1.在pom.xml中导入C3P0数据库连接池的依赖包
<!--前提:先导入数据库驱动依赖-->
<!--c3p0数据库连接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>mchange-commons-java</artifactId>
<version>0.2.20</version>
</dependency>
2.在resources目录下,创建c3p0-config.xml配置类文件并编写
<c3p0-config>
<!--C3P0的缺省(默认)配置,
如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource();”这样写就表示使用的是C3P0的缺省(默认)配置信息来创建数据源-->
<default-config>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSL=true</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<!--C3P0的命名配置,
如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource("MySQL");”这样写就表示使用的是name是MySQL的配置信息来创建数据源-->
<named-config name="MySQL">
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/school?useUnicode=true&characterEncoding=utf8&useSSL=true</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">10</property>
<property name="minPoolSize">5</property>
<property name="maxPoolSize">20</property>
</named-config>
</c3p0-config>
3.在com.GQ.Xxx包下,创建并编写工具类JdbcUtils_C3P0
public class JdbcUtils_C3P0 {
//4.提升作用域
private static ComboPooledDataSource dataSource = null;
//1.获取数据源 dataSource
static {
try {
//创建数据源 (直接new一个ComboPooledDataSource()对象就可以获得数据源)
dataSource = new ComboPooledDataSource(); //C3P0的缺省(默认)配置
//ComboPooledDataSource dataSource = new ComboPooledDataSource("MySQL"); //C3P0的命名配置(配置文件写法)
} catch (Exception e) {
e.printStackTrace();
}
}
//2.获取连接 (数据源dataSource自带连接)
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//3.释放连接资源
public static void release(Connection connection, Statement statement, ResultSet resultSet){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
4.在com.GQ.Yyy包下,编写测试类JdbcTestC3P0
public class JdbcTestC3P0 {
public static void main(String[] args) {
getUserByName("张三"); //正常情况
}
//CRUD业务代码
public static void getUserByName(String name){
//5.提升作用域:为了能在finally中使用
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
connection = JdbcUtils_C3P0.getConnection();//利用数据源改变的地方一
//使用?占位符,来代替参数
String sql = "SELECT * FROM student WHERE studentname = ?";
statement = connection.prepareStatement(sql); //1.使用prepareStatement来预编译sql(先写sql但先不执行)
//2.手动给参数赋值
statement.setString(1,name); //index默认从1开始
//3.执行
resultSet = statement.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getString("studentno"));
System.out.println(resultSet.getString("loginpwd"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//4.释放资源
JdbcUtils_C3P0.release(connection, statement, resultSet); //利用数据源改变的地方二
}
}
}