JDBC技术
一、引言
什么是JDBC?
Java数据库连接:使用java程序去访问和操作数据库的一组接口规范。
为什么是接口规范?
java程序可能会去访问和操作不同类型的数据库。
接口的好处?
屏蔽了不同数据库实现的代码差异。
JDBC接口代码的实现在哪?
有数据库厂商提供 ----> 驱动jar包
连接Oracle数据库, ojdbc14.jar (10g版本) ojdbc5/6
二、代码的实现思想
Java是一门面向对象的编程语言
编程步骤
1、环境搭建 (引入驱动jar包)
lib —> ojdbc.14 右键 build path ----> add to build path
2、注册驱动
Class.forName(“oracle.jdbc.OracleDriver”);
3、创建连接对象
@ip地址 : 监听端口port :SID实例名称
String url=“jdbc:oracle:thin:@localhost:1521:XE”;
Connection conn = DriverManager.getConnection(url, “hr”, “hr”);
4、发送SQL语句
//准备SQL语句 交由ps对象
String sql=“insert into clazz values(3,‘190班’)”;
PreparedStatement ps = conn.prepareStatement(sql);
//发送 返回值代表了数据的实际影响行数 一般没啥用 可选择省略
int i = ps.executeUpdate();
System.out.println(i); 5、接收结果集 (针对查询操作) 略
6、关闭资源
写在finally中
if(ps!=null)try {ps.close();} catch (SQLException e) {}
if(conn!=null)try {conn.close();} catch (SQLException e) {}
public static void main(String[] args) {
//1.环境搭建 对于jar包引入 以及add to build path
Connection conn=null;
PreparedStatement ps = null; // Statement 、 SQL注入的问题
try {
//2.注册驱动
//ClassNotFoundException 1、没有导入驱动jar 2、全限定名称书写错误
Class.forName("oracle.jdbc.OracleDriver");
//3.创建连接
//空指针 换jar包
//SQLException 用户名密码书写错误 ORA-01017
//SQLException The Network Adapter could not establish the connection
// 1. ip地址书写错误 2.监听服务没有启动
conn = DriverManager.
getConnection("jdbc:oracle:thin:@localhost:1521:xe", "hr", "hr");
//4.准备 并发送SQL语句
//SQLException 基本上都是SQL语法问题
String sql = "insert into clazz values(3,'190')";
ps = conn.prepareStatement(sql);
ps.executeUpdate(); //返回值代表了DML操作影响的行数
//5.接收查询结果集 略
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
//6.关闭资源
if(ps!=null)try {ps.close();} catch (Exception e2) {}
if(conn!=null)try {conn.close();} catch (Exception e2) {}
}
}
JUnit 4 测试
一个类中只能存在一个main函数只能测试一个代码方法。
JUnit常用测试手段,不适用main函数进行代码的测试书写,选择@Test注解添加入类中的方法,每一个方法都可以视为独立的测试(可以运行)方法。
书写规范!!!!
Class MyTest{
@Test
public void 方法名称随意(){
//代码、、、、
}
//使用方法替换main函数
}
三、查询操作
//4、书写SQL语句 并发送
String sql="select * from users";
ps = conn.prepareStatement(sql);
//ResultSet 查询数据的结果集
rs = ps.executeQuery();
//5、结果集 进行操作
//判断是否存在下一行数据 rs.next 方法
while(rs.next()){
//存在数据 获取数据 rs.getXXX
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("name"));
System.out.println("-------------------------");
}
注意:
1、 char varchar varchar2 统一都是 rs.getString()
2、 rs.getXXX( int / String );
int : 列的索引 String : 列的名称 (建议)
四、参数动态化
SQL语句中所有的参数数据都是用硬编码形式,需要进行动态替换.
占位符替换 —> 使用 ? 替换数据
书写: 在 ps = conn.prepareStatement(sql); 获取SQL语句之后
在 ps.executeUpdate / Query (); 发送SQL语句之前
//4.准备 并发送SQL语句
//SQLException 基本上都是SQL语法问题
String sql = "insert into users values(?,?)";
ps = conn.prepareStatement(sql);
// 使用数据替换? 第一个参数: 替换第几个? 第二个参数 数据值
ps.setInt(1, 4);
ps.setString(2, "刘洋");
//发送
ps.executeUpdate();
五、工具类的封装
常用的配置文件类型
.properties .xml .yml
public class JDBCUtil {
private static Properties properties = new Properties();
//对于程序中冗余代码的抽取
//配置参数写入配置文件进行读取
//执行效率 静态代码块 会在类加载的时候执行一次 用来读取配置文件
static{
// 读取项目中的配置文件 路径: 以src目录为根路径 / 代表根 src
//空指针异常
// NoClassDefFoundError 路径书写错误
InputStream is =
JDBCUtil.class.getResourceAsStream("/com/baizhi/am/jdbc.properties");
try {
//将读取的数据加载如集合对象
properties.load(is);
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//1、 对于注册驱动 和 连接创建的封装
public static Connection getConnection()
throws ClassNotFoundException,SQLException{
Class.forName(properties.getProperty("driver"));
Connection conn = DriverManager.getConnection(properties.getProperty("url"),
properties.getProperty("username"),
properties.getProperty("password"));
return conn;
}
//2、 资源的关闭的封装
public static void close(Connection conn,PreparedStatement ps,ResultSet rs){
if(rs!=null)try {rs.close();} catch (Exception e2) {}
if(ps!=null)try {ps.close();} catch (Exception e2) {}
if(conn!=null)try {conn.close();} catch (Exception e2) {}
}
public static void close(Connection conn,PreparedStatement ps){
if(ps!=null)try {ps.close();} catch (Exception e2) {}
if(conn!=null)try {conn.close();} catch (Exception e2) {}
}
}
六、ORM对象关系映射
数据库中的数据 和 java中对象的 关系。 (编程思想)成为值对象
将数据库获取的数据封装成java程序中的对象方便程序的处理和操作
命名关系?
表名 Users ----> 类名 User (实体类) 一 一 对应关系
七、DAO设计思想 (层)
数据访问对象: java对于数据的操作进行统一的封装归类。
DAO类:在java程序中有一些特定的类专注于对于数据库的访问和操作!
1、现有的JDBC操作还停留在Test测试类或者main函数中。 如何去使用代码?
2、需要将代码进行封装 —> 封装成方法。 包含JDBC操作方法的类称之为DAO类
DAO的核心: java程序进行数据访问
3、DAO = DAO接口 + DAO的实现类
好处: 3.1、 接口是规范
3.2、 接口解耦合 使用和实现分离
现阶段DAO实现类使用最为原始的JDBC技术进行开发,后续可能存在技术的升级
使用全新的技术完成接口的实现,对于使用者来说没有任何的影响。
4、一个程序中实体类数量以及接口数量和实现类数量由什么决定?
表 和 实体类 一 一 对应关系
接口数量 也和 实体类 和 表 (建议最好 一 一 对应)
t_user表 —> User实体类
----> UserDAO 接口 (一般会将对于一张表的所有操作定义在一个接口中)
完成DAO类的开发
中心思想:java程序区访问和操作数据库
1、建表
2、实体类对象
3、根据CRUD需求定义接口中的方法
4、使用JDBC技术去实现接口
需求: 完成对于t_user表中数据的操作
t_user 要求 列: id(integer) name(varchar2) age(number) birthday(date)
1.根据名称查询用户数据
2.根据id删除数据
3.向表中添加数据
4.修改用户数据的name 和 age
5.查询所有数据
存在日期类型的数据 思路!
//实体类的set、get方法中完成数据类型的转换
//封装 类属性 赋值权/控制权
public java.sql.Date getBirthday() {
return new java.sql.Date(birthday.getTime());
}
public void setBirthday(String birthday) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
try {
Date date = sdf.parse(birthday);
this.birthday = date;
} catch (ParseException e) {
e.printStackTrace();
}
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
八、Service业务 (层)
业务:用户的需求和项目的功能,对于现有程序中一个或者多个DAO中一个或多个方法的调用
业务 : 接口 + 实现
需求分析的探讨:
业务的方法 ---> 业务接口 ----> DAO的接口 ----> 抽象出表和实体类
业务开发步骤
中心思想:java程序区访问和操作数据库
1、建表
2、实体类对象
3、根据CRUD需求定义接口中的方法
4、使用JDBC技术去实现接口
5、定义业务接口 service
6、完成业务的接口实现
需求:转账
UserService 接口
boolean transfer(User u1,User u2);
UserDAO接口
User queryUser (String username);
void updateUser(User user);
实体类 / 表 User t_user (ID,username,password ,account)
九、事务
业务操作中多条DML操作语句要么一起彻底成功要么一起彻底的失败。
JDBC技术默认自动commit提交!!!!
关闭JDBC的默认提交策略,改为手动事务控制。
JDBC的事务控制 依赖于连接对象。 connection对象
//关闭JDBC默认自动提交方式
connection.setAutoCommit(false);
//手动提交
connection.commit();
//手动回滚
connection.rollback();
问题1?
Service中发生了异常,但是数据并没有回滚?
Service和DAO中的连接对象并不是同一个.
问题2?
如何保证Service和DAO可以获取同一个连接对象?
1、使用成员变量将连接对象进行传递. -----> 造成接口污染 X
JDBC技术中对于事务的控制使用连接对象Connection,
MyBatis技术中对于事务的控制使用连接对象sqlSession。
2、final修饰
数据库不可能只有一个人在用
线程绑定
当用户进行数据访问操作时,会在当前线程中绑定唯一的连接对象。
ThreadLocal线程绑定
第一次创建连接对象后将对象存入线程中使用 set 方法。
之后再使用连接对象 通过 get 方法获取。
代码写在什么地方?? 用户通过工具类获取连接的时候进行判断
当关闭资源的时候说明连接已经使用完毕,解除连接和线程的绑定。
tl.remove();
工具类中 getConnection方法中
private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>();
//连接的绑定
public static Connection getConnection()
throws ClassNotFoundException,SQLException{
//每一次获取连接对象都是通过 getConnection 方法 调用时需要进行判断当前是第几次获取连
//当前线程是否存在连接对象
Connection conn = tl.get();
if(conn == null){
//第一次创建连接
Class.forName(properties.getProperty("driver"));
conn = DriverManager.getConnection(properties.getProperty("url"),
properties.getProperty("username"), properties.getProperty("password"));
//将创建出来的连接和当前线程绑定
tl.set(conn);
return conn;
}
//直接return 则说明线程中已经存在了连接对象
return conn;
}
//关闭资源的时候需要解除1线程的绑定 tl.remove();
public static void close(Connection conn,PreparedStatement ps){
if(ps!=null)try {ps.close();} catch (Exception e2) {}
if(conn!=null)try {conn.close();tl.remove();} catch (Exception e2) {}
}
DAO中以后不再会关闭连接对象,Service中关闭连接对象。
注意:
1、DAO中不能关闭连接对象
2、最差情况:DAO中DML操作需要在catch块中完成手动的异常上抛
或者所有的方法全抛异常
return conn;
}
//直接return 则说明线程中已经存在了连接对象
return conn;
}
//关闭资源的时候需要解除1线程的绑定 tl.remove();
public static void close(Connection conn,PreparedStatement ps){
if(ps!=null)try {ps.close();} catch (Exception e2) {}
if(conn!=null)try {conn.close();tl.remove();} catch (Exception e2) {}
}
DAO中以后不再会关闭连接对象,Service中关闭连接对象。
注意:
1、DAO中不能关闭连接对象
2、最差情况:DAO中DML操作需要在catch块中完成手动的异常上抛
或者所有的方法全抛异常
#例子代码
1.工具类代码
public class JDBCUtil {
private static Properties properties=new Properties();
private static ThreadLocal<Connection> tl= new ThreadLocal<Connection>();
static{
InputStream is=JDBCUtil.class.getResourceAsStream("/util/jdbc.properties");
try {
properties.load(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//注册驱动类
public static Connection getConnection() throws Exception{
Connection con=tl.get();
if(con==null){
Class.forName(properties.getProperty("driver"));
con=DriverManager.getConnection(properties.getProperty("url"),
properties.getProperty("username"),
properties.getProperty("password")
);
tl.set(con);
}
return con;
}
//关闭资源
public static void close(PreparedStatement ps){
if(ps!=null)try{ps.close();}catch(Exception e){}
}
public static void close(PreparedStatement ps,ResultSet rs){
if(rs!=null)try{rs.close();}catch(Exception e){}
if(ps!=null)try{ps.close();}catch(Exception e){}
}
public static void close(Connection con){
if(con!=null)try{con.close();}catch(Exception e){}
}
}
2.DAO层实现类
public class ProductDAOImpl implements ProductDAO {
@Override
public List<Product> queryAllProdut() {
Connection con=null;
PreparedStatement ps=null;
List<Product> arrl = new ArrayList<Product>();
ResultSet rs=null;
try{
con=JDBCUtil.getConnection();
String sql="select * from product";
ps=con.prepareStatement(sql);
rs=ps.executeQuery();
while(rs.next()){
Product product= new Product();
product.setId(rs.getInt("pro_id"));
product.setName(rs.getString("pro_name"));
product.setPrice(rs.getDouble("pro_price"));
product.setDate(rs.getDate("hire_date"));
arrl.add(product);
}
}catch(Exception e){
e.printStackTrace();
}finally{
JDBCUtil.close(ps, rs);
}
return arrl;
}
@Override
public Product queryByName(String name) {
Connection con=null;
PreparedStatement ps=null;
ResultSet rs=null;
Product product=null;
String likename="%"+name+"%";
try{
con=JDBCUtil.getConnection();
String sql="select * from product where pro_name like ?";
ps=con.prepareStatement(sql);
ps.setString(1, likename);
rs=ps.executeQuery();
if(rs.next()){
product= new Product();
product.setId(rs.getInt("pro_id"));
product.setName(rs.getString("pro_name"));
product.setPrice(rs.getDouble("pro_price"));
product.setDate(rs.getDate("hire_date"));
}
return product;
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
JDBCUtil.close(ps, rs);
}
}
@Override
public void insertProduct(Product pro) {
Connection con= null;
PreparedStatement ps = null;
try{
con=JDBCUtil.getConnection();
String sql="insert into product values(?,?,?,?)";
ps=con.prepareStatement(sql);
ps.setInt(1, pro.getId());
ps.setString(2, pro.getName());
ps.setDouble(3, pro.getPrice());
ps.setDate(4, (java.sql.Date)pro.getDate());
ps.executeUpdate();
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException("修改操作出错");
}finally{
JDBCUtil.close(ps);
}
}
@Override
public void updateProductDate(String name) {
Connection con =null;
PreparedStatement ps=null;
try{
con=JDBCUtil.getConnection();
String sql="update product set hire_date = sysdate where pro_name=?";
ps=con.prepareStatement(sql);
ps.setString(1,name);
ps.executeUpdate();
}catch(Exception e){
e.printStackTrace();
throw new RuntimeException("修改日期出现错误");
}finally{
JDBCUtil.close(ps);
}
}
}
3.service层实现类
public class ProductServiceImpl implements ProductService {
ProductDAOImpl pdi = new ProductDAOImpl();
@Override
public List<Product> queryAll() {
List l =pdi.queryAllProdut();
return l;
}
@Override
public Product queryName(String name) {
Product product = pdi.queryByName(name);
return product;
}
/*完成商品的添加入库 (如果名称重复,修改hire_date为当前时间)
1、查询商品名称
2、不存在 数据添加
3、存在 修改 hire_date 为系统当前时间*/
@Override
public boolean insert(Product product) {
Connection con=null;
try{
con=JDBCUtil.getConnection();
con.setAutoCommit(false);
Product queryResult = pdi.queryByName(product.getName());
if(queryResult != null){
pdi.updateProductDate(product.getName());
}else{
pdi.insertProduct(product);
}
con.commit();
return true;
}catch(Exception e ){
e.printStackTrace();
if(con!=null)try {con.rollback();} catch (SQLException e1) {e1.printStackTrace();}
return false;
}finally{
JDBCUtil.close(con);
}
}
}