jdbc操作数据库

时间:2022-09-12 15:16:16
JDBC全称为:Java DataBase Connectivity(java数据库连接)。
SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JDBC。
学习JDBC 技术目的,使用Java技术操作数据库中数据记录
 
什么是驱动? 两个设备要进行通信,满足一定通信数据格式,数据格式由设备提供商规定,设备提供商为设备提供驱动软件,通过软件可以与该设备进行通信
 
如果没有JDBC,Java程序员需要面向各个数据库驱动接口编程,开发复杂 ; sun 公司提供一套统一JDBC接口规范,Java程序只需要使用JDBC就可以操作任何数据库,JDBC实现类由各个数据库厂商提供,
 
学习JDBC
1、学习JDK中自带JDBC接口规范  java.sql javax.sql
DriverManager 驱动管理类
Connection 连接接口
Statement (PreparedStatement、CallableStatement) 数据库操作
ResultSet 结果集接口
 
2、必须在工程中引入不同数据库驱动实现
 
JDBC体验
编程从user表中读取数据,并打印在命令行窗口中。
create table user(
   id int primary key auto_increment,
   username varchar(20) unique not null,
   password varchar(20) not null,
   email varchar(40) not null
);
    一、搭建实验环境 :
    1、在mysql中创建一个库,并创建user表和插入表的数据。
    2、新建一个Java工程,并导入数据驱动。
二、编写程序,在程序中加载数据库驱动
    DriverManager. registerDriver(Driver driver)
三、建立连接(Connection)
    Connection conn = DriverManager.getConnection(url,user,pass);
四、创建用于向数据库发送SQL的Statement对象,并发送sql
    Statement st = conn.createStatement();
    ResultSet rs = st.executeQuery(sql);
五、从代表结果集的ResultSet中取出数据,打印到命令行窗口
六、断开与数据库的连接,并释放相关资源
 
JDBC API 详解(重点)
DriverManager 类
static void registerDriver(Driver driver)  注册一个JDBC驱动程序
注意:DriverManager中可以同时注册多个JDBC驱动 例如:同时注册 mysql、oralce、db2 驱动 ,通过对JDBC URL分析,决定采用哪个驱动
static Connection getConnection(String url, String user, String password)  根据jdbc url 和 用户名、密码获得一个数据库连接
 
实际开发中,不推荐使用DriverManager.registerDriver 会导致驱动注册两次、会使得程序依赖 具体数据库API
推荐使用 :Class.forName("com.mysql.jdbc.Driver"); 加载Driver类时完成驱动注册,使得程序不依赖MySQL的API
 
***** 不要引入 与数据库相关 具体 API
 
JDBC URL
jdbc:mysql://localhost:3306/day13
这里 jdbc: 是JDBC连接协议
这里 mysql:// 是mysql数据库连接协议,JDBC子协议
localhost:3306 主机和端口
day13数据库
 
常用数据库URL写法
MYSQL jdbc:mysql://localhost:3306/day13
ORACLE jdbc:oracle:thin:@localhost:1521:sid
 
 
 
创建数据表 users
create table users(
   id int primary key ,
   username varchar(20) unique not null,
   password varchar(20) not null,
   email varchar(40) not null
);
 
插入一些数据记录
insert into users values(1,'zhangsan','123','zhangsan@itcast.cn');
insert into users values(2,'lisi','123','lisi@itcast.cn');
insert into users values(3,'wangwu','123','wangwu@itcast.cn');
 
oracle中执行 insert update delete 后 必须使用commit 操作
 
4) 在工程引入oracle的jdbc 驱动
安装目录\app\oracle\product\10.2.0\server\jdbc\lib\ojdbc14.jar
 
5) 修改Oracle驱动类 oracle.jdbc.driver.OracleDriver  和 URL  jdbc:oracle:thin:@localhost:1521:orcl
 
MySQL 如果连接localhost:3306 可以省略
jdbc:mysql://localhost:3306/day13 --------------- jdbc:mysql:///day11
JDBCURL 可以通过?和& 携带参数
常用属性:useUnicode=true&characterEncoding=UTF-8 ----------- 解决操作数据库乱码问题
 
Connection 连接接口
应用一:获得SQL的操作对象
Statement  conn.createStatement() 该对象可以将SQL发送给数据库进行执行
PreparedStatement conn.prepareStatement(sql) 对SQL语句进行预编译,防止SQL注入
CallableStatement conn.prepareCall(sql); 该对象可以调用数据库中存储过程 (以后Oracle学习)
 
应用二:对数据库事务进行管理(明天)
conn.setAutoCommit(boolean); 设置事务是否自动提交
conn.commit(); 提交数据库事务
conn.rollback(); 回滚数据库事务
Statement 用于将SQL 发送给数据库 获得操作结果
发送单条SQL
executeUpdate 用于向数据库发送 insert update delete 语句,返回int 类型参数,代表影响记录行数
executeQuery  用于向数据库发送 select 语句,返回ResultSet 结果集对象
execute 用于数据库发送任何SQL语句(包括 DDL DML DCL) 返回boolean ,SQL执行结果是ResultSet 返回true,否则 false
 
发送多条SQL
addBatch(sql) 将SQL加入批处理队列
executeBatch() 执行队列中所有SQL语句 ,一次性向数据库发送多条SQL
 
使用ResultSet 遍历结果集
while(rs.next()){
   // 根据数据库内部 列类型,选择相应 getXXX方法
   int ---- getInt
   varchart ---- getString
   date ----- getDate
}
 
在java.sql 定义Date、Time 、TimeStamp 对应数据库中 date time timestamp 类型 --------------- java.sql.Date/Time/TimeStamp 都是 java.util.Date 子类
java.sql.Date 只有日期没有时间
java.sql.Time 只有时间没有日期
java.sql.TimeStamp 既有日期也有时间
 
getXXX 有两种写法 第一种 getString(index) 结果集中列索引 第二种 getString(列名)
 
思考:如果SQL语句可能会返回一行数据,也可能查不到任何记录时,代码应该怎么写? ----- 用于登陆
if(rs.next()){
  // 查到了数据
}else{
  // 没有查到数据
}
 
ResultSet 高级应用 ---- 滚动结果集
Connection 接口的 createStatement()  返回Statement对象,操作SQL后 产生ResultSet 默认执行next 向前滚动,不支持在滚动中对数据进行修改 (只读不执行滚动)
Connection 接口还提供 createStatement(int resultSetType, int resultSetConcurrency) 在创建Statement对象 设置结果集类型,并发策略
 
结果集类型
ResultSet.TYPE_FORWARD_ONLY 只能向前,只能调用next 不能向回滚动
ResultSet.TYPE_SCROLL_INSENSITIVE 支持结果集向回滚动,不能查看修改结果
ResultSet.TYPE_SCROLL_SENSITIVE  支持结果集向回滚动,查看修改结果
 
结果集并发策略
ResultSet.CONCUR_READ_ONLY 只读
ResultSet.CONCUR_UPDATABLE 支持修改
 
常见三种组合
ResultSet.TYPE_FORWARD_ONLY 和 ResultSet.CONCUR_READ_ONLY  (默认) 只读不支持向回滚动
ResultSet.TYPE_SCROLL_INSENSITIVE 和 ResultSet.CONCUR_READ_ONLY  只读,支持向回滚动
ResultSet.TYPE_SCROLL_SENSITIVE 和 ResultSet.CONCUR_UPDATABLE 支持向回滚动,支持对数据修改
JDBC完成CRUD示例
编写对user表 增删改查程序,从重复代码中提取公共方法 JDBCUtils 工具类 ,将数据库连接参数写入properties 配置文件
 
示例代码
 
DAO模式
DAO模式(Data Access Object 数据访问对象):在持久层通过DAO将数据源操作完全封装起来,业务层通过操作Java对象,完成对数据源操作
* 业务层无需知道数据源底层实现 ,通过java对象操作数据源
 
DAO模式结构 :
1、数据源(MySQL数据库)
2、Business Object 业务层代码,调用DAO完成 对数据源操作
3、DataAccessObject 数据访问对象,持久层DAO程序,封装对数据源增删改查,提供方法参数都是Java对象
4、TransferObject 传输对象(值对象) 业务层通过向数据层传递 TO对象,完成对数据源的增删改查
 
DAO登录示例
 
使用三层结构和DAO模式登陆
cn.itcast.web  ---- 表现层
cn.itcast.service ---- 业务层
cn.itcast.dao ----- 持久层
cn.itcast.domain ----- 对应数据表实体类 TO对象
 
1、编写login.jsp 登陆表单,提交 /day11/login
2、编写LoginServlet 获得请求数据,调用业务层UserService
3、UserService编写业务逻辑,调用数据层UserDAO 返回结果
4、在LoginServlet获得结果,判断结果跳转页面
 
示例代码
 
SQL注入
由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL 关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。
 
String sql = select * from user where username ='' and password ='' ;
 
例如:
一、输入 username: 老李' or '1'='1    password 随意
select * from user where username ='老李' or '1'='1' and password ='';
* and 优先级 执行 高于 or
 
 
解决SQL注入:使用PreparedStatement 取代 Statement
PreparedStatement 解决SQL注入原理,运行在SQL中参数以?占位符的方式表示
select * from user where username = ? and password = ? ;
将带有?的SQL 发送给数据库完成编译 (不能执行的SQL 带有?的SQL 进行编译 叫做预编译),在SQL编译后发现缺少两个参数
PreparedStatement 可以将? 代替参数 发送给数据库服务器,因为SQL已经编译过,参数中特殊字符不会当做特殊字符编译,无法达到SQL注入的目的
 
问题:
SQL注入原理是什么?
    1.在输入时连接一个永远为真的一个值
    2.使用mysql 中的 – 注释
为什么PreparedStatement 可以防止SQL注入 ?
    因为它对sql语句进行预编译。
 
 
JDBC处理大数据
在实际开发中,程序需要把大文本 Text 或二进制数据 Blob保存到数据库。
Text是mysql叫法,Oracle中叫Clob
 
基本概念:大数据也称之为LOB(Large Objects),LOB又分为:
•clob和blob
•clob用于存储大文本。Text
•blob用于存储二进制数据,例如图像、声音、二进制文等。
 
对MySQL而言只有blob,而没有clob,mysql存储大文本采用的是Text,Text和blob分别又分为:
•TINYTEXT(255)、TEXT(64k)、MEDIUMTEXT(16M)和LONGTEXT(4G)
•TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
JDBC进行批处理
业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
实现批处理有两种方式,第一种方式:
•Statement.addBatch(sql)
•执行批处理SQL语句
•executeBatch()方法:执行批处理命令
•clearBatch()方法:清除批处理命令
采用Statement.addBatch(sql)方式实现批处理:
•优点:可以向数据库发送多条不同的SQL语句。
•缺点:
SQL语句没有预编译。
当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。例如:
        Insert into user(name,password) values(‘aa’,’111’);
        Insert into user(name,password) values(‘bb’,’222’);
        Insert into user(name,password) values(‘cc’,’333’);
        Insert into user(name,password) values(‘dd’,’444’);
 
实现批处理的第二种方式:
•PreparedStatement.addBatch()
 
采用PreparedStatement.addBatch()实现批处理
•优点:发送的是预编译后的SQL语句,执行效率高。
•缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。
 
注:
默认使用PreparedStatement是不能执行预编译的,这需要在url中给出useServerPrepStmts=true参数(MySQL Server 4.1之前的版本是不支持预编译的,而Connector/J在5.0.5以后的版本,默认是没有开启预编译功能的)。
例如:jdbc:mysql://localhost:3306/test?useServerPrepStmts=true
这样才能保证mysql驱动会先把SQL语句发送给服务器进行预编译,然后在执行executeQuery()时只是把参数发送给服务器。
 
当使用不同的PreparedStatement对象来执行相同的SQL语句时,还是会出现编译两次的现象,这是因为驱动没有缓存编译后的函数key,导致二次编译。如果希望缓存编译后函数的key,那么就要设置cachePrepStmts参数为true。例如:
jdbc:mysql://localhost:3306/test?useServerPrepStmts=true&cachePrepStmts=true
 
MySQL的批处理也需要通过参数来打开:rewriteBatchedStatements=true
例如:jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
 
Mysql驱动要使用mysql-connector-java-5.1.13以上
 
 
 
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
 
import org.gjt.mm.mysql.Driver;
          
 
 
// 注册驱动
DriverManager.registerDriver(new Driver());
 
// 连接测试
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/mydb1", "root", "root");
// 通过连接对象 创建操作sql语句对象 Statement
Statement st = conn.createStatement();
// 操作的sql语句
String t_sql = "select * from employee";
// 执行sql 返回 查询结果集 ResultSet;
java.sql.ResultSet rs = st.executeQuery(t_sql);
 
// 遍历结果集
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
String gender = rs.getString("gender");
System.out.println("流水编号:" + id + ", 姓名:" + name + " ,职位:" + gender
+ ";");
}
 
          // 释放资源
           rs.close();
           st.close();
           conn.close();