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

本质:
其实就是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。
各个数据库厂商去实现这套接口,提供数据库驱动jar包。
我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
JDBC连接数据库使用到的几个对象
- DriverManager
- Connection
- Statement
- ResultSet
- PreparedStatement
1.DriverManager:驱动管理对象
-
功能:
-
1.注册驱动
-
static void registerDriver(Driver driver):注册与给定的驱动程序 DriverManager
-
实际简单注册驱动:Class.forName("com.mysql.jdbc.Driver");
-
通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static{ try{ java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E){ throw new RuntimeException("Cat't register driver!"); } } -
注意:mysql5 之后的驱动jar包可以省略注册驱动的步骤,jar包会自动识别注册驱动
-
-
2.获取数据库连接
- 方法:static Connection getConnection(String url, String user, String password)
- 参数:(每一种数据库连接的url都会不同,这里我们讲解mysql连接)
- url:指定连接路径
- 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
- 细节:如果连接的是本机mysql数据库,并且mysql数据库默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
- user:用户名
- password:密码
- url:指定连接路径
-
2.Connection:数据库连接对象
- 功能:
- 1.获取执行sql的对象
- Statement createStatement()
- PreparedStatement preparedStatement(String sql)
- 2.管理事务
- 开启事务:void setAutoCommit(boolean autoCommit)
- 调用该方法设置参数为false时,即开启事务
- 提交事务:void commit()
- 回滚事务:void rollback()
- 开启事务:void setAutoCommit(boolean autoCommit)
- 1.获取执行sql的对象
3.Statement:执行sql的对象
- 执行sql
- boolean execute(String sql):可以执行任意语句的sql(了解)
- int executeUpdate(String sql):执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句(重点)
- 返回值:影响的行数,可以通过这个值来判断DML语句是否执行成功
- 当返回值 > 0 时,则执行成功,反之,则执行失败
- ResultSet executeQuery(String sql):执行DQL(select)语句(重点)
4.ResultSet:结果集对象,封装查询结果
- boolean next():使游标向下移动一行
- 判断当前行是否是最后一行记录(是否还有数据)
- 如果是最后一行,则返回false
- 如果不是最后一行,即还有数据,则返回true
- 判断当前行是否是最后一行记录(是否还有数据)
- getXxx(参数):获取数据
- Xxx:代表数据类型,如:int-getInt() ; String-getString()
- 参数:
- int类型:代表列的编号,从1开始
- String类型:代表列名称
- 注意:
- 使用步骤:
- 1.使游标向下移动一行
- 2.判断是否还有数据
- 3.获取数据
- 使用步骤:
5.PreparedStatement:执行sql的对象
-
对于Statement执行sql的对象在实现用户输入用户名密码时
- SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接,会造成安全性问题
- 用户名随便输入:asda,密码输入:a' or 'a' = 'a
- sql:select * from user where username='asda' and password='a' or 'a' = 'a'
- 解决sql注入问题:使用PreparedStatement对象来解决
- PreparedStatement对象使用预编译SQL:参数使用?作为占位符
- SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接,会造成安全性问题
-
使用PreparedStatement连接JDBC的步骤
- 1.导入驱动jar包
- 2.注册驱动
- 3.获取数据库连接对象 Connection
- 4.定义sql语句
- 注意:sql的参数使用?作为占位符,如:select * from user where username=? and password=?;
- 5.获取执行sql语句的对象 PreparedStatement
- 使用:Connection.prepareStatement(String sql)
- 6.给占位符?赋值
- 方法:setXxx(参数1, 参数2)
- 参数1:?的位置编号,从1开始
- 参数2:?的值
- 方法:setXxx(参数1, 参数2)
- 7.执行sql语句,接收返回结果
- 8.处理结果
- 9.释放资源
案例演示
需求:
通过键盘录入用户名和密码
判断用户是否登录成功
-- 创建数据库
create table USER{
id int primary key auto_increment,
username varchar(32),
password varchar(32)
};
-- 添加数据
insert into USER values(null,'zhangsan','123');
insert into USER values(null,'lisi','123');
# 配置文件-jdbc.properties(存放在了src目录下)
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///数据库名
user=root
password=root
// JDBC工具类-JDBCUtils
public class JDBCUtils{
private static String driver;
private static String url;
private static String user;
private static String password;
/**
* 配置文件的读取,只需要读取一次即可拿到那些值,使用static静态代码块
*/
static{
// 读取配置文件获取值
try{
// 1.创建一个Properties集合类
Properties pro = new Properties();
// 2.加载文件-获取src路径下的文件方式--》ClassLoader 类加载器
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();
pro.load(path);
// 3.获取属性,并赋值
driver = pro.getProperty("driver");
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
// 4.注册驱动
Class.forName(driver);
} catch(IOExecption e){
e.printStackTrace();
} catch(ClassNotFoundExecption e){
e.printStackTrace();
}
}
/**
* 获取连接
* @return 连接对象
*/
public static Connection getConnection() throws SQLExecption{
return DriverManager.getConnection(url,user,password);
}
/**
* 释放资源
*/
public static void close(Statement stmt, Connection conn){
if(stmt != null){
try{
stmt.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
}
public static void close(ResultSet rs, Statement stmt, Connection conn){
if(rs != null){
try{
rs.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
if(stmt != null){
try{
stmt.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
}
}
// 操作类-LoginTest
public class LoginTest{
public static void main(String[] args){
// 键盘录入信息
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
// 调用登录方法
boolean flag = new LoginTest().login(username, password);
// 判断结果是否登录成功
if(flag){
// 登录成功
System.out.println("登录成功!");
}else{
// 登录失败
System.out.println("用户名或密码错误!");
}
}
/**
* 登录方法
*/
public boolean login(String username, String password){
if(username == null || password == null){
return false;
}
// 连接数据库并判断是否登录成功
Connection conn = null;
PrepareStatement pstmt = null;
ResultSet rs = null;
try{
// 1.获取连接对象
conn = JDBCUtils.getConnection();
// 2.定义sql语句
String sql = "select * from user where username=? and password=?";
// 3.获取执行sql的对象
pstmt = conn.prepareStatement(sql);
// 4.给sql语句中占位符?赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
// 5.执行sql语句并获取结果集
rs = pstmt.executeQuery();
// 6.处理结果-数据库有该用户则返回true,没有则返回false
return rs.next();
} catch (SQLExecption e){
e.printStackTrace();
}finally{
JDBCUtils.close(rs,pstmt,conn);
}
return false;
}
}
使用JDBC控制管理事务
事务:一个包含多个步骤的业务操作。
如果这个业务操作被事务管理,
则这多个步骤要么同时成功,要么同时失败
操作:
- 开启事务
- 提交事务
- 回滚事务
使用Connection管理事务
之前说到Connection拥有管理事务的功能
- 开启事务:void setAutoCommit(boolean autoCommit)
- 调用该方法设置参数为false时,即开启事务
- 在执行sql语句前开启事务
- 提交事务:void commit()
- 当所有sql都执行完后提交事务
- 回滚事务:void rollback()
- 在抛异常catch中回滚事务
下面将上个案例的操作类练习一下事务
// 操作类-LoginTest
public class LoginTest{
public static void main(String[] args){
// 键盘录入信息
Scanner sc = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
// 调用登录方法
boolean flag = new LoginTest().login(username, password);
// 判断结果是否登录成功
if(flag){
// 登录成功
System.out.println("登录成功!");
}else{
// 登录失败
System.out.println("用户名或密码错误!");
}
}
/**
* 登录方法
*/
public boolean login(String username, String password){
if(username == null || password == null){
return false;
}
// 连接数据库并判断是否登录成功
Connection conn = null;
PrepareStatement pstmt = null;
ResultSet rs = null;
try{
// 1.获取连接对象
conn = JDBCUtils.getConnection();
// 开启事务
conn.setAutoCommit(false);
// 2.定义sql语句
String sql = "select * from user where username=? and password=?";
// 3.获取执行sql的对象
pstmt = conn.prepareStatement(sql);
// 4.给sql语句中占位符?赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
// 5.执行sql语句并获取结果集
rs = pstmt.executeQuery();
// 提交事务
conn.commit();
// 6.处理结果-数据库有该用户则返回true,没有则返回false
return rs.next();
} catch (Execption e){
// 当出现任何异常时回滚事务
try{
if(conn !=null){
conn.rollback();
}
} catch(SQLExecption e1){
e3.printStackTrace();
}
e.printStackTrace();
}finally{
JDBCUtils.close(rs,pstmt,conn);
}
return false;
}
}
数据库连接池
概念:
其实就是一个容器(集合),存放数据库连接的容器
当系统初始化好后,容器被创建,容器中会申请一些连接对象,
当用户来访问数据库时,从容器中获取连接对象,
当用户访问完之后,会将连接对象归还给容器
好处:
节约资源、访问高效
》》》实现
- 标准接口:DataSource --> javax.sql包下的
- 方法:
- 获取连接:getConnection()
- 归还连接:Connection.close()
- 如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会关闭连接了,而是归还连接到连接池
- 方法:
- 一般我们不去实现它,由数据库厂商来实现
- C3P0:数据库连接池技术
- Druid:数据库连接池实现技术,由阿里巴巴提供
- ......
1.C3P0
c3p0jar包下载网址:https://mvnrepository.com/artifact/com.mchange/c3p0
依赖:https://mvnrepository.com/artifact/com.mchange/mchange-commons-java
使用步骤:
-
导入jar包(两个):c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar
- 不要忘记导入数据库驱动jar包
-
定义配置文件
- 会自动加载
- 名称:c3p0.properties 或者 c3p0-config.xml
- 路径:直接将文件放在src目录下即可
![image]()
-
创建核心对象-数据库连接池对象 ComboPooledDataSource
-
获取连接:getConnection
练习演示:
<!-- c3p0-config.xml文件 -->
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://ip或域名:端口号/数据库名</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<!-- 初始化申请的连接数量 -->
<property name="initialPoolSize">5</property>
<!-- 最大的连接数量 -->
<property name="maxPoolSize">10</property>
<!-- 超时时间3秒 -->
<property name="checkoutTimeout">3000</property>
</default-config>
<!-- 使用指定的配置读取连接池对象 -->
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://ip或域名:端口号/数据库名</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">8</property>
<property name="checkoutTimeout">1000</property>
</named-config>
</c3p0-config>
// 操作类
public class C3P0Demo{
public static void main(String[] args){
try{
// 1.获取DataSource--使用默认的连接配置
DataSource ds = new ComboPooledDataSource();
/* 1.获取DataSource--使用指定的连接配置
DataSource ds = new ComboPooledDataSource("otherc3p0");*/
Connection conn = null;
PrepareStatement pstmt = null;
// 2.获取连接对象--从容器中获取
conn = ds.getConnection();
// 开启事务
conn.setAutoCommit(false);
// 3.定义sql语句--把用户名为zhangsan的密码修改为123
String sql = "update user set password=? where uaername=?";
// 4.获取执行sql的对象
pstmt = conn.prepareStatement(sql);
// 5.给sql语句中占位符?赋值
pstmt.setString(1,"123");
pstmt.setString(2,"zhangsan");
// 6.执行sql语句并获取结果
int flag = pstmt.executeUpdate();
if(flag > 0){
// 提交事务
conn.commit();
System.out.println("修改成功");
}else{
try{
if(conn !=null){
conn.rollback();
}
} catch(SQLExecption e1){
e3.printStackTrace();
}
System.out.println("修改失败");
}
} catch (Execption e){
// 当出现任何异常时回滚事务
try{
if(conn !=null){
conn.rollback();
}
} catch(SQLExecption e1){
e3.printStackTrace();
}
e.printStackTrace();
}finally{
if(pstmt != null){
try{
pstmt.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
}
}
}
2.Druid(推荐使用)
druid的jar包下载网址:https://github.com/alibaba/druid
使用步骤:
-
导入jar包 druid-1.0.9.jar
-
定义配置文件
- 是properties形式的
- 配置文件可以叫任意的名称,也可以放置在任意目录下
- 不会自动加载,需要指定
![image]()
-
获取数据库连接池对象:通过工厂类(DruidDataSourceFactory)来获取
-
获取连接:getConnection
简单练习演示:
# druid.properties配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://ip或域名:端口号/数据库名
username=root
password=root
# 初始化申请的连接数量
initialSize=5
# 最大的连接数量
maxActive=10
# 超时时间3秒
maxWait=3000
// 操作类
public class DruidDemo{
public static void main(String[] args){
try{
// 1.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
// 2.获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
Connection conn = null;
PrepareStatement pstmt = null;
// 3.获取连接对象--从容器中获取
conn = ds.getConnection();
// 开启事务
conn.setAutoCommit(false);
// 4.定义sql语句--把用户名为zhangsan的密码修改为123
String sql = "update user set password=? where uaername=?";
// 5.获取执行sql的对象
pstmt = conn.prepareStatement(sql);
// 6.给sql语句中占位符?赋值
pstmt.setString(1,"123");
pstmt.setString(2,"zhangsan");
// 7.执行sql语句并获取结果
int flag = pstmt.executeUpdate();
if(flag > 0){
// 提交事务
conn.commit();
System.out.println("修改成功");
}else{
try{
if(conn !=null){
conn.rollback();
}
} catch(SQLExecption e1){
e3.printStackTrace();
}
System.out.println("修改失败");
}
} catch(Execption e){
// 当出现任何异常时回滚事务
try{
if(conn !=null){
conn.rollback();
}
} catch(SQLExecption e1){
e3.printStackTrace();
}
e.printStackTrace();
}finally{
if(pstmt != null){
try{
pstmt.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
}
}
}
》》》上面这样使用Druid连接池多少有些麻烦,每次使用都需要创建一个容器
- 所以我们可以定义一个工具类 JDBCUtils
- 提供静态代码块加载配置文件,初始化连接池对象
- 提供一些方法:
- 获取连接方法:通过数据库连接池获取连接
- 释放资源-即归还至连接池中
- 获取连接池的方法
# druid.properties配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://ip或域名:端口号/数据库名
username=root
password=root
# 初始化申请的连接数量
initialSize=5
# 最大的连接数量
maxActive=10
# 超时时间3秒
maxWait=3000
// 连接池工具类JDBCUtils
public class JDBCUtils{
// 1.定义成员变量
private static DataSource ds;
// 静态代码块
static{
try{
// 2.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
// 3.获取连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch(Execption e){
e.printStackTrace();
}
}
/**
* 获取连接
*/
public static Connection getConnection() throws SQLExecption{
return ds.getConnection();
}
/**
* 释放资源
*/
public static void close(Statement stmt, Connection conn){
close(null,stmt,conn);
}
public static void close(ResultSet rs, Statement stmt, Connection conn){
if(rs != null){
try{
rs.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
if(stmt != null){
try{
stmt.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close(); // 归还于连接池
} catch(SQLExecption e){
e.printStackTrace();
}
}
}
/**
* 获取连接池
*/
public static DataSource getDataSource{
return ds;
}
}
// 操作类
public class DruidTest{
public static void main(String[] args){
try{
Connection conn = null;
PrepareStatement pstmt = null;
// 1.获取连接对象
conn = JDBCUtils.getConnection();
// 开启事务
conn.setAutoCommit(false);
// 2.定义sql语句--把用户名为zhangsan的密码修改为123
String sql = "update user set password=? where uaername=?";
// 3.获取执行sql的对象
pstmt = conn.prepareStatement(sql);
// 4.给sql语句中占位符?赋值
pstmt.setString(1,"123");
pstmt.setString(2,"zhangsan");
// 7.执行sql语句并获取结果
int flag = pstmt.executeUpdate();
if(flag > 0){
// 提交事务
conn.commit();
System.out.println("修改成功");
}else{
try{
if(conn !=null){
conn.rollback();
}
} catch(SQLExecption e1){
e3.printStackTrace();
}
System.out.println("修改失败");
}
} catch(Execption e){
// 当出现任何异常时回滚事务
try{
if(conn !=null){
conn.rollback();
}
} catch(SQLExecption e1){
e3.printStackTrace();
}
e.printStackTrace();
}finally{
JDBCUtils.close(stmt,conn);
}
}
}
JDBC Template:Spring框架对JDBC的简单封装
JDBC Template的jar包下载网址:https://jar-download.com/artifacts/org.springframework/spring-jdbc
1.spring-beans-4.1.2.RELEASE.jar
2.spring-core-4.1.2.RELEASE.jar
3.spring-jdbc-4.1.2.RELEASE.jar
4.spring-tx-4.1.2.RELEASE.jar
5.com.springsource.org.apache.commons.logging-1.1.1.jar

使用步骤:
-
导入jar包
![image]()
-
创建JdbcTemplate对象。依赖于数据源DataSource
- JdbcTemplate template = new JdbcTemplate(ds);
-
调用JdbcTemplate的方法来完成CRUD的操作
-
update():执行DML语句。增、删、改操作
-
queryForMap():查询结果,将结果封装为Map集合
- 将列名作为key,将数据作为value,将这条记录封装为一个map集合
- 注意:这个方法查询的结果只能是一条记录,即集合长度只能是 1
-
queryForList():查询结果,将结果封装为List集合
- 注意:将每一条记录封装为一个Map集合,再将Map集合封装到List集合中
-
query():查询结果,将结果封装为JavaBean对象
- 一般使用 BeanPropertyRowMapper 实现类,可以完成数据导JavaBean的自动封装
- new BeanPropertyRowMapper<实体类型>(实体类型.class)
-
queryForObject():查询结果,将结果封装为对象
-
一般用于聚合函数的查询
-
String sql = "select count(id) from user"; Long total = template.queryForObject(sql,Long.class);
-
-
注意:以上的方法参数是可变的,最少一个参数为sql语句,要是sql语句中有?(占位符),则在后面加参数
-
例如:
String sql = "insert into user values(?,?)"; // 定义整数类型获取修改的记录行数 int count = template.update(sql,"lisi","123");
-
-
练习演示:
# druid.properties配置文件
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://ip或域名:端口号/数据库名
username=root
password=root
# 初始化申请的连接数量
initialSize=5
# 最大的连接数量
maxActive=10
# 超时时间3秒
maxWait=3000
// 连接池工具类JDBCUtils
public class JDBCUtils{
// 1.定义成员变量
private static DataSource ds;
// 静态代码块
static{
try{
// 2.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
// 3.获取连接池对象
ds = DruidDataSourceFactory.createDataSource(pro);
} catch(Execption e){
e.printStackTrace();
}
}
/**
* 获取连接
*/
public static Connection getConnection() throws SQLExecption{
return ds.getConnection();
}
/**
* 释放资源
*/
public static void close(Statement stmt, Connection conn){
close(null,stmt,conn);
}
public static void close(ResultSet rs, Statement stmt, Connection conn){
if(rs != null){
try{
rs.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
if(stmt != null){
try{
stmt.close();
} catch(SQLExecption e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close(); // 归还于连接池
} catch(SQLExecption e){
e.printStackTrace();
}
}
}
/**
* 获取连接池
*/
public static DataSource getDataSource{
return ds;
}
}
// 实体类User
public class User{
private String username;
private String password;
public User(){}
public User(String username, String password){
this.username = username;
this.password = password;
}
public String getUsername(){
return username;
}
public void setUsername(String username){
this.username = username;
}
public String getPassword(){
return password;
}
public void setPassword(String password){
this.password = password;
}
@Override
public String toString(){
return "User{" +
"username='" + username + '\'' +
", password=" + password + '\'' +
'}';
}
}
// 操作类
public class JdbcTemplateTest{
public static void main(String[] args){
// 1.获取JdbcTemplate对象
JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
// 2.定义sql语句
String sql = "select * from user";
// 3.执行sql语句
/*
//3.1方式1 自定义
List<User> list = template.query(sql, new RowMapper<User>() {
@Override
public User mapRow(ResultSet rs, int i) throws SQLExecption{
User user = new User();
String username = rs.getString("username");
String password = rs.getString("password");
user.setUsername(username);
user.setPassword(password);
return user;
}
});
*/
/*3.2方式2 调用实现--前提:实体类中的属性如果是基本数据类型,
则必须使用引用数据类型(例如:int --> Integer ; double --> Double)*/
List<User> list = template.query(sql, new BeanPropertyRowMapper<User>(User.class));
// 打印输出结果
for(User user : list){
System.out.println(user);
}
}
}
// 控制台输出结果
User{username='zhangsan', password='123'}
User{username='lisi', password='123'}
本文来自博客园,作者:早晨9点,转载请注明原文链接:https://www.cnblogs.com/onesun/p/15856942.html




浙公网安备 33010602011771号