JDBC
1.JDBC
JDBC:Java DataBase Connectivty:Java数据库连接 ,简单的来说:Java语言操作数据库。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o8xNXYQ1-1616838354633)(E:\JAVA上课笔记\img\image-20210225194955000.png)]
JDBC:其实是Sun公司定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商实现这套接口,提供数据库驱动jar包,我们可以使用这套接口进行编程,真正执行代码的是数据库驱动jar包
2.JDBC快速入门
步骤:
1.导入驱动jar包
2.注册驱动,让程序知道我们用的是哪个版本的驱动
3.获取数据库连接对象:Connection
4.定义sql
5.获取执行sql语句的对象:Statement
6.执行sql,接收返回结果
7.处理结果
8.释放资源
案例
package org.wdit.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;
/*
* jdbc入门案例*/
public class jdbcDemo {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");
//3.定义sql
String sql="update student set english=100 where id=4";
//4.获取执行sql语句的对象:statement
Statement statement = connection.createStatement();
//5.执行sql,接收返回结果
int count = statement.executeUpdate(sql);//count:受影响行数
//6.处理结果
System.out.println(count);
//7.释放资源
statement.close();
connection.close();
}
}
3.JDBC入门案例过程详解
3.1Class.forName()
源码:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LGQKMlKn-1616838354635)(E:\JAVA上课笔记\img\image-20210225214752160.png)]
回忆:类的加载:加载,链接,初始化
在初始化这一步,会执行类构造方法
clint()
,而该方法是有变量的赋值动作 和 静态代码块的语句合并产生的,也就是说:类加载器在加载指定结构中包含静态代码块,就会执行静态代码块中的代码。
因为JDBC驱动有多个版本,所以JDBC规范中规定,所有驱动必须向驱动管理器进行注册以辨别各种版本。而Drive
类中只有一个无参构造和一个静态代码块,静态代码块中内容刚好就是驱动注册的代码,所以:
Class.forName()目的是加载类的时候向驱动管理器注册自己
3.2 DriveManager:驱动器管理对象
功能
1.注册驱动:(Class.forName中已解释)Mysql 5之后的驱动jar包可以不注册驱动,它会自动帮我们注册
2.获取数据库连接对象:
-
方法:
DriveManager.getConnection(“URL”,“用户名”,“密码”);
-
参数
- URL:jdbc:mysql://localhost:3306/数据库名称
注意:
如果mysql是本地数据库,并且端口号使用的是默认端口:3306,则可以将IP(域名):端口号这部分省略
jdbc:mysql:///数据库名称
2.用户名:连接mysql的用户名
3.密码:对应的密码
3.3Connection:数据库连接对象
功能:
1.获取执行sql的对象
- createStatement() 返回一个Statement对象
- PrepareStatement(String sql)
2.管理事务
- 开启事务:setAutoCommit(boolean autoCommit):调用该方法,false为开启
- 提交事务:commit()
- 回滚事务:rollback()
3.4Statement:执行静态sql对象
功能:
- 执行 静态sql
- **boolean excute(String sql):**可以执行任意的sql(不常用)
- **int excuteUpdate(String sql):**可以执行DML.(insert,update,delete)和DDL(create,alter,drop)语句
- 返回值int:受影响的行数,我们可以将返回值作为是否执行成功的标准:>0,成功
- **ResultSet excuteQuery(String sql):**执行DQL(select)语句
练习:
1.给student表中添加一条记录
package org.wdit.jdbc;
import java.sql.*;
/*练习:给表中添加一条记录*/
public class jdbcDemo2 {
public static void main(String[] args) throws Exception {
Connection connection = null;
Statement statement = null;
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取数据库连接对象
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8", "root", "123456");
//3.编写sql
String sql="insert into student(id,name,age,sex,address,math,english) values (7,'白冰',18,'女','西安',90,90)";
//4.获取执行sql的对象
statement = connection.createStatement();
//5.执行sql
int count = statement.executeUpdate(sql);
//6.处理结果
if(count>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//7.释放资源
try {
if(statement!=null){
statement.close();
}
if(connection!=null){
connection.close();}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
3.5ResultSet:结果集对象
功能:
1.next():类似于Itrator迭代器的next()功能 ,将游标移动到下一条记录,起始位置在表头位置,所以第一次取数据,需先调用一次next()方法。
2.getXxx(参数):获取数据
- Xxx:代表数据类型
- 参数:
- int:代表列的编号。从1开始
- String:代表列名称
package org.wdit.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/*
查询表中数据,并
1.取出一条记录
2.遍历结果集
* */
public class jdbcDemo3 {
public static void main(String[] args) throws Exception {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2.获取连接对象
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8", "root", "123456");
//3.定义sql
String sql="select * from student";
//4.获取执行sql对象
Statement statement = connection.createStatement();
//5.执行sql
ResultSet resultSet = statement.executeQuery(sql);
//6.处理结果
/* resultSet.next();
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println(name + "----" + age);*/
while(resultSet.next()){
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
System.out.println(name + "----" + age);
}
}
}
SQL注入:
因为某些关键字参与SQL语句的拼接导致出现安全问题
如何解决SQL注入?
使用PrepareStatement
4.PrepareStatement:执行动态SQL的对象
PrepareStatement执行指定的sql语句,参数使用占位符,在执行的时候通过某些方法给相应的占位符赋值,才能获得完整的sql语句,这样就有效防止了SQL注入
步骤:
1.导入驱动jar包
2.注册驱动,让程序知道我们用的是哪个版本的驱动
3.获取数据库连接对象:Connection
4.定义sql,参数使用占位符
5.获取执行动态sql对象PrepareStatement:
-
connection.PrepareStatement(String sql)
6.给?占位符赋值
-
setXxx(参数1,参数2)
-
参数1:?占位符的位置编号,从1开始
-
参数2:?占位符对应的值
7.执行sql,接收返回结果,执行不需要传递sql语句
8.处理结果
9.释放资源
模拟登录的优化
静态
package org.wdit.jdbc;
import org.wdit.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
/*
模拟登录
分析:
1.用户输入用户名和密码
2.连接数据库,查询是否有该信息
*有,登陆成功
*没有:用户名或密码错误
* */
public class jdbcDemo6 {
public static void main(String[] args) throws Exception {
Scanner scanner=new Scanner(System.in);
System.out.println("请输入用户名:");
String userName=scanner.next();
System.out.println("请输入密码:");
String password=scanner.next();
boolean flag = login(userName,password);
if(flag){
System.out.println("登陆成功");
}else{
System.out.println("用户名或密码错误");
}
}
public static boolean login(String userName,String password) throws Exception {
if (userName!=null&&password!=null){
//获取连接对象
Connection connection = JDBCUtils.getConnection();
//sql
String sql="select * from user where usename='"+ userName +"' and password='"+password+"'";
//执行sql对象
Statement statement = connection.createStatement();
//执行
ResultSet resultSet = statement.executeQuery(sql);
return resultSet.next();
}
return false;
}
//a'or'a'='a密码都正确
}
动态sql
package org.wdit.jdbc;
import org.wdit.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Scanner;
/*
模拟登录-改进版,防止sql注入
分析:
1.用户输入用户名和密码
2.连接数据库,查询是否有该信息
*有,登陆成功
*没有:用户名或 密码错误
* */
public class jdbcDemo7 {
public static void main(String[] args) throws Exception {
Scanner scanner=new Scanner(System.in);
System.out.println("请输入用户名:");
String userName=scanner.next();
System.out.println("请输入密码:");
String password=scanner.next();
boolean flag = login(userName,password);
if(flag){
System.out.println("登陆成功");
}else{
System.out.println("用户名或密码错误");
}
}
public static boolean login(String userName,String password) throws Exception {
if (userName!=null&&password!=null){
//获取连接对象
Connection connection = JDBCUtils.getConnection();
//sql
String sql="select * from user where usename=?and password=? ";
//执行sql对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//占位符赋值
preparedStatement.setString(1,userName);
preparedStatement.setString(2,password);
//执行
ResultSet resultSet = preparedStatement.executeQuery();
return resultSet.next();
}
return false;
}
//a'or'a'='a密码都正确
}
5.JDBC-控制事务
5.1事务:
一个包含多个步骤的业务操作,如果这个业务被事务管理,则这多步操作,要么同时成功,要么同时失败
5.2事务的操作
1.开启事务
2.提交事务
3.回滚事务
5.3使用Connection对象管理事务
- 开启事务:setAutoCommit(boolean autoCommit):调用该方法,false为开启
- 获取连接对象后可开启事务
- 提交事务:commit()
- 所有语句执行完毕,即可提交
- 回滚事务:rollback()
- 只要捕捉到异常,就在catch中进行事务回滚
package org.wdit.jdbc;
import org.wdit.utils.JDBCUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
/*JDBC事务管理*/
public class jdbcDemo8 {
public static void main(String[] args) {
Connection connection=null;
PreparedStatement pst1=null;
PreparedStatement pst2=null;
try {
connection = JDBCUtils.getConnection();
//开启事务
connection.setAutoCommit(false);
//定义sql
String sql1="update zhuanzhang set money=money-?where name=?";//张三
String sql2="update zhuanzhang set money=money+?where name=?";//李四
//获取执行sql对象
pst1 = connection.prepareStatement(sql1);
pst2= connection.prepareStatement(sql2);
//给?赋值
pst1.setDouble(1,500);
pst1.setString(2,"张三");
pst2.setDouble(1,500);
pst2.setString(2,"李四");
//执行
int i = pst1.executeUpdate();
int i1 = pst2.executeUpdate();
//提交事务
connection.commit();
} catch (Exception e) {
try {
connection.rollback();
} catch (SQLException se) {
se.printStackTrace();
}
e.printStackTrace();
}finally{JDBCUtils.close(connection,pst1);
JDBCUtils.close(null,pst2);
}
}
}
6.数据库连接池
数据库连接池类似于多线程的线程池。
概述:
数据库连接池就是一个容器,存放着一些数据库的连接对象。
当系统初始化完成后,容器被创建,容器会申请一些连接对象 ,当用户访问数据库时,从容器中获取连接对象即可,访问完成后,会将连接对象归还给连接池,这样可以提高访问效率,还可以提高链接对象的复用率
实现:
java提供了一个接口:javax.sql.DataSourse
该接口是连接池技术的规范,具体的实现 还是由数据库厂商实现,该接口中定义了两个方法
- getConnection():获取连接
- close():归还连接
主流连接池技术实现
- c3p0:
- Druid:由阿里巴巴提供的数据库连接池技术
6.1 C3P0连接池
步骤
1.导入jar包
- c3p0-0.9.5.2.jar
- mchange-commons-java-0.2.12.jar
2.定义配置文件
- 有规定名称:c3p0.prooperties或c3p0-config.xml
- 规定路径:放在src目录下
3.创建核心对象:数据库连接池对象CombopooledDataSource
4.获取连接:getConnection()
package org.wdit.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.Connection;
import java.sql.SQLException;
/*c3p0演示案例*/
public class c3p0Demo1 {
public static void main(String[] args) throws Exception {
//创建数据库连接池对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//获取连接对象
Connection connection = dataSource.getConnection();
//查看连接对象
System.out.println(connection);
}
}
演示配置参数
package org.wdit.c3p0;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.SQLException;
/*演示配置参数*/
public class c3p0Demo2 {
@Test
public void test() throws Exception {
//1.获取连接池对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//2.获取连接对象
for(int i=1;i<=11;i++) {
Connection connection = dataSource.getConnection();
System.out.println(i+" "+connection);
}
}
/*验证connection对象是复用的*/
@Test
public void test2() throws Exception {
//1.获取连接池对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//2.获取连接对象
for(int i=1;i<=11;i++) {
Connection connection = dataSource.getConnection();
System.out.println(i+" "+connection);
if(i==3){
connection.close();
}
}
}
/*指定配置信息:如果使用无参构造,则使用默认配置:default-config
* 如果使用有参构造,则使用和参数名相同的连接池配置信息*/
@Test
public void test3() throws Exception {
//1.获取连接池对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//2.获取连接对象
for(int i=1;i<=11;i++) {
Connection connection = dataSource.getConnection();
System.out.println(i+" "+connection);
if(i==3){
connection.close();
}
}
}
}
6.2Druid连接池
步骤:
1.导包: druid-1.0.9.jar
2.定义配置文件:
- 是properties格式的
- 可以是任意名称,可以在任意目录下
3.加载配置文件
4.获取连接池对象:通过工厂来获取:DruidDataSourceFactory
5.获取连接:getConnection()
package org.wdit.Druid;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;
/*Druid连接池演示案例*/
public class DruidDemo1 {
public static void main(String[] args) throws Exception {
//加载配置文件
Properties properties=new Properties();
InputStream resourceAsStream = DruidDemo1.class.getClassLoader().getResourceAsStream("druid.properties");
properties.load(resourceAsStream);
//获取连接池对象
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
//获取连接
Connection connection = dataSource.getConnection();
//查看连接
System.out.println(connection);
}
}
6.3使用Druid连接池定义JDBCUtils
步骤:
1.定义一个工具类:JDBCUtils
2.提供静态代码块:加载配置文件,初始化连接池对象
3.提供方法:
- 获取连接
- 释放资源
- 获取连接池对象的方法
package org.wdit.Druid;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import org.wdit.utils.JDBCUtils;
import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/*使用Druid连接池的JDBC工具类
* **步骤:**
1.定义一个工具类:JDBCUtils
2.提供静态代码块:加载配置文件,初始化连接池对象
3.提供方法:
- 获取连接
- 释放资源
- 获取连接池对象的方法*/
public class JDBUtils {
//成员变量
private static DataSource dataSource;
static{
try {
//加载配置文件
Properties properties=new Properties();
properties.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
//获取连接池对象
dataSource= DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接对象
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
/**
* 释放资源
* @param statement
* @param connection
* @param resultSet
*/
public static void close(Statement statement, Connection connection, ResultSet resultSet){
if(statement!=null){
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(connection!=null){
try {
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(resultSet!=null){
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 释放资源
* @param statement
* @param connection
*/
public static void close(Statement statement, Connection connection){
close(statement,connection,null);
}
/**
* 获取连接池对象
* @return
*/
public static DataSource getDataSource(){
return dataSource;
}
}
案例
package org.wdit.Druid;
import org.junit.jupiter.api.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DruidDemo2 {
/*向student表中插入一条数据*/
@Test
public void test() throws Exception {
//获取连接对象
Connection connection= JDBUtils.getConnection();
//定义sql
String sql="insert into student(id,name,age,sex,address,math,english) values (?,?,?,?,?,?,?)";
//获取执行sql对象
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//给?赋值
preparedStatement.setInt(1,9);
preparedStatement.setString(2,"邢菲");
preparedStatement.setInt(3,18);
preparedStatement.setString(4,"女");
preparedStatement.setString(5,"西安");
preparedStatement.setInt(6,99);
preparedStatement.setInt(7,99);
//执行
int count = preparedStatement.executeUpdate();
System.out.println(count);
}
/*查询英语成绩>60分的所有学生信息*/
@Test
public void test2() throws Exception {
//连接对象
Connection connection = JDBUtils.getConnection();
//sql
String sql="SELECT * FROM student WHERE english >?";
//获取对象。当定占位符
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,60);
//执行
ResultSet resultSet = preparedStatement.executeQuery();
//遍历结果集
while(resultSet.next()){
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
System.out.println(id+"----"+name);
}
}
}
7.Spring JDBCTemplate
概述:
Spring框架对JDBC的简单封装,提供了一个JDBCTemplate对象,来简化JDBC的操作。
步骤:
1.导包
2.创建JDBCTemplate对象,依赖于数据源DataSource
3.通过JDBCTemplate对象调用封装好的一些方法来完成CRUD操作
-
update():执行 增删改
-
参数1:sql语句
-
参数2-n:根据?数量确定,按?顺序给他传值
-
queryForMap():查询,将结果封装成map结合
-
将记录的字段名作为key,字段值作为value封装到Map集合中
-
注意事项:结果集长度只能是1,超过就会报错
-
queryForList():将结果封装成List集合
-
将每一条记录封装到map集合,再把每一条记录对应的map集合封装到List集合中
-
query():查询,将结果封装成JavaBean对象
- 方式1:query(sql语句,RowMapper接口:我们通过匿名内部类的方式对该接口进行实现)
- 方式2:query(sql语句,new BeanPropertyMapper<>(要封装的实体类.class))
-
queryForObject():查询,将结果封装成对象
新建项目:
1.新建module
2.右键–>add…
3.web-InFO–>lib用来存放jar包
-
JDBCTemplate对应的jar包
-
Druid的jar包
-
mysql连接驱动的jar包
4.配置文件
-
Druid.properties
案例:修改学生成绩
package org.wdit.test; import org.junit.Test; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.wdit.pojo.Student; import org.wdit.utils.JDBCUtils; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Map; public class JDBCTemplateDemo { /* * 入门案例:修改学生成绩*/ //创建JDBCTemplate对象 JdbcTemplate template=new JdbcTemplate(JDBCUtils.getDataSource()); @Test public void test(){ //调用方法 String sql="update student set english= ? where name = ?"; int count = template.update(sql,78,"唐浩"); System.out.println(count); } /*给student表中添加一条数据*/ @Test public void test2(){ //2.sql String sql="insert into student(id,name,age,sex,address,math,english) values (?,?,?,?,?,?,?)"; //3.调用方法 int count = template.update(sql, 10, "smile", 18, "女", "西安", 100, 100); System.out.println(count); } /*删除添加的一条数据*/ @Test public void test3(){ //2.sql String sql="delete from student where id= ?"; //3.调用方法 int count = template.update(sql, 10); System.out.println(count); } /*查询id=3的学生记录,将其封装到Map集合中*/ @Test public void test4(){ String sql="select * from student where id = ?"; Map<String, Object> map = template.queryForMap(sql, 3); System.out.println(map); System.out.println("____________________________________________________"); } /*查询所有记录,将其封装到List集合中*/ @Test public void test5(){ String sql="select * from student"; List<Map<String, Object>> maps= template.queryForList(sql); for(Map<String,Object>map:maps){ System.out.println(map); } } /*查询所有记录,将其封装到Student对象的List集合中*/ @Test public void test6(){ System.out.println("-________________________________________________________________"); String sql="select * from student"; List<Student> students=template.query(sql, new RowMapper<Student>() { @Override public Student mapRow(ResultSet resultSet, int i) throws SQLException { int id = resultSet.getInt("id"); String name = resultSet.getString("name"); int age = resultSet.getInt("age"); String sex = resultSet.getString("sex"); String address = resultSet.getString("address"); int math = resultSet.getInt("math"); int english = resultSet.getInt("english"); Student student=new Student(id,name,age,sex,address,math,english); return student; } }); for(Student student:students){ System.out.println(student); } } /*查询所有记录,将其封装到Student对象的List集合中---(方式2) 注意,如果用方式2:你的实体类属性的基本数据类型一定定义成包装类型,否则如果你的查询记录中有null就会报错,因为基本数据类型不能存null、 2.如果查不到数据也会报错 */ @Test public void test7(){ System.out.println("________________________________________________________________"); String sql="select * from student"; List<Student> students=template.query(sql,new BeanPropertyRowMapper<>(Student.class)); for(Student student:students){ System.out.println(student); } } /*查询name="唐浩"的学生信息,并封装成student对象*/ @Test public void test8(){ System.out.println("8________________________________________________________________"); String sql="select * from student where name = ?"; Student student = template.queryForObject(sql, new BeanPropertyRowMapper<>(Student.class), "唐浩"); System.out.println(student); }
}
}
/*查询所有记录,将其封装到Student对象的List集合中---(方式2)
注意,如果用方式2:你的实体类属性的基本数据类型一定定义成包装类型,否则如果你的查询记录中有null就会报错,因为基本数据类型不能存null、
2.如果查不到数据也会报错 */
@Test
public void test7(){
System.out.println("________________________________________________________________");
String sql="select * from student";
List<Student> students=template.query(sql,new BeanPropertyRowMapper<>(Student.class));
for(Student student:students){
System.out.println(student);
}
}
/*查询name="唐浩"的学生信息,并封装成student对象*/
@Test
public void test8(){
System.out.println("8________________________________________________________________");
String sql="select * from student where name = ?";
Student student = template.queryForObject(sql, new BeanPropertyRowMapper<>(Student.class), "唐浩");
System.out.println(student);
}
}