JDBC
概念:
Java DateBase Connectivity Java 数据库连接,Java语言操作数据库
JDBC本质:
其实是(Sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口提供,数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类
Person接口 Worker类 Person p = new Worker() p.eat()
快速入门:
步骤:
1.导入驱动jar包
1.复制mysql-connector-java-8.0.22.jar或其他版本的jar包到项目的libs目录下,没有则自己创建在项目的根目录下
2.右键 ➡ add As Library
2.注册驱动
3.获取数据库连接对象 Connection
4.定义sql
5.获取执行sql语句的对象 statement
6.执行sql,接收返回结果
7.处理结果
8.释放资源
```java
package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
public class jdbcdemo1 {
public static void main(String[] args) throws Exception {
//1.导入驱动jar包
//2.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//3.获取数据库的连接对象
// 记得换成自己的用户名与密码
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1","用户名","密码");
//4.定义sql语句
String sql = "update account set balance = 500 where id=1";
//5.获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
//6.执行sql
int count = stmt.executeUpdate(sql);
//7.处理结果
System.out.println(count);
//释放资源
stmt.close();
conn.close();
}
}
//成功后控制台会给出一个“1”,数据库已改变
```
详解各个部分
1.DriverManager: 驱动管理对象
* 功能:
1. 注册驱动:告诉程序该使用哪一个数据库驱动jar
static void registerDriver(Driver driver) : 注册于给定的驱动程序 DriverManager。
写代码使用:Class.forName("com.mysql.cj.jdbc.Driver");
在源码com.mysql.cj.jdbc.Driver类中存在静态代码块。
注意:在MySQL 5 之后的驱动jar包可以省略注册驱动的步骤
2. 获取数据库连接:
* 方法: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:密码
2.Connection: 数据库连接对象
1.功能:
1.获取执行sql的对象
* Statement createStatement()
* PreparedStatement perpareStatement(String sql)
2.管理事务:
* 开始事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
* 提交事务:commit()
* 回滚事务:rollback()
3.Statement: 执行sql的对象
1.执行sql
1.boolean execute(String sql):可以执行任意的sql (了解)
2.int executeUpdate(String sql):执行DML(insert、update、delete)语句、DDL(create、alter、drop)语句
* 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功,返回值>0的则执行成功,反之则失败
3.ResultSet executeQuery(String sql):执行DQL(select)语句
2.练习:
1.account表 添加一条记录
package cn.itcast.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
/**
* account表 添加一条记录 insert语句
*
*
* **/
public class jdbcdome2 {
public static void main(String[] args) {
Statement stmt=null;
Connection conn=null;
try {//注册
Class.forName("com.mysql.cj.jdbc.Driver");
//定义sql
String sql = "insert into account values(null,'王五',3000)";
//获取Connection对象
conn = DriverManager.getConnection("jdbc:mysql:///自己的表","自己的用户名","自己的密码");
stmt = conn.createStatement();
int count = stmt.executeUpdate(sql);//影响的行数
//处理的结果
System.out.println(count);
if (count >0){
System.out.println("成功!");
}else{
System.out.println("失败");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//避免空指针异常
// 释放资源
if(stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
}
2.account表 修改一条记录
* 在相对应处修改为:String sql = "update account set balance = 1500 where id = 3";
3.account表 删除一条记录
* 在相对应处修改为:String sql = "delete from account where id = 3";
4.ResultSet: 结果集对象,封装查询结果
1.boolean next():游标下移一行,判断当前行是否是最后一行末尾(是否有数据,如果是返回false,不是则返回true)
2.getXxx(参数):获取数据
* Xxx:数据类型。 如:int getInt() String getString()
* 参数:
1.int:代表列的编号,从1开始 如:getString(1)
2.String:代表列的名称。 如:getDouble("balance")
* 注意:
* 使用步骤:
1.游标向下移动一行
2.判断是否有数据
3.获取数据
//5 执行sqk
rs = stmt.executeQuery(sql);
//6 处理的结果
//6.1 让游标向下移动一行
rs.next();
//6.2 获取资源
int id = rs.getInt(1);
String name = rs.getString("name");
double balance = rs.getDouble(3);
System.out.println(id+" --- "+name+" --- "+balance);
*练习
*定义一个方法,查询emp表的数据将其封装为对象,然后装载集合,返回
1.定义Emp类
2.定义方法 public List<Emp> findAll(){}
3.实现方法 select * from emp;
代码我放在资源里了
> https://download.csdn.net/download/m0_51552490/21390260
5.PreparedStatement: 执行sql的对象,比Startement功能更加强大
1.SQL注入问题: 在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
1.随意输入用户,输入密码a' or 'a'='a
2.sql:select * from user where username = 'qwesazs' and `password` = 'a' or 'a' = 'a';
2.解决sql注入问题: 使用preparedStatment对象来解决
3.预编译的SQL: 参数使用?作为占位符
4.步骤:
1.导入驱动jar包
2.注册驱动
3.获取数据库连接对象 Connection
4.定义sql
*注意:sql的参数使用?作为占位符
如:select * from user where username=? and password=?
5.获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(sql)
6.给?赋值:
*方法:setXXX(参数1,参数2)
*参数1:?的位置编号 从1开始
*参数2:?的值
6.执行sql,接收返回结果
7.处理结果
8.释放资源
5.注意:后期都会使用PreparedStatement来完成增删改查的所有操作
1.可以防止SQL注入
2.效率更高
抽取JDBC工具类:JDBCUtils
- 目的:简化书写
- 分析:
1.注册驱动抽取
2.抽取一个方法获取连接对象
- 需求:不想传递参数,还要保证工具类的通用性
- 解决:配置文件
练习:
需求:
1.通过键盘录入用户名和密码:
2.判断用户是否登录成功
* select*from user where username="" and password="";
步骤:
1.创建数据库表user
CREATE TABLE user(
id int PRIMARY KEY auto_increment,
username VARCHAR(32),
password VARCHAR(32)
);
INSERT INTO `user` VALUES(NULL,'zs','123');
INSERT INTO `user` VALUES(NULL,'ls','qwe');
2.创建一个类
为了方便练习代码单独放在一篇文章中
> https://blog.youkuaiyun.com/m0_51552490/article/details/119870317
JDBC控制事务
-
事务:一个包含多个步骤的业务操作。如果这个事务操作被事务管理,则这多个步骤要么同时成功,要么同时失败
-
操作:
1.开始事务
2.提交事务
3.回滚事务 -
使用Connection对象来管理事务
- 开启事务: setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
- 在执行sql之前开启事务
- 提交事务: commit()
- 当所有sql都执行完成提交事务
- 回滚事务: rollbock()
- 在catch中回滚事务
- 开启事务: setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
练习部分实例:
package cn.itcast.jdbc;
import cn.itcast.util.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class JDBCDemo {
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 (SQLException e) {
//事务回滚
try {
if (conn != null)
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}finally {
//释放资源
JDBCUtils.close(pstmt1,conn,null);
JDBCUtils.close(pstmt2,null,null);
}
}
}
数据库连接池
1. 概念:其实就是一个容器(集合),存放数据库连接的容器。
当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库是,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器
2. 优点:
1.节约资源
2.用户访问高效
3. 实现:
1.标准接口:DataSource javax.sql包下
1.方法:
*获取连接:getConnection()
* 归还连接:Connection.close()。如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
2.一般我们不去实现它,有数据库厂商来实现
1.c3p0:数据库连接池技术
2.Druid:数据库连接池实现技术,有阿里巴巴提供
4.c3p0:数据库连接池技术
* 步骤:
1.导入jar包
* 下载地址:https://sourceforge.net/projects/c3p0/
* 导入c3p0-0.9.5.5.jar和mchange-commons-java-0.2.19.jar两个jar包
* 注意:除了这两个jar包外还有基础的数据库驱动jar包
2.定义配置文件:
*名称:c3p0.properties 或者 c3p0-config.xml
*路径:直接将文件放在src目录下即可。
3.创建核心对象 数据库连接池对象 ComboPooledDaraSource
4.获取连接: getConnection
5.Druid:数据库连接池实现技术,有阿里巴巴提供
1.步骤:
1.导入jar包 druid-1.2.6.jar
2.定义配置文件:
* 是properties形式的
* 可以叫任意名称,可以放在任意目录下
3.加载配置文件。Properties
4.获取数据库连接池对象:通过一个工厂内来获取 DruidDataSourceFactory
5.获取连接:getConnection
2.定义工具类
1.定义一个类 JDBCUtils
2.提供静态代码块加载配置文件,初始化连接池对象
3.提供方法
1.获取连接方法:通过数据库连接池获取连接
2.释放资源
3.获取连接池的方法
使用的c3p0配置文件:
<c3p0-config>
<!--使用默认的配置读取连接池对象-->
<default-config>
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db1</property>
<property name="user">root</property>
<property name="password">123456</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.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/db1</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 连接池参数 -->
<!-- 初始化申请的连接数量-->
<property name="initialPoolSize">5</property>
<!-- 最大的连接数量 -->
<property name="maxPoolSize">8</property>
<!-- 超时时间 -->
<property name="checkoutTimeout">1000</property>
</named-config>
</c3p0-config>
c3p0基本使用实现:
package cn.it.datasource.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class c3p0dome2 {
public static void main(String[] args) throws SQLException {
// //1.创建数据库连接池对象,s使用默认配置
// DataSource ds = new ComboPooledDataSource();
//
// //2.获取连接对象
// for (int i = 1; i <11 ; i++) {
// Connection conn = ds.getConnection();
// System.out.println(i+":"+conn);
// if (i==5){
// conn.close();
//
// }
// }
testNamedConfig();
}
public static void testNamedConfig() throws SQLException {
//1.1 获取使用指定名称配置
DataSource ds = new ComboPooledDataSource("otherC3p0");
for (int i = 1; i <=10 ; i++) {
Connection conn = ds.getConnection();
System.out.println(i+":"+conn);
}
}
}
使用的druid.properties基本配置文件:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///db1
username=root
password=123456
#初始化连接数
initialSize=5
#最大连接数
maxActive=10
#最大等待时间
maxWait=3000
druid基本实现:
package cn.it.datasource.druid;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
public class DruidDemo {
public static void main(String[] args) throws Exception {
//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);
//获取连接
Connection conn = ds.getConnection();
System.out.println(conn);
}
}
测试使用:
package cn.it.datasource.druid;
import cn.it.datasource.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
//使用新的工具类
public class DruidDemo2 {
public static void main(String[] args) {
//完成添加操作,给account表添加一条记录
Connection conn=null;
PreparedStatement pstmt=null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.定义sql
String sql = "insert into account values(null,?,?)";
//3.获取pstmt对象
pstmt = conn.prepareStatement(sql);
//4.赋值
pstmt.setString(1,"王五");
pstmt.setDouble(2,3000);
//5.执行sql
int count = pstmt.executeUpdate();
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
}finally {
//6.释放资源
JDBCUtils.close(null,pstmt,conn);
}
}
}
Spring JDBC : JDBC Template
- Sprint框架对JDBC的简单封装。提供了JDBCTemplate对象简化JDBC的开发
- 步骤:
-
导入jar包
commons-logging的jar包:http://commons.apache.org/proper/commons-logging/download_logging.cgi
spring的jar包:
https://repo.spring.io/ui/native/libs-release/org/springframework/spring
-
创建JdbcTemplate对象。依赖于数据源DataSource
- JdbcTemplate template = new JdbcTemplate(ds);
-
调用JdbcTemplate的方法来完成CRUD的操作
* update():执行DML的语句。增、删、改 * queryForMap:查询结果,将结果集封装为map集合,将列名作为key,将值作为value将这条记录封装为一个map集合 * 注意:该方法查询的结果集长度只能是1 * queryForList:查询结果,将结果集封装为list集合 * 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中 * query:查询结果,将结果集封装为JavaBean对象 * queryForObject:查询结果,将结果封装为对象
-
练习:
- 修改第一条数据的salary为10000
- 添加一条记录
- 删除添加的记录
- 查询id为1001的记录,将其封装为Map集合
- 查询所有记录,将其封装为List
- 查询所有记录,将其封装为Emp对象的List集合
- 查询总记录数
-
package com.company.jdbctemplate;
import com.company.domain.Emp;
import com.company.utils.JDBCUtils;
import org.junit.Test;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
public class JdbcTemplateDemo2 {
//Junit单元测试,可以让方法独立执行
private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource());
//1. 修改第一条数据的salary为10000
@Test
public void test(){
//1.获取JDBCTemplate 对象
//定义sql
String sql = "update emp set salary = 10000 where id = 1001";
//执行sql
int count = template.update(sql);
System.out.println(count);
}
//2. 添加一条记录
@Test
public void test2(){
String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
int count = template.update(sql, 1015, "郭靖", 10);
System.out.println(count);
}
//3. 删除添加的记录
@Test
public void test3(){
String sql = "delete from emp where id=?";
int count = template.update(sql, 1015);
System.out.println(count);
}
//4. 查询id为1001的记录,将其封装为Map集合
//注意该方法查询的结果集只能是一
@Test
public void test4(){
String sql = "select * from emp where id = ?";
Map<String, Object> map = template.queryForMap(sql, 1001);
System.out.println(map);
//{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}
}
//5. 查询所有记录,将其封装为List
@Test
public void test5(){
String sql = "select * from emp";
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String,Object> stringObjectMap:list){
System.out.println(stringObjectMap);
}
}
//6. 查询所有记录,将其封装为Emp对象的List集合
@Test
public void test6(){
String sql = "select * from emp";
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
for (Emp emp:list){
System.out.println(emp);
}
}
//7. 查询总记录数
@Test
public void test7(){
String sql = "select count(id) from emp";
Long total = template.queryForObject(sql, Long.class);
System.out.println(total);
}
}