JDBC(重点)
1.数据库驱动
驱动:声卡,显卡,数据库等
我们的程序会通过 数据库 驱动,和数据库打交道!
2.JDBC
SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(Java操作数据库)规范,俗称JDBC
这些规范的实现由具体的厂商去做~
对开发人员来说,我们只需要掌握jdbc接口的操作即可
java.sql
javax.sql
还需要导入一个数据库驱动包
3.第一个JDBC程序
创建测试数据库
CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbcStudy;
CREATE TABLE `users`(
id INT PRIMARY KEY,
NAME VARCHAR(40),
PASSWORD VARCHAR(40),
email VARCHAR(60),
birthday DATE
);
INSERT INTO `users`(id,NAME,PASSWORD,email,birthday)
VALUES(1,'zhansan','123456','zs@sina.com','1980-12-04'),
(2,'lisi','123456','lisi@sina.com','1981-12-04'),
(3,'wangwu','123456','wangwu@sina.com','1979-12-04')
1.创建一个普通项目
2.导入数据库驱动
3.编写测试代码
package com.zhang.test01;
import java.sql.*;
// 我的第一个JDBC程序
public class jdbc_firstdemo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
//2.用户信息,数据库对象 (url) //用?连接参数
// 支持中文编码 字符集utf8 // 使用安全的连接
//useUnicode=true&characterEncoding=utf8&useSSL=true
String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
String username="root";
String password=" ";
//3.连接成功,数据库对象 Connection 代表数据库
//驱动管理.获得链接
Connection conn= DriverManager.getConnection(url,username,password);
//4.执行sql的对象
String sql="SELECT*FROM users";
Statement statement=(Statement) conn.createStatement();
ResultSet resultSet= statement.executeQuery(sql);//返回的结果集
//5.执行sql对象
while (resultSet.next()){
System.out.println("id=" +resultSet.getObject("id"));
System.out.println("name=" +resultSet.getObject("NAME"));
System.out.println("pwd=" +resultSet.getObject("PASSWORD"));
System.out.println("email=" +resultSet.getObject("email"));
System.out.println("birth=" +resultSet.getObject("birthday"));
System.out.println("================================");
}
//6.释放连接
resultSet.close();
}
}
DriverManager
//1.加载驱动
Class.forName("com.mysql.jdbc.Driver");//固定写法,加载驱动
Connection conn= DriverManager.getConnection(url,username,password);
//Connection代表数据库
//数据库自动提交
//事务提交
//事务回滚
conn.commit();
conn.rollback();
conn.setAutoCommit();
URL
//2.用户信息,数据库对象 (url) //用?连接参数
// 支持中文编码 字符集utf8 // 使用安全的连接
//useUnicode=true&characterEncoding=utf8&useSSL=true
String url="jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
// maysql 默认端口3306
//协议:mysql://主机地址:端口号/数据库名?参数1&参数2&参数3(mysql写法)
//oracle 默认端口:1521
//jdbc:oracle:thin:@localhost:1521:sid
String username="root";
String password="zwy262425";
Statement 执行sql的对象 prepareStatement();执行sql的对象
//编写sql
String sql="SELECT*FROM users";
// statement.executeQuery();//查询操作返回ResultSet
// statement.execute();//执行任何sql
// statement.executeUpdate();//更新,插入,删除,都是用这个,返回结果
ResultSet查询的结果集:封装了所有的查询结果
获得指定的数据类型
resultSet.getObject();//不知道类型的情况下使用
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
...
遍历,指针
resultSet.beforeFirst();//移动光标到最前面
resultSet.afterLast();//最后面
resultSet.next();//移动到下一个
resultSet.previous();//移动到前一行
resultSet.absolute(in row);//移动到指定行
释放资源,必须写
//6.释放连接
resultSet.close();
statement.close();
conn.close();
4.Statement 对象
使用 Statement 对象。在对数据库只执行一次性存取的时侯,用 Statement 对象进行处理。
查询:查询可以使用Statement对象的executeQuery()和execute()方法。
两个方法的参数均为一个拼接好的String类型字符串SQL。
executeQuery()方法只能执行一条SQL,返回ResultSet对象。
execute()方法应该仅在语句能返回多个 ResultSet 对象、
多个更新计数或ResultSet对象与更新计数的组合时使用。
可以通过getResultSet获取ResultSet对象等等。
executeUpdate:executeUpdate()方法 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQLDDL(数据定义语言)语句,例如 CREATE TABLE 和 DROP TABLE。
INSERT、UPDATE
或DELETE语句的效果是修改表中零行或多行中的一列或多列,可以根据返回结果判断是否执行成功
executeUpdate 的返回值是一个整数,指示受影响的行数(即更新计数)。
对于CREATE TABLE 或DROP TABLE 等不操作行的语句,executeUpdate 的返回值总为零,
所以不能因为其返回0就认为没有成功。
代码实现
0.配置数据库信息
//db.properties 数据库信息文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=zwy262425
1.提取工具类
package com.zhang.test02.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUitls {
private static String driver=null;
private static String url=null;
private static String username=null;
private static String password=null;
static {
try{
InputStream in = JdbcUitls.class.getClassLoader().getResourceAsStream("db.properties");
Properties properties = new Properties();
properties.load(in);
driver=properties.getProperty("driver");
url=properties.getProperty("url");
username=properties.getProperty("username");
password=properties.getProperty("password");
//1.驱动只用加载一次
Class.forName(driver);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
//释放连接资源
public static void release(Connection connection, Statement statement, ResultSet resultSet) throws SQLException {
if(resultSet!=null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement!=null){
statement.close();
}
if (connection!=null){
try {
connection.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
2.编写增删改的方法:executeUpdate
增:
package com.zhang.test02;
import com.zhang.test02.utils.JdbcUitls;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestInSert {
public static void main(String[] args) throws SQLException {
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
try {
connection= JdbcUitls.getConnection();//获取数据库链接
statement= connection.createStatement();//获取SQL执行语句
String sql="INSERT INTO `users`(id,`NAME`,`PASSWORD`,`email`,`birthday`)\n" +
"VALUES(4,'zhangwenyu','123456','123456@qq.com','1999-05-11')";
int i=statement.executeUpdate(sql);
if(i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUitls.release(connection,statement,null);
}
}
}
删:
package com.zhang.test02;
import com.zhang.test02.utils.JdbcUitls;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDelect {
public static void main(String[] args) throws SQLException {
//固定写法
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
try {
//固定写法
connection= JdbcUitls.getConnection();//获取数据库链接
statement= connection.createStatement();//获取SQL执行语句
String sql="DELETE FROM `users` WHERE `id`=4";
//Update有关的操作以下也是固定写法
int i=statement.executeUpdate(sql);
if(i>0){
System.out.println("删除成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUitls.release(connection,statement,null);
}
}
}
改:
package com.zhang.test02;
import com.zhang.test02.utils.JdbcUitls;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestUpdate {
public static void main(String[] args) throws SQLException {
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
try {
connection= JdbcUitls.getConnection();//获取数据库链接
statement= connection.createStatement();//获取SQL执行语句
String sql="UPDATE `users` SET `NAME`='xiaoyun',`email`='12345@qq.com' WHERE `id`=1";
int i=statement.executeUpdate(sql);
if(i>0){
System.out.println("更新成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUitls.release(connection,statement,null);
}
}
}
3.查询executeQuery
package com.zhang.test02;
import com.zhang.test02.utils.JdbcUitls;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestQuery {
public static void main(String[] args) throws SQLException {
Connection connection=null;
Statement statement=null;
ResultSet resultSet=null;
try {
connection = JdbcUitls.getConnection();
statement=connection.createStatement();
//SQL
String sql="select *from users ";
resultSet=statement.executeQuery(sql);//查询完毕会返回一个结果集
while (resultSet.next()){
System.out.println(resultSet.getString("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUitls.release(connection,statement,resultSet);
}
}
}
SQL注入的问题
sql存在漏洞,会被攻击导致数据泄露SQL会被拼接:因为or存在使得只要满足一个条件即为真
5.PreparedStatement对象
PreparedStatement可以防止sql注入,并且效率更高
增:
public static void main(String[] args) throws SQLException {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
connection = JdbcUitls.getConnection();
//区别
//使用?占位符代替参数
String sql= "INSERT INTO `users`(id,`NAME`,`PASSWORD`,`email`,`birthday`)\n" +
"VALUES(?,?,?,?,?)";
preparedStatement=connection.prepareStatement(sql);//预编译sql,先写sql然后不执行
//手动传递参数
preparedStatement.setInt(1,4);//给id赋值
preparedStatement.setString(2,"xiaoxiao");
preparedStatement.setString(3,"123456");
preparedStatement.setString(4,"147147@qq.com");
//注意点:sql.Date 数据库
// java.Date Java
// new Date().getTime() 获得时间戳
preparedStatement.setDate(5,new java.sql.Date(new Date().getTime()));
// 执行
int i = preparedStatement.executeUpdate();
if (i>0){
System.out.println("插入成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUitls.release(connection,preparedStatement,null);
}
}
删:
public static void main(String[] args) throws SQLException {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
connection = JdbcUitls.getConnection();
//区别
//使用?占位符代替参数
String sql= "delete from users where id=?";
preparedStatement=connection.prepareStatement(sql);//预编译sql,先写sql然后不执行
//手动传递参数
preparedStatement.setInt(1,4);
// 执行
int i = preparedStatement.executeUpdate();
if (i>0){
System.out.println("删除成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUitls.release(connection,preparedStatement,null);
}
}
改:
public static void main(String[] args) throws SQLException {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
connection = JdbcUitls.getConnection();
//区别
//使用?占位符代替参数
String sql= "update users set NAME=? where id=?";
preparedStatement=connection.prepareStatement(sql);//预编译sql,先写sql然后不执行
//手动传递参数
preparedStatement.setString(1,"xiaoyu");
preparedStatement.setInt(2,3);
// 执行
int i = preparedStatement.executeUpdate();
if (i>0){
System.out.println("更新成功");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUitls.release(connection,preparedStatement,null);
}
}
查:
public static void main(String[] args) throws SQLException {
Connection connection=null;
PreparedStatement preparedStatement=null;
ResultSet resultSet=null;
try {
connection = JdbcUitls.getConnection();
//区别
//使用?占位符代替参数
String sql= "select * from users where id=?";
preparedStatement=connection.prepareStatement(sql);//预编译sql,先写sql然后不执行
//手动传递参数
preparedStatement.setInt(1,3);
// 执行
resultSet=preparedStatement.executeQuery();
while (resultSet.next()){
System.out.println(resultSet.getString("NAME"));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUitls.release(connection,preparedStatement,null);
}
}
防止sql注入:
public static void main(String[] args) throws SQLException {
//login("lisi","123456");
login("''or 1=1","''or 1=1");//技巧,当密码和用户名永远为真时
}
//登录
public static void login(String name,String password) throws SQLException {
Connection connection=null;
PreparedStatement statement=null;
ResultSet resultSet=null;
try {
connection = JdbcUitls.getConnection();
//SQL
//PreparedStatement防止sql注入的本质,把传递来的参数当作字符
//假设其中存在转义字符,比如'会被直接转义
String sql="SELECT *FROM `users` WHERE `NAME`=? AND `PASSWORD`=?";
statement=connection.prepareStatement(sql);
statement.setString(1,name);
statement.setString(2,password);
resultSet=statement.executeQuery();//查询完毕会返回一个结果集
while (resultSet.next()){
System.out.println(resultSet.getString("NAME"));
System.out.println(resultSet.getString("PASSWORD"));
System.out.println("=====================================");
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
JdbcUitls.release(connection,statement,resultSet);
}
}
6.使用IDEA连接数据库
连接成功后可以打开数据库
双击数据库
更新数据
8.事务
要么都成功,要么都失败
ACID原则
原子性:要么全部完成,要么都不完成
一致性:总数不变
隔离性:多个进程互不干扰
持久性:一旦提交不可逆,持久化到数据库了
隔离性的问题:
脏读:一个事务读取了另一个没有提交的事务
不可重复读:在同一个事物内,重复读取表中的数据,表数据发生了改变
虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来的结果不一致
代码实现
1.开启事务:
//关闭数据库的自动提交功能,自动开启事务
connection.setAutoCommit(false);//开启事务
2.一组业务执行完毕,提交事务
3.可以在catch语句中显示定义回滚语句,但默认失败就会回滚
public static void main(String[] args) throws SQLException {
Connection connection=null;
PreparedStatement st=null;
ResultSet resultSet=null;
try {
connection= JdbcUitls.getConnection();
//关闭数据库的自动提交功能,自动开启事务
connection.setAutoCommit(false);//开启事务
String sql1="update account set money=money-100 where name ='a'";
st= connection.prepareStatement(sql1);
st.executeUpdate();
int x=1/0;//报错
String sql2="update account set money=money+100 where name ='b'";
st=connection.prepareStatement(sql2);
st.executeUpdate();
//业务完毕,提交事务
connection.commit();
System.out.println("成功!");
} catch (SQLException e) {
//如果失败,默认回滚,下面try~catch可以不写
try {
connection.rollback();//如果失败则回滚事务
}catch (SQLException e1){
e1.printStackTrace();
}
e.printStackTrace();
}finally {
JdbcUitls.release(connection,st,resultSet);
}
}
9.数据库连接池
数据库连接–执行完毕–释放
连接–释放 十分浪费资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
举例:银行业务
----- 开门 – 业务员:等待 – 服务 – 关门(服务关闭)
例如:(以下数字不固定,按自己需求设置)
常用连接数 10个
最小连接池 10个
最大连接数:100 业务最高承载上限
超过上限,排队等待
等待超时:100ms
编写连接池,实现一个接口DataSource
开源数据源(拿来即用)
DBCP
C3P0
Druid:阿里巴巴
使用了这些数据库连接池之后,我们在项目开发就不需要编写连接数据库的代码了!
DBCP
需要用到的jar包
commons-dbcp-1.4,commons-pool-1.6
dbcp简单配置
#连接设置 这里面的名字是DBCP数据源中定义好
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=zwy262425
#指定有连接池错创建的连接的自动提交
defaultAutoCommit=true
#driver default指定有连接池所创建的连接的只读状态
#如果没有设置该值,则“setReadOnly”方法将不被调用,(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=
#driver default指定有连接池所创建的连接的事务级别
#可用值为下列之一:(详情可见javadoc)NONE,READ_UNCOMMITTED,READ_COMMITTED,REPEATABLE_READ,
defaultTransactionIsolation=READ_UNCOMMITTED
#<!-- 初始化连接 -->
initialSize=10
#<!-- 最大连接数量 -->
maxActive=20
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 -->
maxWait=10000
# JDBC驱动简历连接时附带的连接属性的格式必须为这样:【属性名=property;】
# 注意:“user”与“password”两个属性会被明确地传递,因此这里不需要包含他们
C3P0
需要导入的jar包
c3p0-0.9.5.5,mchange-commons-java-0.2.19
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 默认配置,如果没有指定则使用这个配置
如果在代码中“CombPooledDataSource ds=new CombPooledDataSource(),括号为空则为默认”-->
<default-config>
<property name="user">root</property>
<property name="password">zwy262425</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true;characterEncoding=utf8;useSSL=true</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="checkoutTimeout">30000</property>
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">3</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">2</property>
<property name="maxStatements">200</property>
</default-config>
<!-- 命名的配置,可以通过方法调用实现
如果在代码中“CombPooledDataSource ds=new CombPooledDataSource(“MySQL”),括号填上自己定义的数据源-->
<named-config name="MySQL">
<property name="user">root</property>
<property name="password">zwy262425</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="acquireIncrement">5</property>
<!-- 初始化数据库连接池时连接的数量 -->
<property name="initialPoolSize">20</property>
<!-- 数据库连接池中的最大的数据库连接数 -->
<property name="maxPoolSize">25</property>
<!-- 数据库连接池中的最小的数据库连接数 -->
<property name="minPoolSize">5</property>
</named-config>
</c3p0-config>
结论
无论使用什么数据库,本质还是一样的,DataSource接口不会变,当打就不会变
Druid