JDBC(上)
第1关:JDBC连接数据库
任务描述
本关任务:使用jdbc连接数据库并完成创建数据库和创建表的操作。
相关知识
JDBC API提供以下接口和类:
DriverManager:此类管理数据库驱动程序列表。可在JDBC下识别某个子协议的第一个驱动程序,用于建立数据库连接。
Driver:此接口处理与数据库服务器的通信。我们很少会直接与Driver对象进行交互。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序。
Connection:此接口具有用于联系数据库的所有方法。 Connection对象表示通信上下文,即与数据库的所有通信仅通过连接对象。
Statement:用于执行静态SQL语句并返回它所生成结果的对象。一些派生接口还可接受参数,如PrepareStatement。
ResultSet:提供检索不同类型字段的方法。(操作对象为Statement执行SQL查询后的结果)
SQLException:此类处理数据库应用程序中发生的任何错误。
使用JDBC的步骤如下:
加载数据库驱动 → 建立数据库连接(Connection) → 创建执行SQL语句的Statement对象 → 处理执行结果(ResultSet) → 释放资源
为了完成本关任务,你需要掌握:1.如何加载数据库驱动;2.如何建立数据库连接;3.如何执行编写的SQL语句;4.释放资源。
加载数据库驱动
驱动加载是为了打开与数据库的通信通道。
在注册驱动前我们需要装载特定厂商的数据库驱动程序,导入mysq-connector-java的jar包,方法是在项目中建立lib目录,在其下放入jar包。
然后右键jar包 Build Path→Add to Build Path完成jar包导入。将jar包导入项目之后我们就开始注册驱动:
Java加载数据库驱动通常是使用Class类的静态方法forName(),语法格式如下:
Class.forName(String driverManager)
示例:
try {
Class.forName("com.mysql.jdbc.Driver" );
} catch (ClassNotFoundException e) {
e.printStackT\frace();
}
如果加载成功,会将加载的驱动类注册给DriverManager;加载失败,会抛出ClassNotFoundException异常。
建立连接
成功加载完数据库驱动后,就可以建立数据库的连接了,使用DriverManager的静态方法getConnection()来实现。如下:
Connection conn = DriverManager.getConnection(url, user, password);
URL用于标识数据库的位置,通过URL地址告诉JDBC程序连接信息。
若不存在数据库,只建立连接,URL的写法为:
若存在数据库test,URL的写法为:
其中localhost可以换成IP地址127.0.0.1,3306为MySQL数据库的默认端口号,user和password对应数据库的用户名和密码。
执行编写的SQL语句
连接建立完毕后,就可以使用Connection接口的createStatement()方法来获取Statement对象;并通过executeUpdate()方法来执行SQL语句。
创建statement对象
try {
Statement statement = conn.createStatement();
} catch (SQLException e) {
e.printStackT\frace();
}
创建数据库
try {
String sql1="drop database if exists test";
String sql2="create database test";
statement.executeUpdate(sql1);//执行sql语句
statement.executeUpdate(sql2);
} catch (SQLException e) {
e.printStackT\frace();
}
创建表
try {
statement.executeUpdate("use test");//选择在哪个数据库中操作
String sql = "create table table1(" +
"column1 int not null, " +
"column2 varchar(255)" +
")";
statement.executeUpdate(sql);
} catch (SQLException e) {
e.printStackT\frace();
}
释放资源
Jdbc程序运行完后,切记要释放程序在运行过程中创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。
特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。
Connection的使用原则是尽量晚创建,尽量早的释放。
为确保资源释放代码能运行,资源释放代码一定要放在finally语句中。
finally {
try {
if(statement!=null)
statement.close();
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackT\frace();
}
}
编程要求
在右侧编辑器补充代码,完成下列相应任务:
加载数据库驱动;【平台数据库连接的用户(user)为root,密码(password)为123123】
创建数据库mysql_db;
创建表student。
student表结构为:
| 字段名 | 类型 | 备注 | 约束 |
| :————: | :————: | :————: |
| id | int | 学生id | 非空 |
| name | varchar(20) | 学生姓名 | 无 |
| sex | varchar(4) | 学生性别 | 无 |
| age | int | 学生年龄 | 无 |
测试说明
平台会对你编写的代码进行测试:
测试输入:无
预期输出:
id INT(11)
name VARCHAR(20)
sex VARCHAR(4)
age INT(11)
代码实现
package jdbc;
import java.sql.*;
public class jdbcConn {
public static void getConn() {
/********** Begin **********/
try {
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
/********** End **********/
/********** Begin **********/
Connection conn = null;
Statement statement = null;
//2.建立连接并创建数据库和表
String url = "jdbc:mysql://127.0.0.1:3306/";
String user = "root";
String password = "123123";
try{
conn = DriverManager.getConnection(url,user,password);
statement = conn.createStatement();
statement.executeUpdate("drop database if exists test");//执行sql语句
statement.executeUpdate("create database mysql_db");
statement.executeUpdate("use mysql_db");
String sql = "create table student("+
"id int not null,"+
"name varchar(20),"+
"sex varchar(4),"+
"age int)";
statement.executeUpdate(sql);
}catch (SQLException e){
e.printStackTrace();
}
/********** End **********/
finally {
try {
if(statement!=null)
statement.close();
if(conn!=null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
第2关:JDBC对表中数据的操作
任务描述
本关任务:使用JDBC完成数据插入和查询。
相关知识
为了完成本关任务,你需要掌握:1.在连接时如何指定数据库;2.向指定表中插入数据;3.遍历表中数据。
指定数据库连接
当我们已经有数据库时,可以直接在连接时指定数据库,如下指定与test_db数据库建立连接:
String url = "jdbc:mysql://localhost:3306/test_db"; Connection conn = DriverManager.getConnection (url,"root","123123" );
当在连接时指定数据库后,我们就不用编写SQL语句进行选择数据库了。
向指定表中插入数据
建立连接之后,编写向表中插入数据的sql语句,使用Statement对象的executeUpdate()方法来执行该sql语句就可向表中修改数据(该方法适用于insert、update、delete的sql语句),当sql语句为查询语句时,则使用executeQuery()方法
try {
Statement statement = conn.createStatement();
statement.executeUpdate("insert into table1(column1,column2) values(101,'xxx')");
} catch (SQLException e) {
e.printStackT\frace();
}
PreparedStatement
上述直接使用Statement向表中插入数据,操作中存在SQL注入危险,脱离上述表,如下例:
String id = "5";
String sql = "delete from tablename where id=" + id;
Statement st = conn.createStatement();
st.executeQuery(sql);//查询到表中将无数据
//如果用户传入的id为“5 or 1=1”,那么将删除表中的所有记录
为预防这种情况的SQL注入,PreparedStatement 有效的防止sql注入(SQL语句在程序运行前已经进行了预编译,当运行时动态的把参数传给PreprareStatement,即使参数里有敏感字符如or '1=1’数据库也会作为参数的一个字段属性值来处理而不会作为一个SQL指令)
PreparedStatement使用如下:
PreparedStatement statement = conn.prepareStatement("insert into table1(column1,column2) values(?,?)");//使用占位符来先占个位置
statement.setInt(1,101);//占位符顺序从1开始,根据数据库中字段相应的类型存入数据
statement.setString(2, "XXX");//也可以使用setObject
statement.executeUpdate();//每执行一个sql语句就需要执行该方法
查询表中数据
Jdbc程序中的ResultSet用于代表Sql语句的执行结果。
Resultset封装执行结果时,采用的类似于表格的方式,ResultSet对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next()方法,可以使游标指向具体的数据行,然后调用方法获取该行的数据。
//编写查询sql语句
PreparedStatement statement = conn.prepareStatement("select * from table1");
ResultSet resultSet = statement.executeQuery();//将执行结果给ResultSet
while (resultSet.next()) {//循环判断表中是否还有数据
int id = resultSet.getInt(1);//通过列的索引查询
String name = resultSet.getString("column2");//通过列名查询
编程要求
在右侧编辑器补充代码,向上一章节中已创建好的数据库mysql_db中的表student中插入数据,并将插入的数据进行输出:
| id | name | sex | age |
| :————: | :————: | :————: |
| 1 | 张三 | 男 | 19 |
| 2 | 李四 | 女 | 18 |
| 3 | 王五 | 男 | 20 |
提示:已为你封装好student类,可在右侧文件夹中查看,此类可直接使用。
测试说明
平台会对你编写的代码进行测试:
测试输入:无
预期输出:
1 张三 男 19
2 李四 女 18
3 王五 男 20
代码实现
package jdbc;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
public class jdbcInsert {
public static void insert(){
/********** Begin **********/
try {
//加载驱动
Class.forName("com.mysql.jdbc.Driver" );
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
/********** End **********/
Connection conn = null;
PreparedStatement statement = null;
/********** Begin **********/
//连接并插入数据
try{
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql_db","root","123123");
PreparedStatement s = conn.prepareStatement("insert into student values(?,?,?,?)");
s.setInt(1,1);s.setString(2,"张三");s.setString(3,"男");s.setInt(4,19);
s.executeUpdate();
s.setInt(1,2);s.setString(2,"李四");s.setString(3,"女");s.setInt(4,18);
s.executeUpdate();
s.setInt(1,3);s.setString(2,"王五");s.setString(3,"男");s.setInt(4,20);
s.executeUpdate();
s=conn.prepareStatement("select * from student");
ResultSet r = s.executeQuery();
while(r.next()){
System.out.println(r.getString(1)+" "+r.getString(2)+" "+r.getString(3)+" "+r.getString(4));
}
} catch (SQLException e) {
e.printStackTrace();
}
/********** End **********/
finally {
try {
if (statement != null)
statement.close();
if (conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
第3关:JDBC事务
任务描述
本关任务:按照具体要求编写程序。
相关知识
为了完成本关任务,你需要掌握:1. 什么是事务;2. 事务的基本要素;3.如何开启事务;4.事务的提交和回滚。
事务
假设场景,我们有一个人员管理系统,你要删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就可以构成一个事务!
事务能够控制何时更改提交并应用于数据库。 它将单个SQL语句或一组SQL语句视为一个逻辑单元,如果任何语句失败,整个事务将失败。
事务的基本要素(ACID)
原子性(Atomicity):一组事务,要么成功;要么撤回;
一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到;
隔离性(Isolation):事务独立运行。一个事务处理后的结果,影响了其他事务,那么其他事务会撤回。事务的100%隔离,需要牺牲速度;
持久性(Durability):事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。
开启事务
开启事物需要启用手动事务支持,而不是使用JDBC驱动程序默认使用的自动提交模式,可调用Connection对象的setAutoCommit()方法。 如果将布尔的false传递给setAutoCommit(),则关闭自动提交,也就相当于开启了事物。 也可以传递一个布尔值true来重新打开它。
Connection conn = DriverManager.getConnection (url,"root","123123" );
conn.setAutoCommit(false);//关闭自动提交开启事务
提交和回滚
在mysql数据库中,默认为将每一句sql都自动提交,当我们将它设置为手动事务支持(也就是已经手动开启事务)时,我们就可以在需要提交的时候进行手动提交:
conn.commit();//提交事务
当有多条sql语句一次提交时,我们需要考虑到其中sql是否合法,若其中某一条不合法,其他的sql是否仍需更改等一系列问题;确保事务的基本要素,我们需要手动调用事务回滚来控制sql的执行:
try{
Connection conn = DriverManager.getConnection (url,"root","123123" );
conn.setAutoCommit(false);//开启事务
PreparedStatement ps = conn.prepareStatement("insert into table1(column1,column2) values(1,'xx1')");
ps.executeUpdate();
ps = conn.prepareStatement("insert in table1(column1,column2) values(1,'xx1')");
ps.executeUpdate();
conn.commit();//提交事务
} catch (SQLException e) {
try {
conn.rollback();//回滚事务 回滚到你开始事务之前
} catch (SQLException e1) {
e1.printStackT\frace();
}
}
上述代码执行完毕后,数据库中并不会有数据更新。由于第二条insert语句语法错误,所以事务回滚,之前的insert也会失效。通常事务回滚都会放在catch中来捕获。
开启事务后,一定要跟上 commit 或 rollback,及时释放可能锁住的数据。
不用rollback()表面和用了rollback()效果一样,但是不用rollback()可能导致被锁住的数据不能及时的释放(需要等事物超时释放),会影响下一次的事物操作。
编程要求
根据提示,在右侧编辑器补充代码,编写一条新增SQL语句和任意一条错误的SQL语句,提交事务;要求第一条新增语句在数据库中被修改,其后错误SQL语句不执行。
新增插入语句具体要求如下:
在mysql_db数据库student表中新增一条id为4,name为赵六,sex为女,age为21的数据。
提示:每一条SQL语句之后都可提交事务。
测试说明
平台会对你编写的代码进行测试:
测试输入:无
预期输出:
1 张三 男 19
2 李四 女 18
3 王五 男 20
4 赵六 女 21
代码实现
package jdbc;
import java.sql.*;
public class jdbcTransaction {
public static void transaction(){
try {
Class.forName("com.mysql.jdbc.Driver" );
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Connection conn = null;
PreparedStatement ps = null;
/********** Begin **********/
//连接数据库并开启事务
String url = "jdbc:mysql://localhost:3306/mysql_db";
try {
conn = DriverManager.getConnection (url,"root","123123" );
conn.setAutoCommit(false);//关闭自动提交开启事务
ps = conn.prepareStatement("insert into student(id,name,sex,age) values(4,'赵六','女',21)");
ps.executeUpdate();
conn.commit();//提交事务
ps = conn.prepareStatement("insert in student(id,name,sex,age) values(5,'钱七','男',18)");
ps.executeUpdate();
conn.commit();//提交事务
} catch (SQLException e) {
try {
//事务回滚
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
/********** End **********/
finally {
try {
if(ps!=null)
ps.close();
if (conn != null)
conn.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
}
JDBC(下)
第1关:指定类型JDBC封装
任务描述
本关任务:按照要求封装一个JDBC工具类。
相关知识
为了完成本关任务,你需要掌握JDBC的基本使用,可参考上一个实训内容 Java高级特性 - JDBC(上)。
本章节将针对已知数据结构的某张表进行JDBC的封装。
连接数据库
在增删改查的过程中,我们都离不开数据库的连接,因此我们可以将其操作封装成一个方法,方法无需参数,将连接对象作为返回值。
在方法中完成驱动加载和数据库连接即可使用:
private static Connection getConnection() {
//1.加载驱动
//2.连接数据库
Connection conn=null;
//返回连接对象
return conn;
}
封装完后我们就可直接在增删改查中直接使用该方法了。
关闭数据库连接
同样每次连接完数据库我们都需要对相应资源进行释放,我们也将其封装为一个方法,方法参数为经常被使用到的对象,这些对象通常是ResultSet, Statement和Connection,因此我们的关闭连接方法如下:
public static void close(ResultSet rs,PreparedStatement ps,Connection conn){
try {
if(rs!=null) rs.close();
if(ps!=null) ps.close();
if(conn!=null) conn.close();
} catch (SQLException e) {
e.printStackT\frace();
}
}
新增数据
现我们数据库中已有的一张新闻表news,结构如下:
根据表结构我们创建一个News对象:
public class News {
private int id;
private String title;
private String anthor_name;
public News(int id, String title, String anthor_name) {
super();
this.id = id;
this.title = title;
this.anthor_name = anthor_name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
......//省略其他属性的get、set方法
}
日常生活中我们每天都会看到很多新闻,因此我们需要往数据库中不断新增最新新闻,下面我们一起来对新增方法进行封装:
封装前我们先对方法进行构思,该方法需传递一个News对象,无需返回值,因此方法的定义和实现思路如下:
public void insert(News news) throws SQLException {
Connection conn = getConnection();//拿到连接对象
PreparedStatement ps = null;
//编写新增sql语句
String sql = "";
try{
ps = conn.prepareStatement(sql);
//通过传入的news对象对预编译中 ? 进行赋值
ps.setXXX(1,news.getXXX());
//执行新增sql语句
ps.executeUpdate();
}catch(SQLException e){
e.printStackT\frace();
}finally{
//关闭连接
close(null, ps, conn);
}
}
编程要求
在右侧编辑器补充代码,完成数据库连接、删除、更新以及查找方法。其中删除方法是通过用户传入新闻id来进行删除。
注意:连接数据库名为mysql_db,数据库用户为root,密码为123123。
测试说明
平台会对你编写的代码进行测试:
测试输入:无
预期输出:
News [id=1, title=岳云鹏的18岁,贾玲的18岁,沈腾的18岁,网友:不是来搞笑的?, anthor_name=光明网]
说明:测试文件中会向news表中插入二条新闻数据,以便对你编写的方法进行检测,数据如下:
代码实现
package step1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import test.News;
public class JDBCUtils {
/**
* 连接数据库
*/
private static Connection getConnection() {
Connection conn=null;
/********** Begin **********/
String url="jdbc:mysql://localhost:3306/mysql_db";
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, "root","123123");
}catch (ClassNotFoundException e) {
e.printStackTrace();
}catch (SQLException e) {
e.printStackTrace();
}
/********** End **********/
return conn;
}
/**
* 更新数据方法
* @param news
* @throws SQLException
*/
public void update(News news) throws SQLException {
Connection conn = getConnection();
PreparedStatement ps = null;
/********** Begin **********/
String sql = "update news set title=?,author_name=? where id=?";
try{
ps = conn.prepareStatement(sql);
ps.setString(1, news.getTitle());
ps.setString(2, news.getAuthor_name());
ps.setInt(3, news.getId());
ps.executeUpdate();
}catch(SQLException e){
e.printStackTrace();
throw new SQLException("更新数据失败");
}finally{
close(null, ps, conn);
}
/********** End **********/
}
/**
* 查询所有数据
* @return
* @throws SQLException
*/
public List<News> findAll() throws SQLException {
Connection conn = getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
News news = null;
List<News> newsList = new ArrayList<News>();
/********** Begin **********/
String sql = "select * from news";
try{
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()){
news = new News();
news.setId(rs.getInt(1));
news.setTitle(rs.getString(2));
news.setAuthor_name(rs.getString(3));
newsList.add(news);
}
}catch(SQLException e){
e.printStackTrace();
throw new SQLException("查询所有数据失败");
}finally{
close(rs, ps, conn);
}
/********** End **********/
return newsList;
}
/**
* 删除方法
* @param id
* @throws SQLException
*/
public void delete(int id) throws SQLException{
Connection conn = getConnection();
PreparedStatement ps = null;
/********** Begin **********/
String sql = "delete from news where id=?";
try{
ps = conn.prepareStatement(sql);
ps.setInt(1,id);
ps.executeUpdate();
}catch(SQLException e){
e.printStackTrace();
throw new SQLException(" 删除数据失败");
}
finally{
close(null, ps, conn);
}
/********** End **********/
}
/**
* 增加对象
* @param news
* @throws SQLException
*/
public void insert(News news) throws SQLException {
Connection conn = getConnection();
PreparedStatement ps = null;
String sql = "insert into news(id,title,author_name)values(?,?,?)";
try{
ps = conn.prepareStatement(sql);
ps.setInt(1, news.getId());
ps.setString(2, news.getTitle());
ps.setString(3, news.getAuthor_name());
ps.executeUpdate();
}catch(SQLException e){
e.printStackTrace();
throw new SQLException("添加数据失败");
}finally{
close(null, ps, conn);
}
}
/**
* 根据id查询对象
* @param id
* @return
* @throws SQLException
*/
public News findById(int id) throws SQLException {
Connection conn = getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
News news = null;
String sql = "select * from news where id=?";
try{
ps = conn.prepareStatement(sql);
ps.setInt(1, id);
rs = ps.executeQuery();
if(rs.next()){
news = new News();
news.setId(id);
news.setTitle(rs.getString(2));
news.setAuthor_name(rs.getString(3));
}
}catch(SQLException e){
e.printStackTrace();
throw new SQLException("根据ID查询数据失败");
}
finally{
close(rs, ps, conn);
}
return news;
}
/**
* 关闭数据库连接
* @param rs
* @param ps
* @param conn
*/
public static void close(ResultSet rs,PreparedStatement ps,Connection conn){
try {
if(rs!=null)rs.close();
if(ps!=null)ps.close();
if(conn!=null)conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
第2关:泛型JDBC封装
任务描述
本关任务:封装一个泛型类的JDBC工具类。
相关知识
上一章节中我们对具体类(News)进行JDBC的封装,但是在一个项目中,数据库中的表远不止一个,难道我们需要对每一张表都进行封装吗?显然是不妥的,因此我们可以将JDBC工具类封装为一个泛型的。
在学习了反射( Java高级特性 - Java反射 )之后,我们都知道反射机制的强大,利用反射可以获取到类的结构信息,动态调用属性和方法等等。因此,本章节我们采用反射对JDBC的增删改查进行泛型的封装。
为了完成本关任务,你需要掌握:反射的常用方法。
反射的常用方法
获取Class的实例的三种方法
Class class=类名.class;
Class class=Class.forName("全类名");
Class class=对象.getClass();
获取对象的类名
String className=class.getName();//获取结果为全类名
String className=class.getSimpleName();//获取简单类名
获取Field
Field field=class.getField("属性名");//通过属性名获取public的属性
Field[] fields=class.getFields();//获取所有用public修饰的属性
Field field=class.getDeclaredField("属性名");//获取的属性包括public和private
Field[] field = c.getDeclaredFields();//获取所有属性包括public和private
获取Field的信息
String name=field.getName();//获取属性名
Class<?> type=filed.getType();//获取属性类型
Object value=field.get(obj);//获取obj对象field属性的值
field.set(obj,value);//设置obj对象的field属性的值
设置private修饰的属性为可访问
field.setAccessible(true);//默认为false只能对public修饰的操作,设置为true可对private修饰的操作
更新数据的泛型封装分析及实现
我们可以从sql语句来进行分析,更新数据的sql,大家都不陌生:update 表名 set
column2=value2,columen3=value3 where column1=value1;
观察sql语句我们可以将该方法设计为让用户传入一个Object对象,然后利用反射获取对象中的所有属性对其进行修改,具体实现如下(注意Object对象属性名称要求和数据库中表结构字段名以及类型一致):
public static void update(Object obj) {
Connection conn = getConnection();//获取连接对象
PreparedStatement ps = null;
Class<?> c = obj.getClass();//获取obj的Class
StringBuffer sb = new StringBuffer("update "+ c.getSimpleName() +" set ");//利用StringBuffer进行修改SQL语句的构造
Field[] field = c.getDeclaredFields();//通过反射获取对象的属性数组
for(int i = 1; i < field.length; i++) {
if(i != field.length-1) { //判断是否为最后一个属性,若不是则后增加逗号
sb.append(field[i].getName()).append("=?,");
}else { //若为最后一个属性则添加 where
sb.append(field[i].getName()).append("=? where ");
}
}
//默认第一个属性为主键,切更改时通过第一个属性进行更改
sb.append(field[0].getName() + "=?");
try {
ps = conn.prepareStatement(sb.toString());
for(int i = 1; i < field.length; i++) {
field[i].setAccessible(true);//设置可以访问私有属性
ps.setObject(i, field[i].get(obj));//对预编译的SQL语句中的 ? 进行赋值
}
field[0].setAccessible(true);
ps.setObject(field.length, field[0].get(obj));
ps.execute();//执行sql语句
} catch (Exception e) {
e.printStackT\frace();
}finally {
close(null,ps,conn);//关闭连接数据
}
}
编程要求
根据更新数据的示例,在右侧编辑器补充代码,完成增加数据、删除数据、查询表中所有数据三个方法。
测试说明
测试输入:无
预期输出:
Student [id=2, name=李四, sex=男, age=20]
News [id=1, title=岳云鹏的18岁,贾玲的18岁,沈腾的18岁,网友:不是来搞笑的?, author_name=光明网]
News [id=2, title=假设飞行器每秒跑1光年,能飞到宇宙边缘吗?科学家说出答案, author_name=探索宇宙奥秘]
平台将会根据已有的Student表和News表来调用你所编写的方法,进行数据的增删改查,分别为二张表中插入二条数据载进行修改News
表中一条数据,删除Student表中一条数据,插入数据如下:
Student表
News表
注意:本章实训封装的JDBC工具类只做参考,你也可根据自己的实际需求进行封装属于自己的工具类。
代码实现
package step2;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class JDBCUtils {
private static Connection getConnection() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
String url="jdbc:mysql://localhost:3306/mysql_db";
Connection conn=null;
try {
conn = DriverManager.getConnection(url, "root","123123");
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 类名对应表,属性对应字段
* @param obj 传入的对象
* @return
*/
public void insert(Object obj) {
Connection conn = getConnection(); //连接数据库
PreparedStatement ps = null;
/********** Begin **********/
//获取obj的Class
Class<?> c = obj.getClass();
//利用StringBuffer进行插入SQL语句的构造
StringBuffer sb1 = new StringBuffer("insert into "+ c.getSimpleName() +"("); //通过反射获取类名映射表名
StringBuffer sb2 = new StringBuffer(" values("); //注意前面要多加一个空格 否则sql将连在一起
Field[] field = c.getDeclaredFields(); //获取对象的属性数组
for(int i = 0; i < field.length; i++) { //遍历属性构造SQL语句
if(i != field.length-1) {
sb1.append(field[i].getName()).append(",");
sb2.append("?,");
}else {
sb1.append(field[i].getName()).append(")");
sb2.append("?);");
}
}
String sql = sb1.append(sb2).toString();
try {
ps = conn.prepareStatement(sql);
for(int i = 0; i < field.length; i++) {
field[i].setAccessible(true); //设置属性的可访问性,可以访问私有属性
try { //通过Field的get(Object)方法获取Object对象的属性值
ps.setObject(i+1, field[i].get(obj)); //对预编译的SQL语句中的?进行赋值
} catch (Exception e) {
e.printStackTrace();
}
}
ps.execute(); //执行SQL
}
/********** End **********/
catch (SQLException e) {
e.printStackTrace();
}finally {
close(null,ps,conn);
}
}
/**
* 通过对象的Class获取对应表中的所有记录
* @param c
* @return
*/
public <T> List<T> selectAll(Class<T> c) {
Connection conn = getConnection();
List<T> list = new ArrayList<T>();
PreparedStatement ps = null;
ResultSet rs = null;
/********** Begin **********/
String sql = "select * from "+ c.getSimpleName()+";"; //通过反射获取类名对应表名构造SQL语句
Field[] field = c.getDeclaredFields(); //通过反射获取所有属性
try {
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()) {
T obj = c.newInstance(); //通过反射构造一个T类型的实例
for(int i = 0; i < field.length; i++) {
field[i].setAccessible(true); //设置可以访问私有属性
field[i].set(obj, rs.getObject(field[i].getName())); //通过属性名获取结果集中的值赋值到实例对象中
}
list.add(obj); //将实例对象添加到list集合
}
}
/********** End **********/
catch (Exception e) {
e.printStackTrace();
}finally {
close(rs,ps,conn);
}
return list;
}
/**
* 通过主键(默认第一个属性)删除对象
* @param obj
* @return
*/
public void delete(Object obj) {
Connection conn = getConnection();
PreparedStatement ps = null;
/********** Begin **********/
//获取obj的Class
Class<?> c = obj.getClass();
//构造删除的SQL语句
StringBuffer sb = new StringBuffer("delete from ");
sb.append(c.getSimpleName()).append(" where ");
//获取对象属性数组
Field[] field = c.getDeclaredFields();
//设置第一个属性的可访问性
field[0].setAccessible(true);
//获取第一个属性的属性名构造删除sql
sb.append(field[0].getName()).append("=?");
String sql = sb.toString();
try {
ps = conn.prepareStatement(sql);
ps.setObject(1, field[0].get(obj));
ps.execute();
}
/********** End **********/
catch (Exception e) {
e.printStackTrace();
}finally {
close(null,ps,conn);
}
}
/**
* 模拟jdbc的更新操作,默认第一个属性为主键
* @param obj
* @return
*/
public void update(Object obj) {
Class<?> c = obj.getClass();//获取obj的Class
StringBuffer sb = new StringBuffer("update "+ c.getSimpleName() +" set ");//利用StringBuffer进行修改SQL语句的构造
Field[] field = c.getDeclaredFields();//通过反射获取对象的属性数组
for(int i = 1; i < field.length; i++) {
if(i != field.length-1) { //判断是否为最后一个属性,若不是则后增加逗号
sb.append(field[i].getName()).append("=?,");
}else { //若为最后一个属性则添加 where
sb.append(field[i].getName()).append("=? where ");
}
}
//默认第一个属性为主键,切更改时通过第一个属性进行更改
sb.append(field[0].getName() + "=?");
String sql = sb.toString()+";";
Connection conn = getConnection();//获取连接对象
PreparedStatement ps = null;
try {
ps = conn.prepareStatement(sql);
for(int i = 1; i < field.length; i++) {
field[i].setAccessible(true);//设置可以访问私有属性
ps.setObject(i, field[i].get(obj));//对预编译的SQL语句中的 ? 进行赋值
}
field[0].setAccessible(true);
ps.setObject(field.length, field[0].get(obj));
ps.execute();//执行sql语句
} catch (Exception e) {
e.printStackTrace();
}finally {
close(null,ps,conn);//关闭连接数据
}
}
public static void close(ResultSet rs,PreparedStatement ps,Connection conn){
try {
if(rs!=null) rs.close();
if(ps!=null) ps.close();
if(conn!=null) conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public <T> Object selectById(Class<T> c,int id) {
String sql = "select * from "+ c.getSimpleName()+" where id="+id;
Field[] field = c.getDeclaredFields();
Connection conn = getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
Object obj=null;
try {
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
obj = c.newInstance();
while(rs.next()) {
for(int i = 0; i < field.length; i++) {
field[i].setAccessible(true);
field[i].set(obj, rs.getObject(field[i].getName()));
}
}
} catch (Exception e) {
e.printStackTrace();
}finally {
close(rs,ps,conn);
}
return obj;
}
}