03、使用PreparedStatement
实现CRUD操作
3.1、操作和访问数据库
-
数据库连接被用于向数据库服务器发送命令和 SQL 语句,并接受数据库服务器返回的结果。其实一个数据库连接就是一个Socket连接。
-
在
java.sql
包中有 3 个接口分别定义了对数据库的调用的不同方式:-
Statement
:用于执行静态 SQL 语句并返回它所生成结果的对象。 -
PrepatedStatement
:SQL 语句被预编译并存储在此对象中,可以使用此对象多次高效地执行该语句。 -
CallableStatement
:用于执行 SQL 存储过程。
-
3.2、构建数据库连接与关闭的工具类——JDBCUtil
由于获取数据库连接与关闭数据库连接都是会反复使用的,因此可以将构建连接与关闭连接封装进工具类的成员方法,工具类如下:
工具类的构建是基于如下考虑:每次执行数据库操作时都需要获取数据库连接,执行完操作后需要关闭数据库连接,这些操作如果和其他数据库操作写在一起就会很冗余,因此考虑将其写成一个工具类的静态方法,供需要时调用。工具类中应当包含以下两部分。
3.2.1、获取数据库连接
/**
* 获取数据库连接
* @return 数据库的连接
* @throws Exception
*/
public static Connection getConnection() throws Exception {
//1、加载配置文件
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properties");
Properties properties = new Properties();
properties.load(is);
//2、读取配置文件
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String url = properties.getProperty("url");
String driverClass = properties.getProperty("driverClass");
//3、加载驱动
Class.forName(driverClass);
//获取连接
Connection connection = DriverManager.getConnection(url, user, password);
return connection;
}
3.2.2、关闭数据库连接(关闭资源)
(1)关闭没有结果集的数据库连接
/**
* 关闭没有结果集的数据库连接
* @param connection
* @param sm
*/
public static void closeResource(Connection connection, Statement sm) {
if (sm != null) {
try {
sm.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 关闭没有结果集的数据库连接
* @param connection
* @param psm
*/
public static void closeResource(Connection connection, PreparedStatement psm) {
if (psm != null) {
try {
psm.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
(2)关闭有结果集的数据库连接
- 释放
ResultSet
,PreparedStatement
,Connection
。 - 数据库连接(Connection)是非常稀有的资源,用完后必须马上释放,如果Connection不能及时正确的关闭将导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
- 可以在
finally
中关闭,保证及时其他代码出现异常,资源也一定能被关闭。
/**
* 关闭有结果集的数据库连接
* @param connection
* @param sm
* @param rs
*/
public static void closeResource(Connection connection, Statement sm, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (sm != null) {
try {
sm.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 关闭有结果集的数据库连接
* @param connection
* @param psm
* @param rs
*/
public static void closeResource(Connection connection, PreparedStatement psm, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (psm != null) {
try {
psm.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
3.3、使用Statement操作数据表的弊端
-
通过调用
Connection
对象的createStatement()
方法创建该对象。该对象用于执行静态的SQL
语句,并且返回执行结果。 -
Statement
接口中定义了下列方法用于执行SQL
语句:int excuteUpdate(String sql);//执行更新操作INSERT、UPDATE、DELETE ResultSet executeQuery(String sql);//执行查询操作SELECT
-
但是使用
Statement
操作数据表存在弊端:- 问题一:存在拼串操作,繁琐
- 问题二:存在SQL注入问题
-
SQL
注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的SQL
语句段或命令。如
-
对于 Java 而言,要防范 SQL 注入,只要用
PreparedStatement
(从Statement
扩展而来) 取代Statement
就可以了。 -
代码演示:
User.java
public class User { String userName; String password; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public String toString() { return "User{" + "userName='" + userName + '\'' + ", password='" + password + '\'' + '}'; } }
StatementTest.java
import java.lang.reflect.Field; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.Statement; import java.util.Scanner; public class StatementTest { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("用户名:"); String userName = sc.nextLine();//以换行表示输入的结束 System.out.println("密 码:"); String password = sc.nextLine(); String sql = "select userName,password from user where userNAme = '" + userName +"' and password = '" + password + "'"; User user = get(sql, User.class); if (user != null) { System.out.println("登录成功!"); }else { System.out.println("用户名或密码错误!"); } } public static <T> T get(String sql, Class<T> clazz) { T t = null; Connection con = null; Statement sm = null; ResultSet rs = null; try { //使用工具类获取连接 con = JDBCUtil.getConnection(); sm = con.createStatement(); rs = sm.executeQuery(sql); //获取结果集的元数据 ResultSetMetaData rsmd = rs.getMetaData(); //获取结果集的列数 int columnCount = rsmd.getColumnCount(); if (rs.next()) { t = clazz.newInstance(); for (int i = 0; i < columnCount; i++) { //1、获取列的名称 //String columnName = rsmd.getColumnName(i + 1); //1、获取列的别名 String columnName = rsmd.getColumnLabel(i + 1); //2、根据列名获取对应数据表中的数据 Object columnVal = rs.getObject(columnName); //3、将数据表中得到的数据,封装进对象 Field field = clazz.getDeclaredField(columnName); field.setAccessible(true); field.set(t, columnVal); } return t; } } catch (Exception e) { e.printStackTrace(); } finally { JDBCUtil.closeResource(con, sm, rs); } return null; } }
综上:
3.4、PreparedStatement的使用
3.4.1、PreparedStatement介绍
- 可以通过调用
Connection
对象的preparedStatement(String sql)
方法获取PreparedStatement
对象。 PreparedStatement
接口是Statement
的子接口,它表示一条预编译过的 SQL 语句。PreparedStatement
对象所代表的 SQL 语句中的参数用问号(?)来表示,调用PreparedStatement
对象的setXxx()
方法来设置这些参数。setXxx()
方法有两个参数,第一个参数是要设置的 SQL 语句 中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值。
3.4.2、PreparedStatement vs Statement
- 提高了代码的可读性和可维护性。
PreparedStatement
能最大可能提高性能:DBServer
会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer
的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。- 在
statement
语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义。事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。 - (语法检查,语义检查,翻译成二进制命令,缓存)
PreparedStatement
可以防止SQL
注入
3.4.3、Java与SQL对应数据类型转换表
Java类型 | SQL类型 |
---|---|
boolean | BIT |
byte | TINYINT |
short | SAMLLINT |
int | INTEGER |
long | BIGINT |
String | CHAR,VARCHAR,LONGVARCHAR |
byte array | BINARY,VAR BINARY |
java.sql.Date |
DATE |
java.sql.Time |
TIME |
java.sql.Timestamp |
TIMESTAMP |
3.4.4、使用PreparedStatement实现增、删、改操作
3.4.4.1、实现表数据的增删改操作
1、添加操作:(向customers表中插入数据)
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Date;
import java.text.SimpleDateFormat;
public class PreparedStatementTest {
@Test
public void testInsert() throws Exception{
Connection connection = null;
PreparedStatement psm = null;
//1、获取连接
connection = JDBCUtil.getConnection();
//2、预编译SQL语句,返回PreparedStatement实例
String sql = "insert into customers(name,email,birth) values (?,?,?)";
psm = connection.prepareStatement(sql);
//3、填充占位符
psm.setString(1,"wjk");
psm.setString(2, "wjk123@gemail.com");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
java.util.Date date = sdf.parse("1999-09-21");
psm.setDate(3, new Date(date.getTime()));
//4、执行操作
psm.execute();
//5、关闭资源
JDBCUtil.closeResource(connection, psm);
}
}
2、修改操作:
@Test
public void testUpdate() {
Connection connection = null;
PreparedStatement psm = null;
try{
//1、获取数据库的连接
connection = JDBCUtil.getConnection();
//2、预编译SQL语句,返回PreparedStatement实例
String sql = "update customers set name = ? where id = ?";
psm = connection.prepareStatement(sql);
//3、填充占位符
psm.setObject(1, "dc");
psm.setObject(2,1);
//4、执行
psm.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5、关闭资源
JDBCUtil.closeResource(connection, psm);
}
}
- 增删改的方式一:
ps.execute()
:- 如果执行的是查询操作,有返回结果,则此方法返回true;
- 如果是增删改操作,没有返回结果,则此时返回false
- 增删改的方式二:
ps.executeUpdata()
:- 返回一个数,该数值为修改所涉及的条目的个数。例如修改了4条数据,就返回4。
因此可以利用下面的代码段进行添加的信息回执:
int insertCount = update(sql,arg0,arg1...){
if(insertCount>0){
System.out.println("添加成功!");
}
}
3.4.4.2、实现通用的增删改操作
//通用的增删改操作,使用可变形参
//sql语句中的占位符的个数与可变形参的长度一致
public void update(String sql, Object ... args) {
Connection connection = null;
PreparedStatement psm = null;
try {
//1、获取数据库的连接
connection = JDBCUtil.getConnection();
//2、预编译SQL语句,返回PreparedStatement实例
psm = connection.prepareStatement(sql);
//3、填充占位符
for (int i = 0; i < args.length; i++) {
//小心参数的声明错误
psm.setObject(i + 1, args[i]);
}
//4、执行
psm.execute();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5、资源的关闭
JDBCUtil.closeResource(connection, psm);
}
}
@Test
public void testCommonUpdate() throws Exception{
//删除操作
//String sql = "delete from customers where id = ?";
//update(sql , 1);
//插入操作
// String sql = "insert into customers(name,email,birth) values (?,?,?)";
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// java.util.Date date = sdf.parse("1991-10-05");
// update(sql, "xz", "xz@123.com",new Date(date.getTime()));
//修改操作
String sql = "update customers set email = ? where name = ?";
update(sql, "xz222@gemail.com","xz");
}
3.4.5、使用PreparedStatement实现查询操作
3.4.5.1、实现数据表的查询操作
Customer.java
import java.sql.Date;
/**
* ORM编程思想:Object Relational Mapping
* 一个数据表对应一个Java类
* 表中的一条记录对应Java类的一个对象
* 表中的一个字段对应Java类的一个属性
*/
public class Customer {
private int id;
private String name;
private String email;
private Date birth;
public Customer(int id, String name, String email, Date birth) {
this.id = id;
this.name = name;
this.email = email;
this.birth = birth;
}
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 getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirth() {
return birth;
}
public void setBirth(Date birth) {
this.birth = birth;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", email='" + email + '\'' +
", birth=" + birth +
'}';
}
}
CustomerForQuery.java
import org.junit.Test;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
/***
* @Description 针对customers表的查询操作
*/
public class CustomerForQuery {
@Test
public void testQuery1(){
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = JDBCUtil.getConnection();
String sql = "select id, name, email, birth from customers where id = ?";
ps = connection.prepareStatement(sql);
ps.setObject(1, 2);
//执行,返回结果集
rs = ps.executeQuery();
//处理结果集
if (rs.next()) {//next():判断结果集的下一条是否有数据,如果有数据返回true,并指针下移;如果返回false,则指针不下移
//获取当前这条数据的各个字段值
int id = rs.getInt(1);
String name = rs.getString(2);
String email = rs.getString(3);
Date birth = rs.getDate(4);
//方式一:
System.out.println("id = " + id + ", name = " + name + ", email = "+ email + ", birth = " + birth);
//方式二:
Object[] data = new Object[]{id, name, email, birth};
//方式三:将数据封装成一个对象(推荐)
Customer customer = new Customer(id,name,email,birth);
System.out.println(customer);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭资源
JDBCUtil.closeResource(connection, ps, rs);
}
}
}
3.4.5.2、实现通用的查询操作
1、针对于customers表的通用查询操作
/**
*针对于customers表的通用查询操作
*/
public Customer queryForCustomers(String sql, Object ... args) {
Connection connection = null;
PreparedStatement psm = null;
ResultSet rs = null;
try {
connection = JDBCUtil.getConnection();
psm = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
psm.setObject(i + 1, args[i]);
}
rs = psm.executeQuery();
//获取结果集的元数据 String name = "Tom"; Tom是关键,String和name是修饰name的两个元数据
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集中的列表
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
Customer customer = new Customer();
//处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {
Object columnValue = rs.getObject(i + 1);
//获取每个列的列名
String columnName = rsmd.getColumnName(i + 1);
//给customer对象指定的columnName属性赋值columnValue,通过反射
Field field = Customer.class.getDeclaredField(columnName);
field.setAccessible(true);
field.set(customer, columnValue);
}
return customer;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(connection, psm, rs);
}
return null;
}
@Test
public void testQueryForCustomer() {
String sql = "select id ,name, email, birth from customers where id = ?";
Customer customer = queryForCustomers(sql, 3);
System.out.println(customer);
}
2、针对于Order表的通用查询操作(与Customer表不同的是,order表中列名与Order类中的属性名不同)
import org.junit.Test;
import java.lang.reflect.Field;
import java.sql.*;
/**
* 针对于Order表的通用查询操作
*/
public class OrderForQuery {
/**
* 针对于表打的字段名与类的属性名不相同的情况:
* 1、必须声明sql时,使用类的属性名来命名字段的别名
* 2、使用ResultSetMetaData时,需要使用getColumnLabel()来替换getColumnName(),获取列的别名
*
* 说明:如果sql中没有给字段取别名,getColumnLabel()获取的就是列名
*/
@Test
public void testQuery() {
String sql = "select order_id orderId, order_name orderName, order_date orderDate from `order` where order_id = ?";
Order order = orderForQuery(sql,1);
System.out.println(order);
}
/**
* 针对于Order表的通用查询操作
* @return
*/
public Order orderForQuery(String sql, Object... args) {
Connection connection = null;
PreparedStatement psm = null;
ResultSet rs = null;
try {
connection = JDBCUtil.getConnection();
psm = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
psm.setObject(i + 1, args[i]);
}
//执行,获取结果集
rs = psm.executeQuery();
//获取结果集的元数据
ResultSetMetaData rsmd= rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
Order order = new Order();
for(int i = 0; i < columnCount; i++) {
//获取每个列的列值;通过ResultSet
Object columValue = rs.getObject(i + 1);
//getColumnName():获取每个列的列名;通过ResultMetaData-------不推荐使用
//String columnName = rsmd.getColumnName(i + 1);
//getColumnLabel():获取每个列的别名;通过ResultMetaData
String columnLabel = rsmd.getColumnLabel(i + 1);
//通过反射,将对象指定名columnName的属性赋值为指定的值columnValue
Field field = Order.class.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(order, columValue);
}
return order;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(connection,psm,rs);
}
return null;
}
}
3.4.5.3、查询操作的流程
3.4.5.4、实现不同表的通用的查询操作
1、返回表中查询的一条记录
package jdbcTest;
import org.junit.Test;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
/**
* 使用PreparedStatement实现不同表的通用的查询操作
*/
public class PreparedStatementQueryTest {
@Test
public void testGetInstance() {
String sql = "select id, name, email from customers where id = ? ";
Customer customer = getInstance(Customer.class, sql, 2);
System.out.println(customer);
String sql1 = "select order_id orderId, order_name orderName from `order` where order_id = ?";
Order order = getInstance(Order.class, sql1, 1);
System.out.println(order);
}
/**
* 针对于不同的表的通用查询操作,返回表中的一条记录
* @param clazz
* @param sql
* @param args
* @param <T>
* @return
*/
public <T> T getInstance(Class<T> clazz,String sql, Object ... args) {
Connection connection = null;
PreparedStatement psm = null;
ResultSet rs = null;
try {
connection = JDBCUtil.getConnection();
psm = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
psm.setObject(i + 1, args[i]);
}
rs = psm.executeQuery();
//获取结果集的元数据 String name = "Tom"; Tom是关键,String和name是修饰name的两个元数据
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集中的列表
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
T t = clazz.newInstance();
//处理结果集一行数据中的每一个列
for (int i = 0; i < columnCount; i++) {
Object columnValue = rs.getObject(i + 1);
//获取每个列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
//给t对象指定的columnName属性赋值columnValue,通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
return t;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(connection, psm, rs);
}
return null;
}
}
2、返回表中的多条记录
package jdbcTest;
import org.junit.Test;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
/**
* 使用PreparedStatement实现不同表的通用的查询操作
*/
public class PreparedStatementQueryTest {
@Test
public void testGetForList() {
String sql = "select id, name, email from customers ";
List<Customer> list = getForList(Customer.class, sql);
list.forEach(System.out :: println);
String sql1 = "select order_id orderId, order_name orderName from `order` where order_id < ?";
List<Order> list1 = getForList(Order.class, sql1, 3);
list1.forEach(System.out :: println);
}
/**
* 针对于不同的表的通用查询操作,返回表中的多条记录
* @param clazz
* @param sql
* @param args
* @param <T>
* @return
*/
public <T> List<T> getForList(Class<T> clazz, String sql, Object ... args) {
Connection connection = null;
PreparedStatement psm = null;
ResultSet rs = null;
try {
connection = JDBCUtil.getConnection();
psm = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
psm.setObject(i + 1, args[i]);
}
rs = psm.executeQuery();
//获取结果集的元数据 String name = "Tom"; Tom是关键,String和name是修饰name的两个元数据
ResultSetMetaData rsmd = rs.getMetaData();
//通过ResultSetMetaData获取结果集中的列表
int columnCount = rsmd.getColumnCount();
//创建集合对象
ArrayList<T> list = new ArrayList<>();
while (rs.next()) {
T t = clazz.newInstance();
//处理结果集一行数据中的每一个列;给t对象指定的属性赋值
for (int i = 0; i < columnCount; i++) {
Object columnValue = rs.getObject(i + 1);
//获取每个列的别名
String columnLabel = rsmd.getColumnLabel(i + 1);
//给t对象指定的columnName属性赋值columnValue,通过反射
Field field = clazz.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(t, columnValue);
}
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(connection, psm, rs);
}
return null;
}
}
3.5、ResultSet与ResultMetaData
3.5.1、ResultSet
- 查询需要调用
PreparedStatement
的executeQuery()
方法,查询结果是一个ResultSet
对象 ResultSet
对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet
接口由数据库厂商提供实现。ResultSet
返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。ResultSet
对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过ResultSet
对象 的next()
方法移动到下一行。调用next()
方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。 相当于Iterator对象的hasNext()
和next()
方法的结合体。- 当指针指向一行时, 可以通过调用
getXxx(int index)
或getXxx(int columnName)
获取每一列的值。- 例如:
getInt(1)
,getString("name")
- 注意:Java与数据库交互涉及到的相关Java API中的索引都从1开始
- 例如:
ResultSet
接口的常用方法:boolean next()
getString()
3.5.2、ResultSetMetaData
-
可用于获取关于
ResultSet
对象中列的类型和属性信息的对象。 -
ResultSetMetaData meta = rs.getMetaData();
getColumnName(int column)
:获取指定列的名称getColumnLabel(int column)
:获取指定列的别名getColumnCount()
:返回当前ResultSet
对象中的列数。getColumnTypeName(int column)
:检索指定列的数据库特定的类型名称。getColumnDisplaySize(int column)
:指示指定列的最大标准宽度,以字符为单位。isNullable(int column)
:指示指定列中的值是否可以为 null。isAutoIncrement(int column)
:指示是否自动为指定列进行编号,这样这些列仍然是只读的。
问题1:得到结果集后, 如何知道该结果集中有哪些列 ? 列名是什么?
需要使用一个描述 ResultSet
的对象, 即 ResultSetMetaData
问题2:关于ResultSetMetaData
1、如何获取ResultSetMetaData
:调用 ResultSet
的 getMetaData()
方法即可。
2、获取 ResultSet
中有多少列:调用 ResultSetMetaData
的 getColumnCount()
方法。
3、获取 ResultSet
每一列的列的别名是什么:调用 ResultSetMetaData
的getColumnLabel()
方法。
3.6、JDBC API小结
-
两种思想
- 面向接口编程的思想
ORM
思想(object relational mapping)- 一个数据表对应一个java类
- 表中的一条记录对应java类的一个对象
- 表中的一个字段对应java类的一个属性
-
两种技术
JDBC
结果集的元数据:ResultSetMetaData
- 获取列数:
getColumnCount()
- 获取列的别名:
getColumnLabel()
- 获取列数:
- 通过反射,创建指定类的对象,获取指定的属性并赋值
3.7、章节练习
1、练习题1:从控制台向数据库的表customers中插入一条数据,表结构如下:
import jdbcTest.JDBCUtil;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Scanner;
public class exercise1 {
//通用的增删改操作,使用可变形参
//sql语句中的占位符的个数与可变形参的长度一致
public int update(String sql, Object... args) {
Connection connection = null;
PreparedStatement psm = null;
try {
//1、获取数据库的连接
connection = JDBCUtil.getConnection();
//2、预编译SQL语句,返回PreparedStatement实例
psm = connection.prepareStatement(sql);
//3、填充占位符
for (int i = 0; i < args.length; i++) {
//小心参数的声明错误
psm.setObject(i + 1, args[i]);
}
//4、执行
/**
* psm.execute();
* 如果执行的是查询操作,有返回结果,则此方法返回true
* 如果执行的是增删改操作,没有返回结果,则此方法返回false
*/
//psm.execute();
return psm.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
//5、资源的关闭
JDBCUtil.closeResource(connection, psm);
}
return 0;
}
@Test
public void test() {
Scanner sc = new Scanner(System.in);
//从控制台输入要插入的数据
System.out.println("请输入姓名:");
String name = sc.nextLine();
System.out.println("请输入生日(年-月-日):");
String birth = sc.nextLine();
System.out.println("请输入邮箱:");
String email = sc.nextLine();
String sql = "insert into customers(name, birth, email) values (?,?,?)";
int insertCount = update(sql,name,birth,email);
if (insertCount > 0) {
System.out.println("添加成功");
} else {
System.out.println("添加失败");
}
}
}
2、创立数据库表 examstudent,表结构如下:
向数据表中添加如下数据:
代码实现1:插入一个新的student 信息
请输入考生的详细信息
Type:
IDCard:
ExamCard:
StudentName:
Location:
Grade:
信息录入成功!
@Test
public void test() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入考试类型:");
int type = sc.nextInt();
System.out.println("请输入考生身份证号:");
String IDCard = sc.next();
System.out.println("请输入考生准考证号:");
String examCard = sc.next();
System.out.println("请输入学生姓名:");
String StudentName = sc.next();
System.out.println("请输入区域:");
String Location = sc.next();
System.out.println("请输入成绩:");
int grade = sc.nextInt();
String sql = "insert into student(Type, IDCard, ExamCard, StudentName, Location, Grade) values(?,?,?,?,?,?)";
int insertCount = update(sql, type, IDCard, examCard, StudentName, Location, grade);
if (insertCount > 0) {
System.out.println("信息录入成功!");
} else {
System.out.println("信息录入失败!");
}
}
代码实现2:在 eclipse中建立 java 程序:输入身份证号或准考证号可以查询到学生的基本信息。结果如下:
Student.java
package jdbcExercise;
import org.junit.Test;
public class Student {
private int FlowID;
private int Type;//四级/六级
private String IDCard;//身份证号
private String ExamCard;//准考证号
private String StudentName;
private String Location;//区域
private int Grade;
public Student() {
super();
}
public Student(int flowId, int type, String IDCard, String examCard, String studentName, String location, int grade) {
FlowID = flowId;
Type = type;
this.IDCard = IDCard;
ExamCard = examCard;
StudentName = studentName;
Location = location;
this.Grade = grade;
}
public int getFlowID() {
return FlowID;
}
public void setFlowId(int flowId) {
FlowID = flowId;
}
public int getType() {
return Type;
}
public void setType(int type) {
Type = type;
}
public String getIDCard() {
return IDCard;
}
public void setIDCard(String IDCard) {
this.IDCard = IDCard;
}
public String getExamCard() {
return ExamCard;
}
public void setExamCard(String examCard) {
ExamCard = examCard;
}
public String getStudentName() {
return StudentName;
}
public void setStudentName(String studentName) {
StudentName = studentName;
}
public String getLocation() {
return Location;
}
public void setLocation(String location) {
Location = location;
}
public int getGrade() {
return Grade;
}
public void setGrade(int grade) {
this.Grade = grade;
}
@Override
public String toString() {
System.out.println("========查询结果========");
return info();
}
private String info() {
return "流水号:" + FlowID + "\n四级/六级:" + Type +
"\n身份证号:" + IDCard + "\n准考证号:" + ExamCard +
"\n学生姓名:" + StudentName + "\n区域:" + Location + "\n成绩:" + Grade;
}
}
Exercise2Test.java
@Test
public void testQuery() {
Scanner sc = new Scanner(System.in);
boolean flag = true;
while (flag) {
System.out.println("请输入您要输入的类型:\n a:准考证号\n b:身份证号\n");
String type = sc.next();
if (!type.equalsIgnoreCase("a") && !type.equalsIgnoreCase("b")){
System.out.println("您的输入错误,请重新进入程序!");
continue;
}
char c = type.charAt(0);
String sql = "";
switch (c) {
case 'a':
sql = "select * from student where ExamCard = ?";
System.out.println("请输入准考证号:");
break;
case 'b':
sql = "select * from student where IDCard = ?";
System.out.println("请输入身份证号:");
break;
}
String card = sc.next();
Student student = getStudent(sql, card);
if (student == null) {
System.out.println("查无此人!请重新进入程序!");
} else {
System.out.println(student);
flag = false;
}
}
}
/**
* 获取查询学生信息
* @param sql
* @param args
* @return
*/
public Student getStudent(String sql, Object ... args) {
Connection connection = null;
PreparedStatement psm = null;
ResultSet rs = null;
try {
connection = JDBCUtil.getConnection();
psm = connection.prepareStatement(sql);
for (int i = 0; i < args.length; i++) {
psm.setObject(i + 1, args[i]);
}
//查询,返回结果集
rs = psm.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
//获取列数
int columnCount = rsmd.getColumnCount();
if (rs.next()) {
Student student = new Student();
for (int i = 0; i < columnCount; i++) {
Object columnValue = rs.getObject(i + 1);
String columnLabel = rsmd.getColumnLabel(i + 1);
Field field = Student.class.getDeclaredField(columnLabel);
field.setAccessible(true);
field.set(student, columnValue);
}
return student;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtil.closeResource(connection, psm, rs);
}
retu
代码实现3:完成学生信息的删除功能
@Test
public void testDelete() {
Scanner sc = new Scanner(System.in);
System.out.println("请输入学生的考号:");
String examCard = sc.next();
String sql = "delete from student where ExamCard = ?";
int deleteCount = update(sql, examCard);
if (deleteCount > 0) {
System.out.println("删除成功!!!");
} else {
System.out.println("查无此人,请重新输入!!!");
}
}