一、什么是JDBC
JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。也就是我们Java操作数据库的工具接口。
二、安装配置。
1.下载mysql5地址:
https://mvnrepository.com/
如图:
然后:
下载的jar包
在java工程中创建lib目录,将复制到工程的lib目录:
配置让我们的mysql的jar包生效:
认识jdbc的api中常用的接口java.sql. 和javax.sql.
ava.sql.Driver(驱动的接口),java.sql.DriverManager(驱动管理器)
java.sql.ResultSet(查询的结果放置的地方-结果集)
java.sql.Statement(用于操作数据库完成CRUD,有bug,有sql注入漏洞)
java.sql.PreparedStatement(用于操作数据库完成CRUD,推荐使用,防sql注入漏洞)
java.sql.Connection(完成数据库连接)
javax.sql.DataSource(连接池)
JDBC操作数据库的流程
下载和在工程中配置mysql驱动
package com.xja.test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* 连接数据库的测试类
* 1.需要加载驱动mysql5,这个驱动针对mysql5这个版本的驱动,mysql-connector-java-5.1.49.jar
* 2.下载驱动的方法 https://mvnrepository.com/
*/
public class TestJDBC1 {
public static void main(String[] args) {
//1.下载和在工程中配置mysql驱动
try {
//2.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//3.创建连接mysql的url,mytest为连接的数据库
String url = "jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true&characterEncoding=utf-8";
//连接数据库的账号
String user = "root";
//连接数据库的密码
String password = "root";
//4.连接数据库 java.sql.Connection
Connection conn = DriverManager.getConnection(url,user,password);
//5.测试连接是否成功
System.out.println(conn);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
如图:
完成数据库的查询
package com.xja.test;
import java.sql.*;
/**
* 连接数据库的测试类
* 1.需要加载驱动mysql5,这个驱动针对mysql5这个版本的驱动,mysql-connector-java-5.1.49.jar
* 2.下载驱动的方法 https://mvnrepository.com/
*/
public class TestJDBC1 {
public static void main(String[] args) {
//1.下载和在工程中配置mysql驱动
try {
//2.加载驱动
Class.forName("com.mysql.jdbc.Driver");
//3.创建连接mysql的url,mytest为连接的数据库
String url = "jdbc:mysql://127.0.0.1:3306/mytest?useUnicode=true&characterEncoding=utf-8";
//连接数据库的账号
String user = "root";
//连接数据库的密码
String password = "root";
//4.连接数据库 java.sql.Connection
Connection conn = DriverManager.getConnection(url,user,password);
//5.测试连接是否成功
//System.out.println(conn);
//6.创建操作的对象
Statement stmt = conn.createStatement();
//7.创建sql
String sql = "select * from dept";
//8.执行查询
ResultSet rs = stmt.executeQuery(sql);
System.out.println("编号\t名称\t地址\t创建时间");
while(rs.next()){
int deptNo = rs.getInt(1);
String dname = rs.getString(2);
String loc = rs.getString(3);
java.sql.Timestamp timestamp = rs.getTimestamp(4);
System.out.println(deptNo + "\t" + dname + "\t" + loc + "\t" + timestamp);
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
}
完成数据库的插入
主键不是自动增长:
String sql = "insert into dept values(60,'computer','cq',now())";
int count = stmt.executeUpdate(sql);//count
System.out.println("插入了" + count + "行");
主键是自动增长:
创建sql 使用null 主键必须为自动增长
String sql = "insert into teacher values(null,'王老师')";
//8.执行修改 插入 删除 都是一个方法 executeUpdate() jdbc也采用了默认就提交了
int count = stmt.executeUpdate(sql);//count 为修改 插入 删除影响的行数
System.out.println("插入了" + count + "行");
完成数据库的修改
String sql = "update dept set loc = 'chongqing' where deptno = 60";
int count = stmt.executeUpdate(sql);
System.out.println("修改了" + count + "行");
完成数据库的删除
String sql = "delete from dept where deptno = 60";
int count = stmt.executeUpdate(sql);
System.out.println("删除了" + count + "行");
Statement的子接口
Statement 不能防sql注入漏洞
PreparedStatement 可以预编译sql语句,效率高,防sql注入漏洞,传递参数使用占位符?,使用很方便
CallableStatement 用途为调用存储过程(不在使用,了解即可)
使用PreparedStatement 完成查询
//创建sql
String sql = "select * from dept where deptno = ? and dname = ?";//从左边到右边 顺序为第一个?为1
//6.创建操作的对象
stmt = conn.prepareStatement(sql);
//给占位符赋值
stmt.setInt(1,50);
stmt.setString(2,"PROGRAMING");
rs = stmt.executeQuery();//不要在传递sql语句作为参数
连接池:
对比租赁汽车:
实现连接池的步骤一,添加jar包,配置jar包
在src目录下创建dbcp.properties文件,用于配置dbcp参数,代码如下:
创建连接工具类ConnectionUtil:
工具类的代码:
package com.xja.test.util;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class ConnectionUtil {
// 数据源对象(连接池)
private static DataSource ds;
//定义连接对象
private static Connection conn;
// 私有构造方法。外部不能new
private ConnectionUtil () {
}
static {
//加载数据源ds对象
// String path = System.getProperty("user.dir");
// path = path + "\\src\\dbcp.properties";
// InputStream inputStream = new FileInputStream(path);
InputStream is = ConnectionUtil.class.getResourceAsStream("/dbcp.properties");
Properties properties = new Properties();
try {
properties.load(is);
ds = BasicDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection()
throws SQLException {
if (conn == null || conn.isClosed()) {
// 创建数据库连接(从连接池中获取连接)
conn = ds.getConnection();
}
return conn;
}
public static void closeConnection() {
try {
if (conn != null && !conn.isClosed()) {
conn.close();//在使用DBCP时,释放连接对象。
}
} catch (SQLException e) {
e.printStackTrace();
}
}
//测试连接
public static void main(String[] args) throws SQLException {
System.out.println(getConnection());
}
}
创建案例用的表格
账户表:
操作记录表:
添加了账户的测试数据:
系统分层
系统分层的好处为:利于开发、利于维护、利于调试、利于开发效率、利于分工
分层后的开发流程:
系统分层(就是分包)的实现步骤:
8.1使用orm的原理,应该为数据库的表格创建映射的实体类Account账户类和AccountRecord账户记录类,创建com.xja.domain包,创建Account账户类和AccountRecord账户记录类:
创建dao层(注意:dao层如果有异常,一律不处理直接使用throws抛出异常,异常在业务层service处理),创建com.xja.dao包,创建AccountDao接口和在com.xja.dao.impl包AccountDaoImpl实现类:
创建service业务层,创建包com.xja.service,创建AccountService接口,com.xja.service.impl包创建AccountServiceImpl实现类
创建View视图层的界面创建包com.xja.view创建类AccountListView
根据银行账号查询账户信息(正确开发的流程:开发dao层-测试,开发service层-测试,开发控制器(还没有学习)-测试,开发视图层-测试,这样开发很容易找到问题所在的层,提高开发效率)
第一步开发dao 层:
第二步开发service 层:
第三步开发view层:
测试结果:
根据银行账号和金额实现存钱业务:
第一步:创建一个自定义的异常com.xja.exception.AccountDepositException
第二步:开发dao层
第三步:开发service层
1.需要处理系统异常SQLException
2.需要抛出自定义异常AccountDepositException
3.开启事物处理
第四步:开发view层
补充业务层的操作:
改进一下,将账户处理的自定义异常,统统的叫AccountException,将AccountDepositException删除:
根据银行账号和金额实现取钱业务:
第一步: 开发dao层
第二步: 开发service层:
AccountServiceImpl实现类的代码:
package com.xja.service.impl;
import com.xja.dao.AccountDao;
import com.xja.dao.AccountRecordDao;
import com.xja.dao.impl.AccountDaoImpl;
import com.xja.dao.impl.AccountRecordDaoImpl;
import com.xja.domain.Account;
import com.xja.domain.AccountRecord;
import com.xja.exception.AccountException;
import com.xja.service.AccountService;
import com.xja.util.ConnUtil;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* 账户的业务层的实现类,实现业务逻辑和处理异常和事物管理
*/
public class AccountServiceImpl implements AccountService {
//业务层调用数据访问层
private AccountDao accountDao = new AccountDaoImpl();
private AccountRecordDao accountRecordDao = new AccountRecordDaoImpl();
/**
* 查询所有的账户信息
* @return
*/
@Override
public List<Account> getAllAccount() {
List<Account> accountList = null;
try {
accountList = accountDao.getAllAccount(); //业务层需要处理异常,不要直接抛出异常
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//关闭数据库连接,释放连接
ConnUtil.closeConnection();
}
return accountList;
}
/**
* 根据银行账号查询账户信息
* @param accNo
* @return
*/
@Override
public Account selectAccountByAccNo(String accNo) {
Account account = null;
try {
account = accountDao.selectAccountByAccNo(accNo);
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally {
//关闭数据库连接,释放连接
ConnUtil.closeConnection();
}
return account;
}
/**
* 根据账户信息和金额实现存钱
* 1.需要处理系统异常SQLException
* 2.需要抛出自定义异常AccountDepositException
* 3.开启事物处理(增加,修改,删除都需要事物处理)
* 4.关闭数据库,释放连接
* @param acc
* @param amount
* @throws SQLException
*/
@Override
public void updateAccountBalance(Account acc, double amount) throws AccountException {
try {
//同学们完善,实现要查询存钱钱的账号是否存在
//3.开启事物处理
//因为jdbc默认提交,所以出现了异常,没有办法回滚,将jdbc的默认提交设置为默认不提交,没有异常才提交,有异常就回滚
ConnUtil.getConnection().setAutoCommit(false);//交设置为默认不提交
accountDao.updateAccountBalance(acc,amount);//执行操作
//需要同学们完善,要向记录表插入存钱的记录日志数据
ConnUtil.getConnection().commit();//没有异常才提交,该代码一定要写到所有操作的最后
} catch (SQLException throwables) {////1.需要处理系统异常SQLException
try {
ConnUtil.getConnection().rollback();//有异常就回滚
} catch (SQLException e) {
e.printStackTrace();
}
//2.需要抛出自定义异常AccountDepositException
throw new AccountException("账户存钱发生错误,存钱失败");
}finally {
//4.关闭数据库连接,释放连接
ConnUtil.closeConnection();
}
}
/**
* 根据账户信息和金额实现取钱
* 1.需要处理系统异常SQLException
* 2.需要抛出自定义异常AccountDepositException
* 3.开启事物处理(增加,修改,删除都需要事物处理)
* 4.关闭数据库,释放连接
* @param acc
* @param amount
* @throws SQLException
*/
@Override
public void withdrawal(Account acc, double amount) throws AccountException {
try {
//实现要查询取钱的账号是否存在和余额不足
Account account = accountDao.selectAccountByAccNo(acc.getAccNo());
if(Objects.isNull(account)){//没有查询到账户信息
throw new AccountException("账户不存在");
}else{//有查询到账户信息
if(account.getAccBalance() < amount){//余额金额不足
throw new AccountException("余额金额不足");
}else{
//3.开启事物处理
//因为jdbc默认提交,所以出现了异常,没有办法回滚,将jdbc的默认提交设置为默认不提交,没有异常才提交,有异常就回滚
ConnUtil.getConnection().setAutoCommit(false);//交设置为默认不提交
//执行取钱操作
accountDao.withdrawal(acc,amount);
//同时要完成向账户记录表中插入当前操作的记录数据
AccountRecord accountRecord = new AccountRecord();
accountRecord.setReAcc(account);
accountRecord.setReAction("取钱");
accountRecord.setReAccTo(account);
accountRecord.setReAmt(amount);
//插入账户记录表
accountRecordDao.inertAccRecord(accountRecord);
ConnUtil.getConnection().commit();//没有异常才提交,该代码一定要写到所有操作的最后
}
}
} catch (SQLException throwables) {////1.需要处理系统异常SQLException
try {
ConnUtil.getConnection().rollback();//有异常就回滚
} catch (SQLException e) {
e.printStackTrace();
}
//2.需要抛出自定义异常AccountDepositException
throw new AccountException("账户取钱发生错误,取钱失败");
}finally {
//4.关闭数据库连接,释放连接
ConnUtil.closeConnection();
}
}
}
第三步 :创建AccountRecord的dao层,名字叫AccountRecordDao和实现类AccountRecordDaoImpl:
第四步 :创建view层
测试结果需要三个结果同时都有:
看到金额较少:
日志表有数据:
根据开户业务(知识点:在插入数据时返回自动增加的主键的值,拿到account表中acc_id)这个需要jdbc第二章的知识点才能完成该功能:
业务需求:在插入account表时(开户),自动增加的主键acc_id的值要返回,因为该值要插入到我们的acc_record表(开户日志)中的re_acc_id字段中
dao层:
第二步:开发service层
业务层的实现类:
/**
* 开户
* @param acc
* @throws AccountException
*/
@Override
public void createNewAccount(Account acc) throws AccountException {
String accNo = AccountNoUtil.getAccountNo();
try {
Account account = accountDao.selectAccountByAccNo(accNo);
if(Objects.isNull(account)){//账号不重复
//3.开启事物处理
//因为jdbc默认提交,所以出现了异常,没有办法回滚,将jdbc的默认提交设置为默认不提交,没有异常才提交,有异常就回滚
ConnUtil.getConnection().setAutoCommit(false);//交设置为默认不提交
//执行开户操作
acc.setAccNo(accNo);
int accId = accountDao.createNewAccount(acc);
if(accId != -1){
//同时要完成向账户记录表中插入当前操作的记录数据
AccountRecord accountRecord = new AccountRecord();
Account account1 = new Account();
account1.setAccId(accId);
accountRecord.setReAcc(account1);
accountRecord.setReAction("开户");
accountRecord.setReAccTo(account1);
accountRecord.setReAmt(acc.getAccBalance());
//插入账户记录表
accountRecordDao.inertAccRecord(accountRecord);
ConnUtil.getConnection().commit();//没有异常才提交,该代码一定要写到所有操作的最后
}else{
throw new AccountException("主键生成失败");
}
}else{
//重新生成账号
}
} catch (SQLException throwables) {
try {
ConnUtil.getConnection().rollback();//有异常就回滚
} catch (SQLException e) {
e.printStackTrace();
}
//2.需要抛出自定义异常AccountDepositException
throw new AccountException("账户取钱发生错误,取钱失败");
}finally {
//4.关闭数据库连接,释放连接
ConnUtil.closeConnection();
}
}
第三步:开发view层
三、总结
祝大家早日上岸!