JDBC
java提供的一套访问数据库接口
java--->操作数据库
JDBC发展历史:[了解]
TYPE1:JDBC--odbc桥:间接访问数据库//odbc是c语言的jdbc
TYPE2:java---本地api;
java语言通过本地客户端---->访问数据
TYPE3:java---中间服务器(网络):
java通过中间服务器-----访问数据库(mysql,oracle,db2)
优点:可移植性高
缺点:效率低.
TYPE4:JDBC---访问数据库
数据库厂商提供接口的实现类-----数据库的驱动
ojdbc14.jar(jdk1.4)ojdbc5.jar(jdk5.0) ojdbc6.jar
常见数据库访问接口(类)
java.sql.Connection(interface):
//代表一个数据库的连接
java.sql.Statement(interface):
//执行sql的工具
java.sql.ResultSet(interface):
//存放查询的结果数据
**只有执行查询sql,才会有结果数据返回
java.sql.DriverManager;
//管理多个驱动类
**DriverManager会管理多个数据库驱动,会自动选择最合适的驱动类,去建立数据库的连接
JDBC操作数据库的模型:
1.加载驱动
2.建立数据库连接(Connection)
3.准备一个执行sql的工具(发送sql语句至数据库)
4.执行sql语句
5.(执行查询sql),DB 会将数据返回给Resultset,处理结果集
6.释放资源;
***Connection Statement ResultSet
**原则:先打开的后释放
课堂案例t_account表:
create table t_account(
c_id number(10) primary key,
username varchar2(50),
password varchar2(50),
balance number(20,5)
);
第一个jdbc程序
//0.将ojdbc5.jar放入classpath环境中
//classpath: .;D:\ojdbc5.jar
//1.加载驱动 oracle.jdbc.OracleDriver
Class.forName("oracle.jdbc.OracleDriver");
//2.建立数据库连接 协议url 用户名user 密码password
String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
// 协议@ip:端口号:实例名(xe|orcl)
//本地ip:localhost127.0.0.1
String user = "hr";
String password = "hr";
java.sql.Connection conn = java.sql.DriverManager.getConnection(url,user,password);//conn代表一个数据库连接
//3.创建发送sql的工具
java.sql.Statement stm = conn.createStatement();
//4.执行sql:insert into t_account values。。。。
String sql = "insert into t_account values(100001,'wangyx','123',10)";
//**sql字符串后不加 分号
stm.executeUpdate(sql);//执行增删改
//5.释放资源
if(stm!= null)
stm.close();
if(conn != null)
conn.close();
myeclipse开发jdbc:
0.导入ojdbc5.jar
1.项目右键---build path--configured build path--librari---add external jars
ResultSet ;
1.存放查询语句执行返回的结果数据
2.结果集ResultSet的rs指针初始位置,第一行数据前一行
3.next(); 返回值boolean:true当前行有数据,false 当前行没有数据
让指针下移1位
4.获得rs指针所指向的当前行数据
getXXXXX()
getInt(下标)|getInt("c_id")c_id number
getString("username");username varchar2
getDouble("balance");balance number(10,2)
getDate(下标|列名)
【重点】
JDBC6步骤
ResultSet 结果集的处理
思考:
写一个程序:
让用户输入卡号和密码,如果卡号和密码正确
打印“你可以取钱挥霍了”
否则打印“吞卡.....”
======================================================================
JDBC编程6步骤:
1.加载驱动:oracle.jdbc.OracleDriver
2.建立连接: url user password
3.创建发送sql的工具
4.执行sql
5.(查询),处理结果集ResultSet
6.释放资源:后打开的先释放;
ResultSet结果集
next(): 将rs的指针下移一位,(会指向第一行数据),
返回值为boolean值,true代表当前行有数据
getInt(1)|getInt("c_id")---获得数字类型
getString("username")--获得字符串类型
======================================================
Statement:
stm.executeUpdate("增删改sql")---执行增删改的sql
返回值 int
影响了表中多少行数据
stm.executeQuery("查询sql")---执行查询sql
返回值:ResultSet
是一个容器,存放查询语句返回的结果数据
**即使发送的sql语句查询不到任何数据,resultSet对象也不会为null
动态sql:
动态sql的构建
jdbc发送的sql语句,由用户输入的内容不同,导致发送的sql语句也不同
String :
"select * from t_account where username = '"+username+"' and password = '"+password+"'"
问题描述:
输入username: aas' or 1=1 --
password: 123
select * from t_account where username = 'aas' or 1=1 --' and password = '123';
结果:能够查询数据,验证通过
***SQL注入漏洞
PreparedStatement: 是Statement子接口.
**发送sql语句
1.创建发送sql工具
String sql = "select * from t_account where username = ? and password = ?";
PreparedStatement pstm = conn.prepareStatement(sql);//接收的sql是不完整的
2.对sql中的?进行绑定参数
pstm.setInt(?下标,绑定的值);
pstm.setString(..),
pstm.setDouble(..);
pstm.setDate(...);
3.执行sql
pstm.executeUpdate();//执行方法不需要sql
ResultSet rs = pstm.executeQuery();
课堂练习:
1。写一个程序,向t_account表中插入一条数据(卡号,用户名,密码,余额),让用户输入
2.练习pstm的登录程序
需求:
向数据库中插入1000条数据
1000 10000
Statement 901878716 4188379667
PreparedStatement826617465 3988919980
批处理 474696792559667059
**1.PreparedStatement执行同构sql比Statement效率高(大量数据插入)
①当创建PreparedStatement是,需要sql
PreparedStatement pstm = conn.prepareStatement(sql);//将sql语句发送给 数据库
oracle执行:
数据库验证该sql的执行权限
验证slq的语法结构
翻译成内部指令
②pstm发送绑定参数 发送10000
Oracle执行sql的过程:
1.验证sql语句执行权限
2.验证sql语句的语法结构
3.将sql语句转换成内部指令
4.执行内部指令
批处理:
处理大量同构sql
1.创建发送sql的工具 PreparedStatement
String sql = "....???";
PreparedStatement pstm = conn.prepareStatement(sql);
2.绑定参数
pstm.setXXX(?的下标,值);
pstm.setXXX
pstm.setXXX
//缓存绑定参数到本地
pstm.addBatch();
3.发送缓存的绑定参数
pstm.executeBatch();
***以内存空间换时间效率;
问题:现有的JDBC存在大量的冗余代码
1.加载驱动
获得连接
// 1.加载驱动
Class.forName("oracle.jdbc.OracleDriver");
// 2.获得数据库连接 Connection
String url = "jdbc:oracle:thin:@localhost:1521:xe";
String user = "hr";
String password = "hr";
Connection conn = DriverManager.getConnection(url, user, password);
2.释放资源
// 6.释放资源
if(rs != null)
rs.close();
if(stm != null)
stm.close();
if(conn != null)
conn.close();
解决方案:
将所有重复代码封装在函数中.
JDBCUtil工具类
1.获得连接
2.释放资源
【重点】
preparedStatement 概念和代码
批处理()TestBatchUpdate.java
JDBCUtil
习题:11.19,20,22
==============================================================================
问题:
JDBCUtil1中有一些在使用过程中需要经常修改的值
解决方案:
将需要经常修改的值,从java代码中抽取出来.放在文件中.
**
1.jdbcpropertie.txt
"oracle.jdbc.OracleDriver"
"jdbc:oracle:thin:@localhost:1521:xe"
"hr"
"hr"
2.
InputStream is = new InputStream("路径");
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
..br.readLine();
properties 文件
文件名:**.properties
内容:字符串
特点: key=value
*******************************************
driverClassName=oracle.jdbc.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:xe
user=hr
password=hr
properties 类
Map集合
特点:键--字符串 值--字符串
读取properties文件
使用:
1.创建读取properties文件的Properties对象
Properties prop = new Properties();
2.准备一个对应properties文件的输入流
InputStream is = new InputStream("路径");
**类加载:jvm读取硬盘上.class文件过程
类名.class.getResourceAsStream("路径");
3.加载文件信息到prop
prop.load(is);
4.获得prop中的内容
prop.getProperty("key");
prop.getProperty("driverClassName");//oracle.jdbc.OracleDriver
JDBCUtil3
getConnection方法中:将加载配置文件,和加载驱动,只执行一次
**放在JDBCUtil工具类的static代码块中
课堂练习:
1.联系JDBCUitl新版本,并测试
2.程序:
让用户输入信息,插入account表中
根据用户输入的卡号,删除account表中的一条数据
思考:
account表中一条数据修改的程序(根据卡号修改)
分析:
对于数据库中一张表的操作总结:增删改查
**将该表的所有操作封装在一个类中
类名:AccountDAO
//1.查询
//2.添加
//3.删除
//4.修改
实体:Entity
**属性对应表的列
public class Account{// t_account
private Integer cid;// c_id
private String username;// username
private String password;// password
private Double balance;// balance
}
**提供有参和无参构造方法
**提供get/set方法
DAO编程模型: data access object 数据访问对象
命名:***DAO
方法://将对于该表的所有的数据库操作,全部封装在DAO类中
public Account selectAccountById(Integer id){}
public void insertAccount(Account Acccount){}
public void deleteAccount(Integer id){}
public void updateAccount(Account account){}
public List<Account> selectAllAccounts(){}
...
Entity实体: javaBean
封装数据库数据
1.类名和表名相关
2.所有属性和表中所有列一一对应
3.添加无参和有参构造方法
4.添加所有属性的set/get方法
5.实现serializable接口
作业:
①完成AccountDAO剩余的方法;(删除,查询所有,修改)
②
表:
create table person(
id number(10) primary key,
username varchar2(50),
email varchar2(100),
phone varchar2(20),
address varchar2(200)
);
create sequence seq_person start with 10000;
书写该表操作的DA---PersonDAO
书写全部的方法实现+测试
==================================================================
【重点】
吃货(吃饭需求) 服务员(收集需求)----厨师(处理业务需求)--- 采购员(采购原材料)
用户(取钱) (收集数据)view (业务处理)AccountDAO(数据库数据)
项目分层:
view 接收用户数据,展示数据AccountView
biz(service) 业务处理 AccountService
dao 数据库访问AccountDAO
**解耦和,优化代码结构,代码可重用
1.将业务功能封装成service中的方法
Service层:业务处理
一个业务功能就是一个事务,需要将所有的业务代码放在一个事务中执行
**jdbc中的conn的事务提交模式为自动提交
方法:
try{
开启事务
业务处理
提交事务
}cath(){
conn.rollback;事务回滚
}
课堂作业:
1.取钱三层结构:
2.转账功能
收集数据(转账卡号,密码,到账卡号,转账金额)
将转账业务封装在service中
//开启事务
**根据转账卡号查询账户,判断账户是否存在
**判断密码
**判断到账卡号是否存在
**将到账账户+¥
**将转账账户-¥
//提交事务
【难点】
转账业务分析:
问题:如果转账失败,数据不发生改变,但是却到账卡号+钱...
事务控制失败了.....
原因:
service控制事务所用的conn和dao发送sql语句所用的conn不是同一连接对象
解决方案:
**一次调用过程中:保证service和dao用同一个连接对象
1.只调用一次jdbcutil,将service获得的连接对象传给dao
缺点:将jdbc API耦合在方法参数中,不利于解耦和
----
线程Thread对象中有一小块内存空间,可以用来存储数据;(类似Map类型)
ThreadLocal:操作当前线程对象的内部小块内存空间
ThreadLocal<Connection> tdl = new ThreadLocal<Connection>();
set(conn);//将conn对象存入当前线程对象中
get();//将当前线程对象中的值取出
remove();//将当前线程中的值,移除;
---------------------------------------------
JDBCUtil4:
getConnection();
先获得当前线程中的conn
conn = tdl.get();
判断conn是否为空
if(conn == null){
//创建新的conn
//将新的conn放入当前Thread
tdl.set(conn);
}
release()
//释放conn资源
tdl.remove();
conn.close()
Service:
获得连接
开启事务
提交事务
回滚事务
释放conn资源
DAO
。。。
释放资源(不能关闭conn)
========================================================================
view:收集数据,展示数据:调用service
service:处理业务:(事务开启,提交事务|回滚事务)[释放资源conn]:调用dao
dao:数据访问(DB): JDBC代码 释放资源(rs,stm|pstm,null)
开发注意事项:
1.需要给dao和service定义接口
项目分包管理:
com.zpark.项目名.view
com.zpark.项目名.service
.impl
com.zpark.项目名.dao
.impl
com.zpark.项目名.entity
com.zpark.项目名.util
com.zpark.项目名.sql--项目用到的sql文件语句
com.zpark.项目名.test
步骤:
1.新建java项目
**导入项目需要的jar包
2.创建包结构
xxx.xxx.xxx.view
.service
.dao
.entity
.sql
.test
.util
.conf ---放配置文件
-----------------------
多表操作:
1 对 n
--学生管理系统
(学号,姓名,年龄,班级id 班级名称)
学生表:
stu_id stu_name agec_id
001 wangyx17 001
002 houhui16 001
003 chengcheng19 001
班级表
class_id class_name
001 java35班
**原则:将外键建在多的一方;
表---实体
public class Student{//学生
private Integer stuId;
private String stuName;
private Integer age;
private Clazz clazz;//该学生所在的班级信息
}
public class Clazz{//班级
private Integer classId;
private String className;
private List<Student> students;//该班级所有的学生对象
}
**原则:
1.表对应实体
2.列(本实体信息)对应属性
3.在实体中添加另一方的关系属性
1 对 1
--学生租赁电脑的管理系统;(一个学生只能对应一台电脑)
学号,姓名,年龄,电脑编号,电脑名称
学生表
stu_id stu_name age c_id(unique)
022 孙振19 001
023 王乐20 002
024 罗亮21 003
电脑表
computer_id computer_name s_id(unique)
001 Thinkpad022
002 dell023
003 HP024
**FK+UK(外键+唯一)
**将外键建在更重要的一方;
实体:
public class Student{
private Integer stuId;
private String stuName;
private Integer age;
private Computer computer;
}
public class Computer{
private Integer cId;
private String comName;
private Student student;
}
原则:
1.表和实体
2.列和属性对应
3.互相保留对方的引用(关系属性)
n 对 n
--学生考试系统
(学生学号,姓名,年龄,科目,科目编号,成绩)
学生表
stu_id stu_name age
001 李山20
002 侯青21
003 刘yi22
课程表
course_id course_name
01 coreJava
02 jdbc
学生课程关系表(选课表)
s_id c_idscore
001 01100
001 02100
002 01150
003 01180
**创建一张关系表,将外键建在关系表中
--每个学生每个课程只能选一次
primary key(s_id,c_id)
实体:
public class Student{
private Integer stuId;
private String stuName;
private Integer stuAge;
//private List<Course> courses;
private Map<Course,Double> score;
}
public class Course{
private Integer cID;
private String courseName;
//private List<Student> student;
private Map<Student,Double> score;
}
原则:
1.实体和表对应(关系表一般不会创建实体)
2.属性和列对应
3.互相保留对方的集合引用
功能:学生管理系统(学号,姓名,年龄,班级id 班级名称)
1.添加学生信息 insertStudent(Student stu)
输入信息:
姓名
年龄
班级编号
2.查询学生信息 selectStudentById(Integer sId);
展示信息:
学号
姓名
年龄
班级名称
步骤:
1.建表(测试数据)
2.建实体
作业:
完成Person通讯录的功能:
1.添加个人信息
2.根据编号查询个人信息
3.修改个人信息
4.根据编号删除个人信息
5.展示所有的通讯录信息
create table person(
id number(10) primary key,
username varchar2(50),
email varchar2(100),
phone varchar2(20),
address varchar2(200)
);
create sequence seq_person start with 10000;
要求:
0.ojdbc5.jar
1.代码分包
2.分层
PersonDAO
PersonDAOImpl
personService
PersonServiceImpl
PersonView
JDBCUtil4
jdbc.properties