抽取JDBC工具类:JDBCUtils
- 目的:简化书写
- 分析:
- 注册驱动也抽取
- 抽取一个方法获取连接对象
- 需求:不想传递参数(麻烦),还得保证工具类的通用性。
- 解决:配置文件
- jdbc.properties
- url=
- user=
- password=
- jdbc.properties
- 抽取一个方法释放资源
- 新建一个文件
package jdbc.util;
import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/*
* JDBC工具类
* */
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driver;
/*
* 文件的读取,只需要读取一次即可拿到这些值,使用静态代码块
* */
static {
// 读取资源文件,获取值
// 1. properties集合类
Properties pro=new Properties();
// 2.加载文件
try {
pro.load(new FileReader("src/jdbc.properties"));
// 3.获取数据,赋值
url= pro.getProperty("url");
user= pro.getProperty("user");
password= pro.getProperty("password");
driver=pro.getProperty("driver");
// 4.注册驱动
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 获取连接
* 返回的是连接对象
* */
public static Connection getConnection(String url,String user,String password) throws SQLException{
return DriverManager.getConnection("jdbc:mysql:///study","root","123456");
}
public static Connection getConnection() throws SQLException{
return DriverManager.getConnection(url,user,password);
}
/*
* 释放资源
* stmt
* conn
*/
public static void close(Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
public static void close(ResultSet rs, Statement stmt, Connection conn) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
动态获取src目录下的文件(dreuid.properties)路径
把
pro.load(new FileReader("src/jdbc.properties"));
替换成
// 获取src路径下的文件的方式---> ClassLoader 类加载器
ClassLoader classLoader=JDBCUtils.class.getClassLoader();
URL res= classLoader.getResource("druid.properties");
String path= res.getPath();
System.out.println(path);
// 2.加载文件
// pro.load(new FileReader("D:/project/JDBC02/src/druid.properties"));
pro.load(new FileReader(path));
如果路径中有中文会报错,只要改成全英文即可
练习(利用工具类)
-
需求
- 通过键盘录入用户名和密码。
- 判断用户是否登录成功,如果成功,则提示登录成功,失败,则提示登录失败。
-
步骤
- 创建一个数据表(user)
package jdbc;
import java.sql.*;
import java.util.Scanner;
public class JDBCPractice6 {
public static void main(String[] args) {
// 1. 键盘录入,接受用户名和密码
Scanner sc=new Scanner(System.in);
System.out.println("请输入用户名");
String username=sc.nextLine();
System.out.println("请输入用密码");
String password=sc.nextLine();
// 2. 调用方法
boolean flag=new JDBCPractice6().login(username,password);
// 3.判断结果,输出不同语句
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;
Statement stmt=null;
ResultSet rs=null;
// 连接数据库判断是否连接成功
// 1.获取连接
try {
// conn= JDBCUtils.getConnection();
try {
Class.forName("com.mysql.jdbc.Driver");
try {
// 2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 2.定义SQL
String sql="select * from user where username='"+username+"' and password = '"+password+"'";
// 3.获取执行SQL的对象
stmt =conn.createStatement();
// 4.执行查询
rs=stmt.executeQuery(sql);
// 5.判断
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
}finally {
JDBCPractice6.close(rs,stmt,conn);
}
return false;
}
public static void close(ResultSet executeQuery, Statement preparedStatement, Connection connection) {
if(executeQuery!=null){
try {
executeQuery.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection!=null){
try {
connection.close(); //工具类
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
PreparedStatement(SQL注入)
执行SQL的对象
- SQL注入问题 :在拼接SQL时,有一些SQL的特殊关键字参与字符串的拼接。会造成安全性问题 。
- 输入用户随便,输入密码:a' or 'a' ='a
- sql:
select * from user where username = 'wkks' and password = 'a' or 'a'='a'
- 解决SQL注入问题:使用preparedStatement对象来解决
- 预编译SQL:参数使用?作为占位符
- 步骤:
- 导入jar包mysql-connertor-java-5.1.37-bin.jar
- 注册驱动
- 获取数据库连接对象connection
- 定义sql
- 注意:sql的参数使用?作为占位符。如:
select * from user where username=? and password=?;
- 注意:sql的参数使用?作为占位符。如:
- 获取执行sql语句的对象prepareStatement
Connection.PrepareStatement(String sql) - 给?赋值
- 方法:setxxx(参数1,参数2)
- 参数1:?的位置编号从1开始
- 参数2:?的值
- 方法:setxxx(参数1,参数2)
- 执行sql,接收返回结果
- 处理结果
- 释放资源
- 注意:后期都会使用preparedStatement来完成增删改查的所有操作
- 可以防止SQL注入
- 效率更高
package jdbc;
import java.sql.*;
import java.util.Scanner;
public class PrepareStatement8 {
public static void main(String[] args) {
// 1. 键盘录入,接受用户名和密码
Scanner sc=new Scanner(System.in);
System.out.println("请输入用户名");
String username=sc.nextLine();
System.out.println("请输入用密码");
String password=sc.nextLine();
// 2. 调用方法
boolean flag=new PrepareStatement8().login(username,password);
// 3.判断结果,输出不同语句
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;
PreparedStatement pstmt=null;
ResultSet rs=null;
// 连接数据库判断是否连接成功
// 1.获取连接
try {
// conn= JDBCPractice6.getConnection();
try {
Class.forName("com.mysql.jdbc.Driver");
try {
// 2.获取连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 2.定义SQL
String sql="select * from user where username=? and password = ?";
// 3.获取执行SQL的对象
pstmt =conn.prepareStatement(sql);
// 给?赋值
pstmt.setString(1,username);
pstmt.setString(2,password);
// 4.执行查询,不需要传递参数
rs=pstmt.executeQuery();
// 5.判断
return rs.next();
} catch (SQLException e) {
e.printStackTrace();
}finally {
PrepareStatement8.close(rs,pstmt,conn);
}
return false;
}
public static void close(ResultSet executeQuery, PreparedStatement preparedStatement, Connection connection) {
if(executeQuery!=null){
try {
executeQuery.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(preparedStatement!=null){
try {
preparedStatement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
JDBC控制事务
-
事务:一个包含多个步骤的操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
-
操作
- 开启事务
- 提交事务
- 回滚事务
-
使用Connection对象来管理事务
- 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
- 在执行SQL之前开启事务
- 提交事务:commit()
- 当所有SQL都执行完提交事务
- 回滚事务:rollback()
- 在catch中回滚事务
- 开启事务:setAutoCommit(boolean autoCommit):调用该方法设置参数为false,即开启事务
package jdbc;
import jdbc.util.JDBCUtils;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
/*
* 事务操作
* */
public class affair9 {
public static void main(String[] args) {
Connection conn=null;
PreparedStatement pstmt1=null;
PreparedStatement pstmt2=null;
// 1.获取连接
try {
try {
Class.forName("com.mysql.jdbc.Driver");
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 开启事务
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);
}
}
}
数据库连接池
- 概念:其实是一种容器(集合),存放数据库连接的容器
- 当系统初始化好后,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。
- 好处
- 节约资源
- 用户访问高效
- 实现:
- 标准接口:DateSource javax.sql包下的
- 方法:
- 获取连接:getConnection()
- 归还连接·:如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了。而是归还连接
- 方法:
- 一般我们不去实现它,有数据库厂商实现
- C3P0:数据库连接池技术
- Druid:数据库连接池实现技术,由阿里巴巴提供的
- 标准接口:DateSource javax.sql包下的
C3P0:数据库连接技术
* 步骤:
1. 导入jar包 两个) c3p0-0.9.5.2.jar mchange-commons-java-0.2.12.jar
2. 定义配置文件:
* 名称: c3p0.properties or c3p0-config.xml
* 路径: 直接将文件放在src目录下即可
3. 创建核心对象 数据库连接池对象CombopooledDataSource
4. 获取连接:getConnection
Druid
数据库连接池实现技术,由阿里巴巴提供的
- 步骤
- 导入jar包 druid-1.0.9.jar
- 定义配置文件:
- 是prooerties形式的
- 可以叫任意名称,可以放在任意位置
driver=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/study?useSSL=false username=root password=123456 #初始化连接数量 initialSize=5 #最大连接数 maxActive=10 #最大等待时间 maxWait=3000
3. 加载配置文件
4. 获取数据库连接池对象:通过工厂来获取 DruidDataSourceFactory
5. 获取连接:getConnection
package jdbc;
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) {
try {
// 1.导入jar包
// 2.定义配置文件
// 3.加载配置文件
Properties pro=new Properties();
InputStream is=DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
// System.out.println(is);
pro.load(is);
// 4. 获取连接池对象
DataSource ds=DruidDataSourceFactory.createDataSource(pro);
// 5. 获取连接
Connection conn =ds.getConnection();
System.out.println(conn);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 定义工具类
- 定义一个类JDBCUtils
- 提供静态代码块加载配置文件,初始化连接池对象
- 提供方法
- 获取连接方法:通过数据库连接池获取连接
- 释放资源
- 获取连接池的对象
package jdbc.util;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
/*
Druid连接池的工具类
* */
public class JDBCUtils {
// 1. 定义成员变量
private static DataSource ds;
static{
try {
// 1. 加载配置文件
Properties pro =new Properties();
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
// 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 DataSource getDataSource(){
return ds;
}
}
package jdbc;
/*
* 使用新的工具类
* */
import jdbc.util.GjlClose;
import jdbc.util.JDBCUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.util.Scanner;
public class DruidDemo2 {
public static void main(String[] args) {
PreparedStatement pstmt=null;
Connection conn=null;
Scanner sc=new Scanner(System.in);
System.out.println("请输入用户名");
String name=sc.nextLine();
System.out.println("请输入余额");
Double money=sc.nextDouble();
/*
* 完成添加操作,给account表添加一条记录
* */
try {
// 1.获取连接
// Class.forName("com.mysql.jdbc.Driver");
// conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/study", "root", "123456");
conn= JDBCUtils.getConnection();
// 2.定义·SQL
String sql="insert into account values(null,?,?)";
// 3获取pstmt对象
pstmt=conn.prepareStatement(sql);
// 4.给?赋值
pstmt.setString(1,name);
pstmt.setDouble(2,money);
// 5.执行SQL
int count=pstmt.executeUpdate();
System.out.println(count);
// pstmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
}finally {
// 6.释放资源
GjlClose.close(pstmt,conn);
}
}
}
Spring JDBC:JDBC Template
- 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:查询结果,将结果封装为对象
- 一般用于聚合函数的查询
- 导入jar包
package jdbc.Template;
import jdbc.util.JDBCUtils;
import org.springframework.jdbc.core.JdbcTemplate;
/*
* JdbcTemplate入门
* */
public class JDBCTemplate {
public static void main(String[] args) {
// 1.导入jar包
// 2.创建JDBCTemplate对象
JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
// 3.调用方法
String sql="update account set balance=5000 where id=?";
int count=template.update(sql,3);
System.out.println(count);
}
}
4. 练习
- 需求:
- 修改1号数据salary为10000
- 添加一条记录
- 删除刚才添加的记录
- 查询所有的记录,将其封装为Map集合
- 查询所有记录,将其封装为Emp对象的List集合
- 查询总记录数
package jdbc.Template;
import jdbc.cn.Emp;
import jdbc.util.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.Date;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
public class JDBCTemplateDemo1 {
//Junit单元测试,可以让方法独立执行
// 1.获取JDBCTemplate对象
private JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource());
@Test
public void test1(){
// 2.定义sql
String sql="update emp set salary=1000 where id=1";
// 3.执行sql
int count =template.update(sql);
System.out.println(count);
}
/*
* 添加一条记录
* */
@Test
public void test2(){
// 2.定义sql
String sql="insert into emp (id,name,age) values(?,?,?)";
int count=template.update(sql,null,"萧策",25);
System.out.println("添加了"+count+"条记录");
}
/*
* 删除刚才添加的记录
* */
@Test
public void test3(){
String sql="delete from emp where id=?";
int count =template.update(sql,21);
System.out.println("删除了"+count+"条记录");
}
/*
* 查询id为1的记录,将其封装为Map集合
* */
// 注意这个方法查询的结果集长度只能是1
@Test
public void test4(){
String sql = "select * from emp where id=?";
Map<String,Object> map=template.queryForMap(sql,1);
System.out.println(map);
}
@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);
// }
System.out.println(list);
}
@Test
public void test6_1(){
String sql="select * from emp";
List<Emp> list=template.query(sql, new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet rs, int i) throws SQLException {
Emp emp=new Emp();
int id=rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String job = rs.getString("job");
double salary = rs.getDouble("salary");
Date joindate = rs.getDate("entrydate");
emp.setId(id);
emp.setAge(age);
emp.setName(name);
emp.setJob(job);
emp.setJoindate(joindate);
emp.setSalary(salary);
return emp;
}
});
for (Emp emp:list){
System.out.println(emp);
}
}
@Test
public void test6_2(){
String sql="select * from emp";
List <Emp> list=template.query(sql,new BeanPropertyRowMapper<Emp>(Emp.class));
for(Emp emp:list){
System.out.println(emp);
}
}
@Test
public void test7(){
String sql="select count(id) from emp";
Long total=template.queryForObject(sql,Long.class);
System.out.println("总共有"+total+"条记录");
}
}