JDBC
- 概念:Java DataBase Connectivity ( Java 数据库连接 ) --> Java语言操作数据库
- 本质:是官方(SUN公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动 jar 包,我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动 jar 包中的实现类
快速入门
- 步骤
- 导入驱动 jar 包
- 复制 mysql-connector-java.jar 到项目的 libs 目录下
- 右键 libs --> add as Library
- 注册驱动
- 获取数据库连接对象 Connection
- 定义 sql
- 获取执行 sql 语句的对象 Statement
- 执行 sql,接收返回结果
- 处理结果
- 释放资源
- 导入驱动 jar 包
//1.导入jar包
Statement stmt = null;
Connection conn = null;
try {
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","password");
//4.定义 sql 语句
String sql = "update student set name = "阿旺" where id = 1";
//5.获取执行 sql 的对象 Statement
Statement stmt = conn.createStatement();
//6.执行 sql
int count = stmt.executeUpdate(sql);//影响的行数
//7.处理结果
System.out.println(count);//输出影响成功的行数
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e){
e.printStackTrace();
} finall {
//8.释放资源
//避免空指针异常
if (stmt != null){
try {
stmt.close();
} catch (SQLException e){
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e){
e.printStackTrace();
}
}
}
各个对象详解
DriverManager(驱动管理对象)
-
注册驱动:告诉程序该使用哪一个数据库驱动 jar
- 注册与给定的驱动程序 DriverManager:
static void registerDriver(Driver driver)
- 使用方法:
Class.forName("com.mysql.jdbc.Driver");
- 注意:mysql 5 之后的 jar 包可以省略注册驱动的步骤
- 在
com.mysql.jdbc.Driver
类中存在静态代码块
static { try { java.sql.DriverManager.registerDrvier(new Driver()); } catch (SQLException e){ throw new RuntimeException("Can't register driver!"); } }
- 注册与给定的驱动程序 DriverManager:
-
获取数据库连接
- 方法:
static Connection getConnection(String url,String user,String password);
- 参数
- Url:指定连接的路径
- 语法:
jdbc:mysql://ip地址(域名):端口号/数据库名称
- 例子:
jdbc:mysql://localhost:3306:db1
- 细节:如果连接的是本机的 mysql 服务器,并且 mysql 服务端默认端口是 3306,则 url 可以简写为
jdbc:mysql:///数据库名称
- 语法:
- User:用户名
- Password:密码
- 方法:
-
实例:
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","password");
Connection(数据库连接对象)
-
获取执行 sql 的对象
Statement createStatement()
PreparedStatement prepareStatement(String sql)
-
管理事务
- 开启事务:
setAutoCommit(boolean autoCommit)
:调用该方法设置参数为 false,即开启事务 - 提交事务:
commit()
- 回滚事务:
rollback()
- 开启事务:
Statement(执行 sql 的对象)
-
boolean execute(String sql)
:可以执行任意的 sql (了解即可) -
int executeUpdate(String sql)
:执行 DML(insert、update、delete)语句、DDL(create、alter、drop)语句- 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功(
返回值>0
则执行成功)
- 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功(
-
ResultSet executeQuery(String sql)
:执行 DQL(select)语句
ResultSet(结果集对象,封装查询结果)
next()
:游标向下移动一行(mysql数据库的下一行),判断当前行是否是最后一行末尾(如果是返回false,如果不是返回true)getXxx(参数)
:获取数据- Xxx:代表数据类型
- 如:
int getInt()
,String getString()
- 如:
- 参数
- Int:代表列的编号,从1开始。如:
getString(1);
- String:代表列的名称。如:
getDouble("salary");
- Int:代表列的编号,从1开始。如:
- Xxx:代表数据类型
//1.导入jar包
Statement stmt = null;
Connection conn = null;
ResultSet rs = null;
try {
//2.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//3.获取数据库对象
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","password");
//4.定义 sql 语句
String sql = "select * from student";
//5.获取执行 sql 的对象 Statement
Statement stmt = conn.createStatement();
//6.执行 sql
rs = stmt.executeQuery(sql);
//7.处理结果
rs.next();//默认在0上,让光标向下移动一行(以后用while循环,这里只做演示)
int id = rs.getInt(1);
String name = rs.getString("name");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e){
e.printStackTrace();
} finall {
//8.释放资源
//避免空指针异常
if (stmt != null){
try {
stmt.close();
} catch (SQLException e){
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e){
e.printStackTrace();
}
}
}
PrepareStatement(执行 sql 的对象)
-
SQL注入问题:在拼接 sql 时,有一些 sql 的特殊关键字参与字符串的拼接。(会造成安全性问题)
-
解决 sql 注入问题:使用 PrepareStatement 对象来解决
-
预编译的 SQL:参数使用
?
作为占位符 -
步骤
- 导入驱动 jar 包
- 注册驱动
- 获取数据库连接对象 Connection
- 定义 sql
- 注意:sql的参数使用
?
作为占位符。如:select * from user where username = ? and password = ?;
- 注意:sql的参数使用
- 获取执行 sql 语句的对象 PrepareStatement Connection.prepareStatement(String sql)
- 给
?
赋值- 方法:
setXxx(参数1,参数2)
- 参数1:
?
的位置,编号从1开始 - 参数2:
?
的值
- 方法:
- 执行 sql,接收返回结果
- 处理结果
- 释放资源
-
注意:后期都会使用 PrepareStatement 来完成增删改查的所有操作
- 可以防止 SQL 注入
- 效率更高
抽取JDBC工具类:JDBCUtils
- 先配置
jdbc.properties
文件
url = jdbc:mysql://localhost:3306:db1
user = root
password = password
driver = com.mysql.jdbc.Driver
//JDBC工具类
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
//文件只需要读取一次即可拿到这些值,用静态代码块完成
static {
//读取资源文件,获取值
try{
//1.创建Properties集合类
Properties pro = new Properties();
//2.加载文件
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
pro.load(is);
//3.获取数据,赋值
url = pro.getProperty("url");
user = pro.getProperty("user");
password = pro.getProperty("password");
driver = pro.getProperty("driver");
//4.注册驱动
Class.forName(driver);
} catch () {
e.printStackTrace();
}
}
//获取连接,连接对象
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url,user,password);
}
//释放资源
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();
}
}
}
}
JDBC控制事务
- 事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
- 使用 Connection 对象来管理事务
- 开启事务:
setAutoCommit(boolean autoCommit)
:调用该方法设置参数为 false,即开启事务- 在执行 sql 之前开启事务
- 提交事务:
commit()
- 当所有 sql 都执行完提交事务
- 回滚事务:
rollback()
- 在 catch 中回滚事务
- 开启事务:
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) {
//事务回滚
try {
if(conn != null) {
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(pstmt1,conn);
JDBCUtils.close(pstmt2,null);
}
数据库连接池
- 概念:其实就是一个容器(集合),存放数据库连接的容器
- 当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器
- 好处
- 节约资源
- 用户访问高效
- 实现
- 标准接口:DataSource javax.sql包下的
- 获取连接方法:
getConnection()
- 归还连接:如果连接对象 Connection 是从连接池中获取的,那么调用
Connection.close()
方法,则不会再关闭连接了,而是归还连接
- 获取连接方法:
- 一般我们不去实现它,由数据库厂商实现
- C3P0:数据库连接池技术
- Druid:数据库连接池实现技术,由阿里巴巴提供
- 标准接口:DataSource javax.sql包下的
C3P0:数据库连接池技术
- 步骤
- 导入 jar 包(两个):
c3p0.jar
和machange-commons-java.jar
(不要忘记导入数据库驱动 jar 包) - 定义配置文件:
- 名称:
c3p0.properties
或者c3p0-config.xml
- 路径:直接将文件放在 src 目录下即可
- 名称:
- 创建核心对象:数据库连接池对象 CombpPooledDataSource
- 获取连接:
getConnection()
- 导入 jar 包(两个):
<!-- c3p0-config.xml配置 -->
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db1</property>
<property name="user">root</property>
<property name="password">password</property>
<!-- 连接池参数 -->
<!-- 初始化申请的连接数量 -->
<property name="initialPoolSize">5</property>
<!-- 最大的连接数量 -->
<property name="maxPoolSize">10</property>
<!-- 超时时间 -->
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="otherc3p0">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day25</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>
//1.创建数据库连接池对象(使用默认配置)
DataSource ds = new ComboPooledDataSource();
//1.1创建数据库连接池对象(使用指定配置)
DataSource ds1 = new ComboPooledDataSource("otherc3p0");
//2.获取连接对象
Connection conn = ds.getConnection();
//3.归还连接到连接池,下次取的时候是同一地址值的连接对象
conn.close();
Druid:数据库连接池实现技术
- 步骤
- 导入 jar 包:
druid.jar
- 定义配置文件
- 是 properties 形式的
- 可以叫任意名称,可以放在任意目录下
- 获取数据库连接池对象:通过工厂来获取 DruidDataSourceFactory
- 导入 jar 包:
#druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db1
username=root
password=password
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待时间
maxWait=3000
filters=stat
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=false
maxPoolPreparedStatementPerConnectionSize=200
//1.导入jar包
//2.定义配置文件
//3.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
//4.获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
//5.获取连接
Connection conn = ds.getConnection();
- 定义工具类
- 定义一个类 JDBCUtils
- 提供静态代码块加载配置文件,初始化连接池对象
- 提供方法
- 获取连接方法:通过数据库连接池获取连接
- 释放资源
- 获取连接池的方法
public class JDBCUtils {
//1.定义成员变量 DataSource
private static DataSource ds;
static {
try{
//1.加载配置文件
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties pro = new Properties();
pro.load(is);
//2.获取 DataSource
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e){
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
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();
}
}
}
//获取连接池的方法
public static DataSource getDataSource(){
return ds;
}
}
Spring JDBC
- Spring 框架对 JDBC 的简单封装。提供了一个 JDBCTemplate 对象简化 JDBC 的开发
- 步骤
- 导入 jar 包
- 创建 JdbcTemplate 对象。依赖于数据源 DataSource
JdbcTemplate template = new JdbcTemplate(ds);
- 调用 JdbcTemplate 的方法来完成 CRUD 操作
update()
:执行DML(增、删、改)语句queryForMap()
:查询结果将结果集封装为 map 集合(将列名作为key,将值作为value,只能将一条记录封装为一个map集合)- 注意:这个方法查询的结果集长度只能是1
queryForList()
:查询结果将结果集封装为 list 集合- 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
query()
:查询结果,将结果封装为 JavaBean 对象- query的参数:RowMapper
- 一般我们使用 BeanPropertyRowMapper 实现类。可以完成数据到 JavaBean 的自动封装
new BeanPropertyRowMapper<类型>(类型.class)
- query的参数:RowMapper
queryForObject()
:查询结果,将结果封装为对象- 一般用于聚合函数的查询
public class JdbcTemplateDemo{
public static void main(String[] args){
//1.导入jar包
//2.创建JdbcTemplate对象
JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
//3.调用方法
String sql = "update student set age = 20 where id =?";
template.update(sql,3); //'3'对应的是第一个?
//用完了会自动归还到数据库连接池中
}
}
简易代码
public class JdbcTemplateTest{
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
@Test
public void testUpdate(){
//让id为3的学生年龄为20
String sql = "update student set age = 20 where id =?";
template.update(sql,3);
}
@Test
public void testAdd(){
//添加一条记录
String sql = "insert into student(id,name,age) values(?,?,?)";
template.update(sql,6,"阿旺",20);
}
@Test
public void testDel(){
//删除id为6的记录
String sql = "delete from student where id = ?";
template.update(sql,6);
}
@Test
public void testSelOne(){
//查询id为3的学生
String sql = "select * from student where id =?";
Map<String, Object> map = template.queryForMap(sql,3);
//这个方法查询的结果集长度只能是 1
}
@Test
public void testSelAll(){
//查询所有记录
String sql = "select * from student";
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String, Object> map : list){
//...
}
}
@Test
public void testSelAll2(){
//查询所有记录,将其封装为student对象的list集合
String sql = "select * from student";
/*
List<Student> list = template.query(sql, new RowMapper<Student>(){
@Override
public Student mapRow(ResultSet rs, int i) throws SQLException {
int id = rs.getInt("id");
String name = rs.getName("name");
int age = rs.getInt("age");
return new Student(id,name,age);
}
});
*/
//简化书写
List<Student> list = template.query(sql, new RowMapper<Student>(Student.class));
}
@Test
public void testSelAll(){
//查询总记录数
String sql = "select count(id) from student";
Long total = template.queryForObject(sql, long.class);
}
}