Java 数据库程序设计(JDBC)
补:数据库的基本操作
JDBC的基本概念
全称: Java Data Base Connectivity
组成:
由一组用Java语言编写的类和接口组成,不同的数据库都会实现这种接口。
作用:
为多种关系数据库提供同一的接口
优点:
对于不同厂商的数据库,JDBC驱动包都相同。
一次编写,处处运行
JAR包:
用JAVA语言实现与数据库的交互,是直接访问数据库,执行效率高
编程步骤:
- 加载数据库驱动:告知JVM使用的是哪一个数据库的驱动
- 连接数据库。
- 创建SQL语句的执行者对象。
- 执行sql语句:使用执行者对象,向数据库执行SQL语句 获取到数据库的执行后的结果
- 处理结果。
- 关闭SQL语句执行对象 。
- 关闭连接。
- 程序结束。
常用API
Driver接口
概念:
Driver接口由数据库厂家提供。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。
用到的实现类:JAVA.lang.Class
方法:
装载驱动:Class.forName(String className);
返回与 给定字符串名的类或接口 相关联的Class对象。 ---加载JDBC驱动时使用
装载MySql驱动:Class.forName("com.mysql.jdbc.Driver");
Connection接口
作用:
与特定数据库的连接,和创建向数据库发送sql的statement对象。
用到的实现类:JAVA.sql.DruverMannager
连接数据库方法:
Connction getConnection (String url ,String user, String password)
---获取url对应的数据库的链接
连接MySql数据库:
Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
创建statement对象方法:
createStatement():创建向数据库发送sql的statement对象。
prepareStatement(String sql) :创建向数据库发送预编译sql的PrepareSatement对象。
Statement接口
作用:
用于执行SQL语句并返回它所生成结果的对象。
用到的实现类:
Statement:
由createStatement创建,用于发送简单的SQL语句(不带参数)。
PreparedStatement :
继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,一般都使用PreparedStatement。
常用Statement方法:
executeQuery(String sql):运行select语句,返回ResultSet结果集。
executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
ResultSet接口
作用:
提供检索不同类型字段的方法。
用到的实现类:ResultSet
获取信息的方法
getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。
getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。
getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据。
getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。
对结果集进行滚动的方法:
next():移动到下一行
Previous():移动到前一行
absolute(int row):移动到指定行
beforeFirst():移动resultSet的最前面。
afterLast() :移动到resultSet的最后面。
代码实现
编程步骤
-
加载数据库驱动:
Class.forName("...")
告知JVM使用的是哪一个数据库的驱动 -
连接数据库:
DriverManager.getConnection(url,user,password)
->con -
创建SQL语句的执行者对象:
con.createStatement()
或者con.PreparedStatement()
->st -
执行sql语句:
st.executeUpdate("...")
或者st.executeQuery("...")
使用执行者对象,向数据库执行SQL语句 获取到数据库的执行后的结果 -
处理结果
-
关闭SQL语句执行对象 :
st.close;
-
关闭连接:
con.close();
-
程序结束
基础JDBC
固定语句的JDBC管理
准备
新建JAVA project
新建lib文件夹
粘贴mysql-connector-java-5.1.18-bin.jar到lib文件夹下
在.jar下右键,选择如下
连接数据库+插入
package cn.sdut.text;
import static java.sql.DriverManager.getConnection;
import java.sql.Connection;
import java.sql.SQLException;
import com.mysql.jdbc.Statement;
public class JDBCText {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// TODO Auto-generated method stub
//加载JDBC驱动
Class.forName("com.mysql.jdbc.Driver");
//获取数据库的链接
Connection con = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/book","root","usbw");
//准备SQL语句执行对象
Statement st=(Statement) con.createStatement();
// 准备SQL语句
String sql= "insert into student values(null,'JOHN',22)";
//执行SQL语句
int result= st.executeUpdate(sql);
System.out.println(result>0?"成功":"失败");
//关闭SQL语句执行对象
st.close();
//关闭数据库链接
con.close();
}
}
遍历输出
//准备SQL语句
String selectSql="select *from student";
//执行SQL语句
ResultSet rs = st.executeQuery(selectSql);//和增删改不一样
//如果指针还有元素则输出
while(rs.next()) {
int stuId=rs.getInt(1);
String stuName=rs.getString("name");
String stuIn=rs.getString(3);
int StuScore=rs.getInt(4);
System.out.println(stuId+" "+stuName+" "+stuIn+" "+StuScore);
}
单条查询:
String selectSql1 = "select *from student where id=3";
ResultSet rs2 = st.executeQuery(selectSql1);//和增删改不一样
System.out.println("id为3的数据为:");
if (rs2.next()) {
int stuId = rs2.getInt(1);
String stuName = rs2.getString("name");
String stuIn = rs2.getString(3);
int StuScore = rs2.getInt(4);
System.out.println(stuId + " " + stuName + " " + stuIn + " " + StuScore);
} else {
System.out.println("没有查询到id=3的数据");
}
删除
// 删除id为1的数据
String deleteSql1 = "delete from student where id=1";
int deleteResult1 = st.executeUpdate(deleteSql1);
System.out.println(deleteResult1 > 0 ? "id为1的数据删除成功" : "id为1的数据删除失败(可能是因为id为1的数据不存在)");
更新数据
// 修改id=2的数据
String updataSql1 = "update student set name='山东理工' where id=2";
int updataResult1 = st.executeUpdate(updataSql1);
System.out.println(updataResult1 > 0 ? "修改id为2的数据成功" : "修改id为2的数据失败(可能是因为id为2的数据不存在)");
输入变量JDBC
方法1:
更改准备SQL语句阶段
// 准备SQL语句
String name="里斯";
int age=20;
String sql= "insert into student values(null,'"+name+"',"+age+")";
但是比较复杂。
方法2:
PreparedStatement代替Statement
优点:
- 速度快
- 可以防止SQL注入
package cn.sdut.text;
import static java.sql.DriverManager.getConnection;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.PreparedStatement;
import com.mysql.jdbc.Statement;
public class JDBCText {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// TODO Auto-generated method stub
//加载JDBC驱动
Class.forName("com.mysql.jdbc.Driver");
//获取数据库的链接
Connection con = getConnection("jdbc:mysql://localhost:3306/book","root","usbw");
//准备SQL语句执行对象
java.sql.PreparedStatement pst=null;
// 准备SQL语句
String name="里斯22";
int age=20;
String sq14="insert into student values(null,?,?)";
pst=(PreparedStatement) con.prepareStatement(sq14);
pst.setString(1,name);
pst.setInt(2, age);
String sql="select * from student";
String sql3="select * from student where id=?";
//执行SQL语句
int result= pst.executeUpdate();
System.out.println(result>0?"成功":"失败");
//遍历输出
pst=con.prepareStatement(sql);
ResultSet rs=pst.executeQuery();
while(rs.next()) {
int id1=rs.getInt(1);
String name1=rs.getString("name");
int age1=rs.getInt(3);
System.out.println(id1+" "+name1+" "+age1);
}
System.out.println("-----------------------");
pst=con.prepareStatement(sql3);
pst.setInt(1, 4);
ResultSet rs1 = pst.executeQuery();
if(rs1.next()) {
int id1=rs1.getInt(1);
String name1=rs1.getString("name");
int age1=rs1.getInt(3);
System.out.println(id1+" "+name1+" "+age1);
}
else System.out.println("无此人");
rs.close();
rs1.close();
//关闭SQL语句执行对象
pst.close();
//关闭数据库链接
con.close();
}
}
面向对象JDBC
//数据类
package Work;
public class Student {
private int id=-1;
private String name=null;
private String date=null;
private int score=-1;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public Student(int id, String name, String date, int score) {
super();
this.id = id;
this.name = name;
this.date = date;
this.score = score;
}
public Student() {
super();
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", date=" + date + ", score=" + score + "]";
}
}
//面向对象JDBC类
package Work;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.mysql.jdbc.Connection;
import com.mysql.jdbc.PreparedStatement;
public class ClassJdbc {
Connection con;
PreparedStatement pst;
ResultSet rs;
// 获取数据库连接
public Connection getConnection() throws Exception {
// 加载jdbc驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取数据库的连接
Connection con = (Connection) DriverManager.getConnection(
"jdbc:mysql://localhost:3306/book?useunicode=true&characterEncoding=utf-8", "root", "usbw");
return con;
}
public int addStu(Student stu) throws Exception {
con = getConnection();
String sql = "insert into student values (null,?,?,?)";
pst = (PreparedStatement) con.prepareStatement(sql);
pst.setString(1, stu.getName());
pst.setString(2, stu.getDate());
pst.setInt(3, stu.getScore());
int result = pst.executeUpdate();
End();
return result;
}
// 删除对象
public int deleteStu(int id) throws Exception {
con = getConnection();
String sql = "delete from student where id=?";
pst = (PreparedStatement) con.prepareStatement(sql);
pst.setInt(1, id);
int result = pst.executeUpdate();
End();
return result;
}
// 更新数据
public int updateStu(Student stu) throws Exception {
String sql = "update student set name=?,schoolin=?,score=? where id=?";
//先查询到待更新的数据,(因为每次不一定要更新全部数据)
Student data = getStu(stu.getId());
//获取数据库连接
con = getConnection();
pst = (PreparedStatement) con.prepareStatement(sql);
//如果更新的数据某项为空,则设置原来的数据。(初始化默认为空)
if (stu.getName() != null) {
pst.setString(1, stu.getName());
}
else {
pst.setString(1, data.getName());
}
//如果更新的数据某项为空,则设置原来的数据。(初始化默认为空)
if (stu.getDate() != null) {
pst.setString(2, stu.getDate());
} else {
pst.setString(2, data.getDate());
}
//如果更新的数据某项为空,则设置原来的数据。(初始化默认为-1)
if (stu.getScore() != -1) {
pst.setInt(3, stu.getScore());
} else {
pst.setInt(3, data.getScore());
}
//因为更新数据id一定存在,所以不需要判断
pst.setInt(4, stu.getId());
int result = pst.executeUpdate();
End();
return result;
}
// 遍历输出数据
public List<Student> getStuAll() throws Exception {
con = getConnection();
//利用List储存查询到的数据
List<Student> stuAll = new ArrayList<Student>();
String sql = "select *from student";
pst = (PreparedStatement) con.prepareStatement(sql);
rs = pst.executeQuery();
//如果指针不为空,则代表有元素,继续向下处理数据
while (rs.next()) {
//处理数据
Student stu = new Student();
stu.setId(rs.getInt(1));
stu.setName(rs.getString(2));
stu.setDate(rs.getString(3));
stu.setScore(rs.getInt(4));
//将数据存入List
stuAll.add(stu);
}
End();
return stuAll;
}
// 依据id,查询数据
public Student getStu(int id) throws Exception {
con = getConnection();
Student stu = new Student();
String sql = "select *from student where id=?";
pst = (PreparedStatement) con.prepareStatement(sql);
pst.setInt(1, id);
rs = pst.executeQuery();
if (rs.next()) {
stu.setId(rs.getInt(1));
stu.setName(rs.getString(2));
stu.setDate(rs.getString(3));
stu.setScore(rs.getInt(4));
} else
stu.setId(-1);//如果id为-1,代表数据未查询到
End();
return stu;
}
// 关闭对象,收尾。
public void End() throws SQLException {
if (rs != null)
rs.close();
if (pst != null)
pst.close();
if (con != null)
con.close();
}
public static void main(String[] args) throws Exception {
ClassJdbc jdbc = new ClassJdbc();
// 准备插入数据
Student addStu1 = new Student(4, "李三", "2017-09-01", 96);
Student addStu2 = new Student(5, "王四", "2018-09-01", 97);
Student addStu3 = new Student(6, "长武", "2019-09-01", 98);
// 插入默认数据
int result1 = jdbc.addStu(addStu1);
System.out.println(result1 > 0 ? "语句1插入成功" : "语句1插入失败");
int result2 = jdbc.addStu(addStu2);
System.out.println(result2 > 0 ? "语句1插入成功" : "语句1插入失败");
int result3 = jdbc.addStu(addStu3);
System.out.println(result3 > 0 ? "语句1插入成功" : "语句1插入失败");
// 删除id为1的数据
int deleteResult1 = jdbc.deleteStu(1);
System.out.println(deleteResult1 > 0 ? "id为1的数据删除成功" : "id为1的数据删除失败(可能是因为id为1的数据不存在)");
// 显示所有记录
List<Student> stuAll = jdbc.getStuAll();
if (stuAll != null) {
System.out.println("此时数据库的全部数据为");
for (Student t : stuAll) {
System.out.println(t);
}
} else {
System.out.println("此时数据库没有数据");
}
// 修改id=2的数据
Student updataStu = new Student();
updataStu.setName("山东理工");
updataStu.setId(2);
int updataResult1 = jdbc.updateStu(updataStu);
System.out.println(updataResult1 > 0 ? "修改id为2的数据成功" : "修改id为2的数据失败(可能是因为id为2的数据不存在)");
// 查询表中id=3的记录并显示
Student selectStu = jdbc.getStu(3);
if (selectStu.getId() != -1) {
System.out.println("id为3的数据为:" + selectStu);
} else {
System.out.println("没有查询到id=3的数据");
}
}
}
面向对象JDBC升级改造
将con封装成父类
package cn.sdut.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.mysql.jdbc.PreparedStatement;
public class BaseDao {
protected Connection con;
protected PreparedStatement pst;
protected ResultSet rs;
//获得数据库的链接
public Connection getConnection() throws SQLException, ClassNotFoundException {
// 加载JDBC驱动
Class.forName("com.mysql.jdbc.Driver");
// 获取数据库的链接
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/book", "root", "usbw");
return con;
}
//关闭对象
public void closeAll() throws SQLException {
if(rs!=null) {
rs.close();
}
if(pst!=null) {
pst.close();
}
if(con!=null) {
con.close();
}
}
}
面向接口编程是把客户的业务功能提取出来,作为接口。业务的具体实现通过该接口的实现类来完成。当客户需求变化时,只需编写该业务逻辑的新的实现类,或者通过更改配置文件中该接口的实现类就可以完成需求,不需要改写现有代码,减少对系统的影响。采用面向接口编程的好处包括:业务逻辑清晰、代码易懂、降低了程序的耦合性、方便扩展、可维护性强。
子类记得继承
public class StudentDao extends BaseDao{
将方法封装成接口
package cn.sdut.dao;
import java.sql.SQLException;
import java.util.List;
import cn.sdut.po.*;
public interface StudentDaoInterface {
public int addStudent (Student student) throws ClassNotFoundException, SQLException ;
public int delStudent (int id) throws ClassNotFoundException, SQLException;
public int updataStudent(Student student) throws SQLException, ClassNotFoundException;
public List<Student> getStudent () throws SQLException, ClassNotFoundException;
public Student getStudentById(int id) throws SQLException, ClassNotFoundException;
}
注意引入实现方法
public class StudentDao extends BaseDao implements StudentDaoInterface{
三层架构
访问层/界面层(用户与程序的交互)
^
||
业务逻辑层(判断数据的合法性)
^
||
数据库访问层(访问数据库)
界面层
package cn.sdut.aview;
import java.util.Scanner;
import java.util.List;
import cn.sdut.base.BookBiz;
import cn.sdut.po.Book;
public class Main {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
String name =sc.next();
double price=sc.nextDouble();
int count=sc.nextInt();
BookBiz biz=new BookBiz();
Book addbook=new Book(1,name,price,count);//id为 系统自动赋值,故随意输入
try {
int reulst=biz.addBook(addbook);
List<Book> booklist=biz.selectAll();
for(Book b:booklist) {
System.out.println(b);
}
}
catch (Exception e) {
// TODO: handle exception
//e.printStackTrace();
System.out.println("dadsdsdsa");
}
sc.close();
}
}
业务逻辑层
package cn.sdut.base;
import java.sql.SQLException;
import java.util.List;
import cn.sdut.dao.BookDao;
import cn.sdut.po.Book;
public class BookBiz {
BookDao bookDao = new BookDao();
public int addBook(Book book) throws SQLException {
int result = 0;
if (book.getPrice() > 0 && book.getCount() > 0) {
result = bookDao.addBook(book);
}
return result;
}
//和用户无关,故只需过度一下
public List<Book> selectAll() throws SQLException {
return bookDao.selectAll();
}
}
数据访问层
上面JDBC便是。
事务
定义:
指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。
特性
- 原子性(atomicity) 一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
- 一致性(consistency) 事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
- 隔离性(isolation) 一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(durability) 持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
实现方法
在JDBC中,通过Connection对象进行事务管理,默认是自动提交事务。
如果人工设计事务需要:
-
手工将自动提交关闭(con.setAutoCommit(false); ),
-
通过commit方法进行提交,
-
rollback方法进行回滚,如果不提交,则数据不会真正的插入到数据库中。
public boolean transactionTset (Student student ,int id) throws ClassNotFoundException, SQLException { con=getConnection(); con.setAutoCommit(false); //设置SQL的非自动提交,并开启事务 //不会自动提交,而是进入缓冲区 //增加一个学生 String sql1= "insert into student values(null,?,?)"; pst=(PreparedStatement) con.prepareStatement(sql1); pst.setString(1, student.getName()) ; pst.setInt(2, student.getAge()); int resutl1=pst.executeUpdate(); //删除一个学生 String sql="updade student set name?,age=?,where id=?"; pst=(PreparedStatement) con.prepareStatement(sql); pst.setString(1, student.getName()); pst.setInt(2, student.getAge()); pst.setInt(3, student.getId()); int result2=pst.executeUpdate(); int result=resutl1*result2; if(result==1) { con.commit();//提交事务 return true; } else { con.rollback();//回滚事务 return false; } }
```java
public void testtransactionTset() throws ClassNotFoundException, SQLException {
Student stu=new Student();
stu.setName("fs");
stu.setAge(1);
stu.setId(11);
int id=11;
boolean result=dao.transactionTset (stu ,id);
assertEquals(result, true);
//断言他是对的
}