JDBC:Java DataBase Connection

目录

第一节:JDBC入门

1.  概念

2. JDBC驱动

3.  JDBC 访问数据库的步骤

4. 几个重要的接口和类

5.  具体操作

第二节:Dao封装

1.简单打开关闭的封装

2. 查询封装

3. 增删改

4. 新增返回主键

5 .读取配置文件

第三节: JDBC深入编程

1. 事务

2. 批处理

3. 日期处理

4. Dao模式

 1、DAO接口: 把对数据库的所有操作定义成抽象方法,可以提供多种实现。

 2、DAO 实现类: 针对不同数据库给出DAO接口定义方法的具体实现。

 3. 实体类:用于存放与传输对象数据。没有不影响

4.数据库连接和关闭工具类: 避免了数据库连接和关闭代码的重复使用,方便修改。

5.Main:逻辑代码的调用——Service业务逻辑层


第一节:JDBC入门

1.  概念

JDBC:Java DataBase Connection

java数据库连接技术的简称,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,同时,JDBC也是个商标名。不同数据库有不同的驱动包,相关的链接的实现类都在驱动包(链接驱动————jar包,一堆类和接口)

jdk自带提供了一套接口以及一些实现类,但是不包含链接,如果想要具体的链接类必须在驱动包找——链接数据库必须提供驱动包

简单地说,JDBC 可做三件事:与数据库建立连接、发送 操作数据库的语句并处理结果。

DriverManager :依据数据库的不同,管理JDBC驱动

Connection :负责连接数据库并担任传送数据的任务——通过DriverManager获取的 

Statement :由 Connection 产生、负责执行SQL语句

ResultSet:负责保存Statement执行后所产生的查询结果

2. JDBC驱动

Java DataBase Connection:

使用java使用连接数据库。

由JDBC驱动直接访问数据库

优点:100% Java,快又可跨平台

缺点:访问不同的数据库需要下载专用的JDBC驱动

不同的数据库都得下载不懂jar包,相同的数据库也需要不同的jar(版本不同) 版本不兼容

使用JDBC-ODBC桥方式连接数据库:

将对JDBC API的调用,转换为对另一组数据库连接API的调用

优点:可以访问所有ODBC可以访问的数据库

缺点:执行效率低、功能不够强大

3.  JDBC 访问数据库的步骤

(1)加载数据库的驱动:DriverManager.registerDriver(Driver)_Jar

DriveManager里面的registerDriver(Driver driver)

Class.forName(包名.类名)_驱动类中书写一个静态代码,只要加载这个

(2)创建与数据库的连接

DriverManager里面getConnection(String url, String user, String password)

(3)获取Statemnt对象:stm=conn

(4)编写sql语句:Navicat写好

(5)执行sql语句

Statement里面executeQuery(String sql)

(6)处理结果

  (7)释放资源(关闭连接):数据库的连接个数是有限。

4. 几个重要的接口和类

A.    JDBC驱动程序管理器 DriverManager:

是JDBC的管理层,作用于用户和驱动程序之间。它跟踪可用的驱动程序,并在数据库和相应的驱动程序之间建立连接。

JDBC标准规定:所有的驱动程序类必须包含一个静态部分。这个静态部分在加载该实例时由DriverManager类进行注册。

用户在正常情况下将不会直接调用DriverManager.regiserDriver方法,而是在加载驱动程序时由驱动程序自动调用。

注册驱动程序:

Class.forName(“com.microsoft.sqlserver.jdbc. SQLServerDriver”);

Class.forName(“sun.jdbc.odbc.JdbcOdbcDriver”);

Class.forName( "oracle.jdbc.driver.OracleDriver ");

Class.forName(“com.mysql.jdbc.Driver”);

注册的驱动程序类名称必须在用户的classPath中。

B.    Connection接口及对象:

代表数据库的连接,是接口,在java.sql包里面

创建Statement对象:Statement createStatement()

创建预编译对象PreparedStatement prepareStatement(String sql)

DriverManger的方法:

static Connection getConnection(String url,String user,String password):

url: jdbc : <subprotocol> : <subname>

// 用户名密码不用说肯定有 url:指明链接数据库 链接端口号:指标链接服务 指明数据库

Connection conn=DriverManager.getConnection
("jdbc:mysql://localhost:3306/schooldb?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true","root","root");

连接串:

协议:数据库://IP地址:端口号/数据库名

C.    Statement对象:

1、由Connection接口的createStatement()方法创建。

2、常用方法有:

executeUpdate(String sql);可以执行插入、删除、更新等操作,返回值是执行该操作所影响的行数。int

executeQuery(String sql); 执行SQL查询并获取到ResultSet对象

execute();可以执行任意SQL语句,然后获得一个布尔值,表示是否返回是否成功失败

(Statement.RETURN_GENERATED_KEYS 可以获得返回的主键)

D.    ResultSet接口:

在JDBC中数据库的所有查询记录将使用ResultSet接收并显示内容

ResultSet常用的方法如下:

next():判断有没有下一行,有指向下一行,并返回true,没有返回false

getObject(int index):索引,获取当前行第几列的值。1--Object

getObject(String lieming):列名,根据列名获取列名对应的值

E.    ResultSetMetaData对象

可用于获取关于 ResultSet 对象中列的类型和属性信息的对象

获得ResultSetMetaData对象:

ResultSet对象的getMetaData()方法;

ResultSetMetaData对象的常用方法:

int getColumnCount() : 获得本次查询中的列数

String getColumnName(int  column) :获得本次查询中指定列的列名。

String getColumnTypeName(int column)  :检索指定列的数据库特定的类型名称。

int getColumnDisplaySize(int column) :指示指定列的最大标准宽度,以字符为单位。

String getTableName(int column) :获取指定列的表名称

F.    PreparedStatement

PreparedStatement 接口继承 Statement接口

PreparedStatement比普通的Statement对象使用起来更加灵活,更有效率 。解决SQL注入(登陆千万不要用statement)。

使用PrepareStatement对象 Statement对象的区别

1.Statement 可以先行创建, 然后将sql语句写入.执行时传入sql

2. PreparedStatement 在创建时一定要传入 sql语句, 因为它要先运送到数据库执行预编译

3. PreparedStatement 在执行之前 先要设置 语句中的参数. (预处理的sql语句有占位?  执行前需要给?指定参数值,执行时可以直接执行不需要传入sql)

PrepareStatement 在执行之前已经 设置好了 sql语句 以及对应参数. 执行方法不需要参数

5.  具体操作

A.    连接数据库

 使用IDEA创建JAVA项目,jar包需要执行以下操作方可使用。

B.    使用statement执行新增修改 删除

try {

//1.加载驱动:本质是执行该类中得DriverManager.registerDriver(new Driver())——静态代码块

Class.forName("com.mysql.cj.jdbc.Driver");

/*//不同得数据库这个位置都得改

DriverManager.registerDriver(new Driver());*/

//2:获取连接:协议://什么数据库/ip地址:端口号(定服务)/

String url="jdbc:mysql://localhost:3307/schooldb?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true";

String dbusername="root";

String dbpwd="xxxxxxx";

Connection conn=DriverManager.getConnection(url,dbusername,dbpwd);


//书写sql

String sql="insert into classinfo(classname,begintime,endtime)values('QY111','2011-01-01','2011-01-01')";

//获取Statment对象并执行

Statement stm=conn.createStatement();

int yingxiang=stm.executeUpdate(sql);//executeUpdate:可以执行增删改语句

System.out.println(yingxiang);


conn.close();

stm.close();

} catch (Exception throwables) {

throwables.printStackTrace();

}

C.    使用statement执行查询

try {

//1.加载驱动:本质是执行该类中得DriverManager.registerDriver(new Driver())——静态代码块

Class.forName("com.mysql.cj.jdbc.Driver");

/*//不同得数据库这个位置都得改

DriverManager.registerDriver(new Driver());*/

//2:获取连接:协议://什么数据库/ip地址:端口号(定服务)/

String url = "jdbc:mysql://localhost:3307/schooldb?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true";

String dbusername = "root";

String dbpwd = "123456";

Connection conn = DriverManager.getConnection(url, dbusername, dbpwd);


//书写sql

String sql = "select classid,classname from classinfo";

//获取Statment对象并执行

Statement stm = conn.createStatement();

ResultSet rs = stm.executeQuery(sql);//executeQuery:执行查询

//处理结果

while(rs.next()){//有没有下一行,有指向

System.out.print(rs.getObject(1));//取当前行第一列得值

System.out.println(rs.getObject(2));

}


conn.close();

stm.close();

rs.close();

} catch (Exception throwables) {

throwables.printStackTrace();

}

E.    使用Statement进行登陆有可能发生sql注入

--Sql注入本质就是利用字符串拼接破坏sql

package com.jdbc02.login;


import javax.xml.transform.Result;

import java.sql.*;


/**

* @author KiwiFruit jitongke

* @create 2022-05-06 9:07

* @description

*/


public class User {

public static void main(String[] args) {

User user=new User();

boolean state=user.login("凄凄切切","111' or '1'='1");

System.out.println(state);

}

public boolean login(String username,String pwd) {

//连接数据库--去数据库中根据用户名 密码进行查询,如果数据库中:有用户名为admin 密码为111的信息,那么则登录成功否则登录失败

Connection conn=null;//为了变量可以在finally中使用——注意判空,因为初始值是null,如果try中有异常有可能无法赋值。

Statement stm=null;

ResultSet rs=null;

try{

String url = "jdbc:mysql://localhost:3307/schooldb?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true";

Class.forName("com.mysql.cj.jdbc.Driver");

conn= DriverManager.getConnection(url,"root","123456");


String sql="select * from users where username='"+username+"' and usepwd='"+pwd+"'";

stm=conn.createStatement();

rs=stm.executeQuery(sql);

if(rs.next()){

return true;

}else{

return false;

}

}catch (Exception e){

e.printStackTrace();

}finally {

//这个异常如何处理——有可能关闭失败——书写三个并列的try——是因为只有并列try才可以互不影响——各管各的

if(rs!=null) {

try {

rs.close();

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

if(stm!=null) {

try {

stm.close();

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

if(conn!=null) {

try {

conn.close();

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}


}

return false;

}

}

F.    PreparedStatement解决sql注入完成查询

package com.jdbc02.login;


import java.sql.*;


/**

* @author KiwiFruit jitongke

* @create 2022-05-06 9:07

* @description

*/


public class User02 {

public static void main(String[] args) {

User02 user=new User02();

boolean state=user.login("凄凄切切","111' or '1'='1");

System.out.println(state);

}

public boolean login(String username,String pwd) {

//连接数据库--去数据库中根据用户名 密码进行查询,如果数据库中:有用户名为admin 密码为111的信息,那么则登录成功否则登录失败

Connection conn=null;//为了变量可以在finally中使用——注意判空,因为初始值是null,如果try中有异常有可能无法赋值。

PreparedStatement stm=null;

ResultSet rs=null;

try{

String url = "jdbc:mysql://localhost:3307/schooldb?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true";

Class.forName("com.mysql.cj.jdbc.Driver");

conn= DriverManager.getConnection(url,"root","123456");

//避免使用字符串拼接——PreparedStatement:是statment的子接口,可以用字符串拼接——具备statment的功能

//——如何避免sql——还可以不使用字符串拼接——所有需要写活的位置上都是?,占位符,后期以特殊的方法给这个?赋值。 如果赋值的是字符串会自动加上''

//——?只能在值的位置上 不能出现在表明列名关键字的位置上

String sql="select * from users where username=? and usepwd=?";

stm=conn.prepareStatement(sql);//获取对象的时候里边有sql:sql提前就给了里边——预设sql

stm.setObject(1,username);

stm.setObject(2,pwd);


rs=stm.executeQuery();//最后执行的时候里边没有sql

if(rs.next()){

return true;

}else{

return false;

}

}catch (Exception e){

e.printStackTrace();

}finally {

//这个异常如何处理——有可能关闭失败——书写三个并列的try——是因为只有并列try才可以互不影响——各管各的

if(rs!=null) {

try {

rs.close();

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

if(stm!=null) {

try {

stm.close();

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

if(conn!=null) {

try {

conn.close();

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}


}

return false;

}

}

G.简单封装:

public class DbUtil {

static String url = "jdbc:mysql://localhost:3307/schooldb?useSSL=false&serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&allowPublicKeyRetrieval=true";

static String pwd="xxxxxx";

static String username="root";

static {//加载驱动一次就可以——static中执行一次

try {

Class.forName("com.mysql.cj.jdbc.Driver");

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

public Connection getConn() throws SQLException {

return DriverManager.getConnection(url,"root","123456");

}

public void close(Connection conn, Statement stm, ResultSet rs){

//这个异常如何处理——有可能关闭失败——书写三个并列的try——是因为只有并列try才可以互不影响——各管各的

if(rs!=null) {

try {

rs.close();

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

if(stm!=null) {

try {

stm.close();

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

if(conn!=null) {

try {

conn.close();

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

}

}

H.ResultSetMetaData:

public static void main(String[] args) {

Connection conn=null;

PreparedStatement pstm=null;

ResultSet rs=null;

DbUtil dbUtil=new DbUtil();

try{

//查询:学生学生列表

conn=dbUtil.getConn();

pstm=conn.prepareStatement("select * from studentinfo");

rs=pstm.executeQuery();


ResultSetMetaData metaData=rs.getMetaData();//结果集的结构—— 总共多少列 每一列的列名 每一列的数据类型等

int allCounts=metaData.getColumnCount();//总共多少列

//列出学生列表

while(rs.next()){//遍历行

//遍历列

for(int i=1;i<=allCounts;i++){

System.out.print(metaData.getColumnName(i)+":"+rs.getObject(i)+"\t");//getColumnName获取第i列的列名

}

System.out.println();


}



}catch (Exception e){

e.printStackTrace();

}finally {

dbUtil.close(conn,pstm,rs);

}

}

第二节:Dao封装

1.简单打开关闭的封装

2. 查询封装

public List<Map> executeQuery(String sql, Object[] args) throws SQLException {

Connection conn=null;

PreparedStatement pstm=null;//sql注入是破坏sql语句的 --- 依靠这种机制完成登录的破坏 也可能直接500——异常

ResultSet rs=null;

try{

conn=this.getConn();

pstm=conn.prepareStatement(sql);

//给?赋值

if(args!=null) {

for (int i = 1; i <= args.length; i++) {

pstm.setObject(i, args[i-1]);//?从1开始 数组下标从0开始

}

}

rs=pstm.executeQuery();

ResultSetMetaData metaData=rs.getMetaData();

int allCount=metaData.getColumnCount();

List<Map> list=new ArrayList<>();

while(rs.next()){//行遍历:判断是否有下一行,如果有就指向

Map map=new HashMap();

//遍历列:

for(int i=1;i<=allCount;i++){

map.put(metaData.getColumnName(i),rs.getObject(i));//列名作为key 列值作为值:

}

list.add(map);


}

return list;//rs关闭就不可以使用了。


}catch (Exception e){

e.printStackTrace();

throw e;

}finally {

this.close(conn,pstm,rs);

}

}
@Test

public void selectClass(){

String sql="select *from classinfo ";

DbUtil dbUtil=new DbUtil();

try {

List<Map> list=dbUtil.executeQuery(sql,null);//这个位置是需要结果列表的:需要你的结果集,所以工具需要将结果集返回了

for(Map map:list){

System.out.println(map);

}

} catch (Exception throwables) {

throwables.printStackTrace();

}


}

3. 增删改

//String sql="insert into studentinfo(studentname,gender)values(?,?)"; 2 == new Object[]{“zhagnsna”,"nan"}

//?个数传递的时候必须和数组长度一致 达到一一对应,否则jdbc一定会出错

public int executeUpdate(String sql,Object[] args) throws SQLException {

Connection conn=null;

PreparedStatement pstm=null;//sql注入是破坏sql语句的 --- 依靠这种机制完成登录的破坏 也可能直接500——异常

try{

conn=this.getConn();


pstm=conn.prepareStatement(sql);

//给?赋值

if(args!=null) {

for (int i = 1; i <= args.length; i++) {

pstm.setObject(i, args[i-1]);//?从1开始 数组下标从0开始

}

}

/* pstm.setObject(1,"张三");

pstm.setObject(2,"男");*/

return pstm.executeUpdate();//影响条数:

}catch (Exception e){

e.printStackTrace();

throw e;//告知调用者出异常了

}finally {

this.close(conn,pstm,null);

}

}
@Test

public void add(){

String sql="insert into studentinfo(studentname,gender)" +

"values(?,?)";

Object[] objs=new Object[]{"脏三","男"};

DbUtil dbUtil=new DbUtil();

try {

int i=dbUtil.executeUpdate(sql,objs);

System.out.println(i);

} catch (SQLException throwables) {

throwables.printStackTrace();

}


}

4. 新增返回主键

public int insertForKey(String sql,Object[] args) throws SQLException {

Connection conn=null;

PreparedStatement pstm=null;//sql注入是破坏sql语句的 --- 依靠这种机制完成登录的破坏 也可能直接500——异常

ResultSet rs=null;

try {

conn = this.getConn();


pstm=conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);//这个多的参数:本sql执行后将会返回主键

//给?赋值

if(args!=null) {

for (int i = 1; i <= args.length; i++) {

pstm.setObject(i, args[i-1]);//?从1开始 数组下标从0开始

}

}

int i=pstm.executeUpdate();//执行新增返回影响条数


//这个就是新增返回的主键的结果集:这个结果集中只有当前执行这个sql的主键列

//这个结果集只有一列:主键列:几行——新增几行是几个——目前不考虑多条新增,单条——if 如果多条while

rs=pstm.getGeneratedKeys();

if(rs.next()){

return rs.getInt(1);

}

return 0;//sql正常执行拿不到主键——有可能sql传递错了


}catch (Exception e){

e.printStackTrace();

throw e;

}finally {

this.close(conn,pstm,rs);

}

}
@Test

public void insertOrder(){

String sql="insert into tab_order(name,totalmoney)values(?,?)";

DbUtil dbUtil=new DbUtil();

try {

int orderid=dbUtil.insertForKey(sql,new Object[]{"xxxx",2000});

String sql2="insert into tab_orderdetail(orderid,proname,num,proprice)values(?,?,?,?)";

dbUtil.executeUpdate(sql2,new Object[]{orderid,"电脑",1,900});


sql2="insert into tab_orderdetail(orderid,proname,num,proprice)values(?,?,?,?)";

dbUtil.executeUpdate(sql2,new Object[]{orderid,"AAA",1,900});


sql2="insert into tab_orderdetail(orderid,proname,num,proprice)values(?,?,?,?)";

dbUtil.executeUpdate(sql2,new Object[]{orderid,"BBB",1,900});

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

5 .读取配置文件

java中的配置文件常为properties文件:——配置一些参数

A. 后缀为.properties;

B. 格式是“键=值”格式;

C. 使用“#”来注释

D. 让用户脱离程序本身修改相关的变量设置——使用配置文件

E. Java中提供了Properties类来读取配置文件

F. 注意:第一:前面有 “   / ”:“ / ”代表了工程的根目录,例如工程名叫做myproject,“ / ”代表了myproject 。 eg:me.class.getResourceAsStream("/com/x/file/myfile.xml");

第二:前面没有 “   / ”::代表当前类的目录。Eg:me.class.getResourceAsStream("myfile.xml");

资源配置文件:存储一些参数的

自动检测

# 资源配置文件,文本文件,不用进行编译,放到项目中直接就能用

db.username=root

db.password=xxxxxx

db.url=jdbc:mysql://localhost:3306/schooldb?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8

db.className=com.mysql.cj.jdbc.Driver

简单的读取方法:

static String url ;

static String pwd;

static String username;

static {//加载驱动一次就可以——static中执行一次

try {

//这个是相对路径:相对于DbUtil这个类的

InputStream is=DbUtil.class.getResourceAsStream("db.properties");

Properties properties=new Properties();

properties.load(is);

url=properties.get("db.url").toString();

pwd=properties.get("db.pwd").toString();

username=properties.get("db.username").toString();

Class.forName(properties.get("db.className").toString());

//Class.forName("com.mysql.cj.jdbc.Driver");

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

第三节: JDBC深入编程

1. 事务

事务概念:事务的特征:

一个事务可以是一条SQL语句,一组SQL语句或整个程序。 事务是恢复和 并发控制 的基本单位。

事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID特性

-- 需求:张三管李四借5000块钱

-- 一个事务由1或者多条sql组成的整体,要达到一个效果:要成功都成功要失败都失败

--  如果后续sql有异常直接回滚 都没有问题才真正在数据库中生效

-- 给张三的今天+5000  给李四的金额-5000

-- update 张三

-- 李四的没有成功

-- update 李四

-- 需求: 下单订单

-- 买了汉堡  鸡腿  pizza 总共100

-- insert 订单表

-- insert 订单详情表

-- insert 订单详情表

-- insert 订单详情表

-- 出错了:订单下了,但是钱没扣

-- 减钱

-- +积分

 默认情况下,连接Connection是处于自动提交模式。

 在自动提交模式下,每个SQL更新语句(insert,update,delete)成功执行完后就会自动提交到数据库中。

 为了将多个数据库更新组合成一组更新,我们需要将自动提交模式关闭(使用setAutoCommit(boolean auto)方法)。 默认true

 一旦关闭了自动提交模式,每个SQL语句都是一个事务的一部分,为使事务对数据库产生永久效果,需要使用 commit()方法来显式地进行提交。

 在自动提交关闭后,不成功的提交会导致数据库进行隐式的回滚,所有的更新都会丢失。

 也可以调用rollback();

public static void main(String[] args) throws SQLException {

DbUtil dbUtil=new DbUtil();//这个里边的增删改查的封装都是由问题的:都包含不了事务——他们执行一个sql后连接就已经关闭了。

Connection conn=null;

PreparedStatement pstm=null;

try{

conn=dbUtil.getConn();

conn.setAutoCommit(false);

String sql1="update tab_money set money=money-5000 where accountid=1";

pstm=conn.prepareStatement(sql1);

pstm.executeUpdate();

int i=1/0;

String sql2="update tab_money set money=money+5000 where accountid=2";

pstm=conn.prepareStatement(sql2);

pstm.executeUpdate();

conn.commit();

}catch (Exception e){

conn.rollback();

e.printStackTrace();

}finally {

dbUtil.close(conn,pstm,null);

}

}

2. 批处理

-- 批处理

-- 处理一次新增多条sql

-- 多条新增:insert into values(),(),()

-- insert into  select

-- 系统的时候导入:1000个学生,需要导入到学生表中

-- :insert into values(),(),(),(),(),(),()

用多条sql:太长了

循环入库:一次交互就需要推送一次 1000条和数据库交互效率太慢了

--批处理。

public static void main(String[] args) throws SQLException {

DbUtil dbUtil=new DbUtil();//这个里边的增删改查的封装都是由问题的:都包含不了事务——他们执行一个sql后连接就已经关闭了。

Connection conn=null;

PreparedStatement pstm=null;

try{

conn=dbUtil.getConn();

conn.setAutoCommit(false);

pstm=conn.prepareStatement("insert into classinfo(classname,begintime,endtime)values(?,?,?)");

for(int i=1;i<=1100;i++){

pstm.setObject(1,"新增"+i);

pstm.setObject(2,new Date());

pstm.setObject(3,new Date());

pstm.addBatch();//添加批次,将本次准备好的sql放入批次。

if(i%500==0){//每500个成一批,推送到数据库

pstm.executeBatch();//一次执行一批。

pstm.clearBatch();

}

}

// 如果总条数不是500的整数,后边会有一批不满。

pstm.executeBatch();

conn.commit();

}catch (Exception e){

e.printStackTrace();

conn.rollback();

}finally {

dbUtil.close(conn,pstm,null);

}

}

3. 日期处理

java.sql.Date类:java.util.Date的子类,一个包装了毫秒值的瘦包装器 (thin wrapper),它允许 JDBC 将毫秒值标识为 SQL DATE 值.

在向数据库中插入日期数据时,会出现java.util.Date与数据库日期格式不兼容的情况,此时就要用到java.sql.Date类和Timestamp,该类的格式为:yyyy-MM-dd

赋值:

//日期处理

//给日期赋值

pstm=conn.prepareStatement("insert into classinfo(classname,begintime,endtime)values(?,?,?)");

pstm.setObject(1,"aaa");

pstm.setObject(2,new Date());//util.Date

pstm.setObject(3,"2011-01-01");//日期字符串——

pstm.executeUpdate();


Date d1=new Date();

java.sql.Date sqldate=new java.sql.Date(d1.getTime());//sql.Date不能直接new 必须传入一个毫秒值才可以被new

java.sql.Timestamp sqldate1=new Timestamp(d1.getTime());//这个年月日时分秒 sqldate只有年月日

pstm.setObject(1,"bbb");

pstm.setObject(2,sqldate);//入库很麻烦:如果是一个字符串,字符串转util.date,util.date再换sql.date

pstm.setObject(3,sqldate1);//Timestamp:年月日时分秒——都没有字符串简单

pstm.executeUpdate();


LocalDate localDate=LocalDate.now();

LocalDateTime localDateTime=LocalDateTime.now();

pstm.setObject(1,"bbb");

pstm.setObject(2,localDate);//jdk1.8的日期也可以

pstm.setObject(3,localDateTime);

pstm.executeUpdate();

取值:

从数据库中查询:日期的数据类型——

sql.Date sql.timestamp

对于高版本的jdbc:LocalDate LocalDateTime

public static void main(String[] args) {

DbUtil dbUtil=new DbUtil();//这个里边的增删改查的封装都是由问题的:都包含不了事务——他们执行一个sql后连接就已经关闭了。

Connection conn=null;

PreparedStatement pstm=null;

ResultSet rs=null;

try {

conn = dbUtil.getConn();

String sql="select * from classinfo where classid>51162";

pstm=conn.prepareStatement(sql);

rs=pstm.executeQuery();

ResultSetMetaData metaData=rs.getMetaData();

while(rs.next()){

java.sql.Date d1=rs.getDate("BeginTime");

java.sql.Timestamp d2=rs.getTimestamp("EndTime");

System.out.println(d1);

System.out.println(d2);


System.out.println(metaData.getColumnClassName(3));

System.out.println(metaData.getColumnClassName(4));

}

}catch (Exception e){

e.printStackTrace();

}finally {

dbUtil.close(conn,pstm,rs);

}

}

4. Dao模式

这个是没有dao模式——仅仅只有一个工具的代码

public static void main(String[] args) {

//下订单

DbUtil dbUtil=new DbUtil();

String insertOrder="insert into tab_order(name,totalmoney)values(?,?)";

try {

int orderid=dbUtil.insertForKey(insertOrder,new Object[]{"zhagnsan",2000});

if(orderid>0) {

String orderDetail="insert into tab_orderdetail(orderid,proname,num,proprice)values(?,?,?,?)";

dbUtil.executeUpdate(orderDetail,new Object[]{orderid,"电脑",1,2000});


dbUtil.executeUpdate(orderDetail,new Object[]{orderid,"电脑2",2,2000});


dbUtil.executeUpdate(orderDetail,new Object[]{orderid,"电脑3",3,2000});


//添加积分记录


//减少余额

}

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

dao模式是一个分层理念:

工具就做工具的活

写sql的就只负责书写sql然后调用工具去执行就行

也逻辑找一个类专门负责业务逻辑——具体涉及到入库的找dao层

main方法——下指令直接给逻辑层下一个指令。

dao模式主要:将业务逻辑和sql分离

 使用DAO模式进行项目开发主要有以下两个好处:

  隔离了业务逻辑代码数据访问代码,分工明确,降低耦合性,提高可重用性。

  采用面向接口编程,提高了项目的可扩展性和可维护性。

 一个典型的DAO模式主要有以下几部分组成:

 1、DAO接口: 把对数据库的所有操作定义成抽象方法,可以提供多种实现。

public interface OrderDao {

int insertOrder(TabOrder order) throws SQLException;

List<Map> selectOrder() throws SQLException;

}
/**

* @author KiwiFruit jitongke

* @create 2022-05-10 11:03

* @description

*/

public interface OrderDetailDao {

//接收参数使用实体类 Map——后期如果参数调整:多一个少一个等等不用改变接口 方法声明直接少部分调换

int insertOrderDetail(TabOrderdetail orderdetail) throws SQLException;

}

 2、DAO 实现类: 针对不同数据库给出DAO接口定义方法的具体实现。

一般情况下一个表对应一个Dao的实现类,有关本表的增删改查都在本类中

/**

* dao层就是写sql的:执行sql的——一般情况下一张表的sql放到一个dao类中——一个sql一个方法

*/

public class OrderDaoImpl implements OrderDao{

DbUtil dbUtil=new DbUtil();


/**

* 新增订单

*/

public int insertOrder(TabOrder order) throws SQLException {

String insertOrder="insert into tab_order(name,totalmoney)values(?,?)";

int orderid=dbUtil.insertForKey(insertOrder,

new Object[]{order.getName(),order.getTotalmoney()});

return orderid;

}


/**

* 查询这个目前是没有用的:仅仅是为了体现一个类可以有很多放个:一个sql一个方法

*/

public List<Map> selectOrder() throws SQLException {

return dbUtil.executeQuery("select * from tab_order",null);

}

}
/**

* dao层负责写sql:

* 第一问题:sql不同数据库是有区别:——面向接口编程:一个表会对应一个dao层接口+一个实现类。如果要换数据库,可以重新在书写一个实现类。

*

* 第二个问题:参数需要别人给你——如果多了怎么办,如果频繁改动,如果是列表怎么办——封装实体 批量新增:封装列表

*/

public class OrderDetailDaoImpl implements OrderDetailDao {

DbUtil dbUtil=new DbUtil();

public int insertOrderDetail(TabOrderdetail orderdetail) throws SQLException {

String orderDetailSql="insert into tab_orderdetail(orderid,proname,num,proprice)values(?,?,?,?)";

int i = dbUtil.executeUpdate(orderDetailSql,

new Object[]{orderdetail.getOrderid(),orderdetail.getProname(),

orderdetail.getNum(),orderdetail.getProprice()

});

return i;

}

}

 3. 实体类:用于存放与传输对象数据。没有不影响

(回参:Map和List   Student List<Student>)

public class TabOrder {


private long orderid;

private String name;

private String totalmoney;



public long getOrderid() {

return orderid;

}


public void setOrderid(long orderid) {

this.orderid = orderid;

}



public String getName() {

return name;

}


public void setName(String name) {

this.name = name;

}



public String getTotalmoney() {

return totalmoney;

}


public void setTotalmoney(String totalmoney) {

this.totalmoney = totalmoney;

}


}

4.数据库连接和关闭工具类: 避免了数据库连接和关闭代码的重复使用,方便修改。

5.Main:逻辑代码的调用——Service业务逻辑层

//一个业务一个

public class OrderServiceImpl {

//main方法:

public static void main(String[] args) {

OrderDao orderDao=new OrderDaoImpl();

OrderDetailDao orderDetailDao=new OrderDetailDaoImpl();

int key= 0;

try {

TabOrder tabOrder=new TabOrder();

tabOrder.setName("zz");

tabOrder.setTotalmoney("3000");

key = orderDao.insertOrder(tabOrder);

TabOrderdetail orderdetail=new TabOrderdetail();

orderdetail.setOrderid(key);

orderdetail.setProname("dd");

orderdetail.setNum(1);

orderdetail.setProprice(200);

orderDetailDao.insertOrderDetail(orderdetail);

orderdetail=new TabOrderdetail();

orderdetail.setOrderid(key);

orderdetail.setProname("dd");

orderdetail.setNum(1);

orderdetail.setProprice(200);

orderDetailDao.insertOrderDetail(orderdetail);

orderdetail=new TabOrderdetail();

orderdetail.setOrderid(key);

orderdetail.setProname("dd");

orderdetail.setNum(1);

orderdetail.setProprice(200);

orderDetailDao.insertOrderDetail(orderdetail);

orderdetail=new TabOrderdetail();

orderdetail.setOrderid(key);

orderdetail.setProname("dd");

orderdetail.setNum(1);

orderdetail.setProprice(200);

orderDetailDao.insertOrderDetail(orderdetail);

} catch (SQLException throwables) {

throwables.printStackTrace();

}

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值