一文彻底弄明白贾琏欲执事(JDBC)【2】
五、JDBC之CRUD操作
5.1、Statement对象及其弊端
5.1.1、Statement对象
Statement
对象时用于执行静态 SQL 语句并返回它所生成结果的对象。
- 通过调用 Connection 对象的 createStatement() 方法创建该对象。该对象用于执行静态的 SQL 语句,并且返回执行结果。
- Statement 接口中定义了下列方法用于执行 SQL 语句:
int excuteUpdate(String sql):执行更新操作INSERT、UPDATE、DELETE
ResultSet executeQuery(String sql):执行查询操作SELECT
5.1.2、使用Statement对象的弊端
用Statement操作数据表存在弊端:
- 存在拼串操作,繁琐
- 存在SQL注入问题
SQL 注入是利用某些系统没有对用户输入的数据进行充分的检查,而在用户输入数据中注入非法的 SQL 语句段或命令(如:SELECT user, password FROM user_table WHERE user=‘a’ OR 1 = ’ AND password = ’ OR ‘1’ = ‘1’) ,从而利用系统的 SQL 引擎完成恶意行为的做法。
public class StatementTest {
// 使用Statement的弊端:需要拼写sql语句,并且存在SQL注入的问题
@Test
public void testLogin() {
Scanner scan = new Scanner(System.in);
System.out.print("用户名:");
String userName = scan.nextLine();
System.out.print("密 码:");
String password = scan.nextLine();
// SELECT user,password FROM user_table WHERE USER = '1' or ' AND PASSWORD = '='1' or '1' = '1';
String sql = "SELECT user,password FROM user_table WHERE USER = '" + userName + "' AND PASSWORD = '" + password
+ "'";//字符串拼接过于繁杂
User user = get(sql, User.class);
if (user != null) {
System.out.println("登陆成功!");
} else {
System.out.println("用户名或密码错误!");
}
}
// 使用Statement实现对数据表的查询操作
public <T> T get(String sql, Class<T> clazz) {
T t = null;
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
// 1.加载配置文件
InputStream is = StatementTest.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties pros = new Properties();
pros.load(is);
// 2.读取配置信息
String user = pros.getProperty("user");
String password = pros.getProperty("password");
String url = pros.getProperty("url");
String driverClass = pros.getProperty("driverClass");
// 3.加载驱动
Class.forName(driverClass);
// 4.获取连接
conn = DriverManager.getConnection(url, user, password);
st = conn.createStatement();
rs = st.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 {
// 关闭资源
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
return null;
}
}
5.2、PreparedStatement
我们可以通过调用 Connection 对象的 preparedStatement(String sql)
方法获取PreparedStatement
对象,PreparedStatement 接口是 Statement 的子接口,它表示一条预编译过的 SQL 语句。
PreparedStatement
对象所代表的 SQL 语句中的参数用问号(?)来表示,调用PreparedStatement
对象的 setXxx() 方法来设置这些参数. setXxx() 方法有两个参数,第一个参数是要设置的 SQL 语句中的参数的索引(从 1 开始),第二个是设置的 SQL 语句中的参数的值。
// 常用方法:
void setXxx(int parameterIndex,Xxx value); //设置第几个占位符的真正参数值.
// Xxx 表示数据类型,比如 String,int,long,Date等.
void setObject(int parameterIndex, Object x); //设置第几个占位符的真正参数值.
int executeUpdate(); //执行DDL/DML语句. 注意:没有参数
// 若当前 SQL是 DDL语句,则返回 0.
// 若当前 SQL是 DML语句,则返回受影响的行数.
ResultSet executeQuery(); //执行DQL语句,返回结果集.
close(); //释放资源
5.3、PreparedStatement vs Statement
-
PreparedStatement
对象比Statement
对象的代码的可读性和可维护性。 -
PreparedStatement
能最大可能提高性能。- DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
- 在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存。这样每执行一次都要对传入的语句编译一次。
-
PreparedStatement 可以防止 SQL 注入 。
5.4、 ResultSet
查询需要调用PreparedStatement
的 executeQuery()
方法,查询结果是一个ResultSet 对象,ResultSet 对象以逻辑表格的形式封装了执行数据库操作的结果集,ResultSet 接口由数据库厂商提供实现。
ResultSet 返回的实际上就是一张数据表。有一个指针指向数据表的第一条记录的前面。
ResultSet 对象维护了一个指向当前数据行的游标,初始的时候,游标在第一行之前,可以通过 ResultSet 对象的 next()
方法移动到下一行。调用 next()
方法检测下一行是否有效。若有效,该方法返回 true,且指针下移。相当于Iterator对象的hasNext()
和next()
方法的结合体。
当指针指向一行时, 可以通过调用 getXxx(int index)
或 getXxx(int columnName)
获取每一列的值。*Java与数据库交互涉及到的相关Java API中的索引都从1开始。*例如:
getInt(1), getString("name")
5.5、CRUD操作
5.5.1、User类
package domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Xiao_Lin
* @date 2021/1/2 19:44
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
//id
private Integer id;
//用户名
private String username;
//密码
private String pwd;
//构造方法的重载
public User(String username, String pwd) {
this.username = username;
this.pwd = pwd;
}
}
5.5.2、IUserDAO
package dao;
import domain.User;
import java.util.List;
/**
* @author Xiao_Lin
* @date 2021/1/2 19:46
*/
public interface IUserDAO {
public void insert(User user);
public void delete(Integer id);
public void update(User user);
public List<User> selectAll();
public User selectUserById(Integer id);
}
5.5.3、UserDAOImpl
package dao.impl;
import dao.IUserDAO;
import dao.utils.DaoUtils;
import domain.User;
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 java.util.UUID;
/**
* @author Xiao_Lin
* @date 2021/1/2 19:49
*/
public class UserDAOImpl implements IUserDAO {
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
@Override
public void insert(User user) {
try {
connection = DaoUtils.getConnection();
String sql = "insert into user (username,pwd) values (?,?)";
ps = connection.prepareStatement(sql);
ps.setString(1,user.getUsername());
ps.setString(2,user.getPwd());
ps.executeUpdate();
System.out.println("添加成功!!");
} catch (SQLException e) {
e.printStackTrace();
}finally {
DaoUtils.close(connection,ps,null);
}
}
@Override
public void delete(Integer id) {
try {
connection = DaoUtils.getConnection();
ps = connection.prepareStatement("delete from user where id = ?");
ps.setInt(1,id);
ps.executeUpdate();
System.out.println("删除成功!!");
} catch (Exception e) {
e.printStackTrace();
}finally {
DaoUtils.close(connection,ps,null);
}
}
@Override
public void update(User user) {
try {
connection = DaoUtils.getConnection();
ps = connection.prepareStatement("update user set username = ? , pwd = ? where id = ?");
ps.setString(1,user.getUsername());
ps.setString(2,user.getPwd());
ps.setInt(3,user.getId());
ps.executeUpdate();
System.out.println("修改成功");
} catch (Exception e) {
e.printStackTrace();
}finally {
DaoUtils.close(connection,ps,null);
}
}
@Override
public List<User> selectAll() {
List<User> users = new ArrayList<>();
try {
connection = DaoUtils.getConnection();
ps = connection.prepareStatement("select * from user");
rs = ps.executeQuery();
while (rs.next()){
users.add(new User(rs.getInt("id"),rs.getString("username"),rs.getString("pwd")));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DaoUtils.close(connection,ps,rs);
}
return users;
}
@Override
public User selectUserById(Integer id) {
User user = null;
try {
connection = DaoUtils.getConnection();
ps = connection.prepareStatement("select * from user where id = ?");
ps.setInt(1,id);
rs = ps.executeQuery();
while (rs.next()){
user = new User(rs.getInt("id"),rs.getString("username"),rs.getString("pwd"));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
DaoUtils.close(connection,ps,rs);
}
return user;
}
}
5.5.4、DaoUtils
package dao.utils;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
/**
* @author Xiao_Lin
* @date 2021/1/2 19:56
*/
public class DaoUtils {
static Connection connection = null;
static Properties properties = null;
static {
InputStream resourceAsStream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("db.properties");
properties = new Properties();
try {
properties.load(resourceAsStream);
Class.forName(properties.getProperty("DriverClassName"));
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
connection= DriverManager.getConnection(properties.getProperty("url"),properties.getProperty("username"),properties.getProperty("password"));
return connection;
}
public static void close(Connection conn , PreparedStatement ps , ResultSet rs){
try {
if(rs!=null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
if(ps!=null){
ps.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if(conn!=null){
conn.close();
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
5.5.5、测试类
package dao.impl;
import dao.IUserDAO;
import dao.utils.DaoUtils;
import domain.User;
import java.sql.SQLException;
import java.util.List;
import org.junit.Test;
/**
* @author Xiao_Lin
* @date 2021/1/2 20:11
*/
public class UserDAOImplTest {
IUserDAO userDAO = new UserDAOImpl();
@Test
public void insert() {
userDAO.insert(new User("ghy","123"));
}
@Test
public void delete() {
userDAO.delete(14);
}
@Test
public void update() {
userDAO.update(new User(1,"张三","666"));
}
@Test
public void selectAll() {
List<User> users = userDAO.selectAll();
users.forEach(System.out::print);
}
@Test
public void selectUserById() {
User user = userDAO.selectUserById(1);
System.out.println(user);
}
}
六、操作BLOB类型字段
6.1、BLOB类型简介
MySQL中,BLOB是一个二进制大型对象,是一个可以存储大量数据的容器,它能容纳不同大小的数据。**插入BLOB类型的数据必须使用PreparedStatement,因为BLOB类型的数据无法使用字符串拼接写的。**MySQL有四种BLOB类型,他们除了在存储的最大信息量上不同外,除此之外他们是等同的。
如果在指定了相关的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件加上如下的配置参数: max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。
6.2、插入BLOB类型
//获取连接
Connection conn = JDBCUtils.getConnection();
String sql = "insert into customers(name,email,birth,photo)values(?,?,?,?)";
PreparedStatement ps = conn.prepareStatement(sql);
// 填充占位符
ps.setString(1, "张三");
ps.setString(2, "zs@126.com");
ps.setDate(3, new Date(new java.util.Date().getTime()));
// 操作Blob类型的变量
FileInputStream fis = new FileInputStream("zs.png");
ps.setBlob(4, fis);
//执行
ps.execute();
fis.close();
JDBCUtils.closeResource(conn, ps);
6.3、修改BLOB类型
Connection conn = JDBCUtils.getConnection();
String sql = "update customers set photo = ? where id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
// 填充占位符
// 操作Blob类型的变量
FileInputStream fis = new FileInputStream("coffee.png");
ps.setBlob(1, fis);
ps.setInt(2, 25);
ps.execute();
fis.close();
JDBCUtils.closeResource(conn, ps);
6.4、从数据库表中读取BLOG类型
String sql = "SELECT id, name, email, birth, photo FROM customer WHERE id = ?";
conn = getConnection();
ps = conn.prepareStatement(sql);
ps.setInt(1, 8);
rs = ps.executeQuery();
if(rs.next()){
Integer id = rs.getInt(1);
String name = rs.getString(2);
String email = rs.getString(3);
Date birth = rs.getDate(4);
Customer cust = new Customer(id, name, email, birth);
System.out.println(cust);
//读取Blob类型的字段
Blob photo = rs.getBlob(5);//这里也可以通过列的索引来读取
InputStream is = photo.getBinaryStream();
OutputStream os = new FileOutputStream("c.jpg");
byte [] buffer = new byte[1024];
int len = 0;
while((len = is.read(buffer)) != -1){
os.write(buffer, 0, len);
}
JDBCUtils.closeResource(conn, ps, rs);
if(is != null){
is.close();
}
if(os != null){
os.close();
}
}
- 点赞
- 收藏
- 关注作者
评论(0)