1 JDBC的使用(续)
1.1 dao层的编写、PreparedStatement执行器的使用
dao层,(Data Access Object,数据实现对象)存放使用JDBC应用Java对数据库表进行增删改查的方法所封装成的类的集合。
-
PreparedStatement和Statement有什么区别?
-
两者同为接口,但PreparedStatement是Statement的子接口
(接口与接口之间是继承,接口与类之间是实现)
源码中:
public interface Statement extends Wrapperpublic interface PreparedStatement extends Statement
-
Statement只能执行静态语句,PreparedStatement可以执行IN参数的SQL语句(所谓IN参数是指,SQL语句可以进行字段等数据的更改,并不是一个固定的语句)。
PreparedStatement存在一个强大缓存区,可以对SQL语句进行预编译。 在执行相同的SQL语句时,PreparedStatement将语句加载进缓存区,仅仅编译一次,当第二次执行此语句时不需要再次进行编译。也就是说相同的SQL语句仅仅编译一次,PreparedStatement仅对改动数据进行修改,而不再进行编译。而Statement只要语句发生了改变,则必须重新进行编译。
-
PreparedStatement支持对SQL语句使用“?”占位符,从而对SQL语句进行字段参数的修改,降低了开发难度。并且从根本上杜绝了SQL注入这个安全隐患。
-
但如果SQL语句不需要多次执行,或者“?”过多,则效率可能比Statement低。
-
1.1.1 定义接口
这里以我上一篇写的student表为关系表,创建接口,实现对student表的增删改查。
StudentIf.java
package com.test.dao;
import com.test.po.Student;
import java.util.List;
public interface StudentIf {
//1:添加一个Student
public boolean addStudent(Student student);
//2:删除一个Student
public boolean delStudentById(Integer id);
//3:根据用户名删除
public boolean delStudentByName(String name);
//4:拿取全部数据
public List<Student> queryAll();
//5:根据用户名查询
public boolean queryStudentByName(String name);
//6:根据用户名和密码查询
public Student queryStudentByNameAndPasswd(String name, String passwd);
//7:拿取总记录数(包装类)若查询出错就为null
//返回0表示没记录,使用包装类来避免歧义
public Integer queryCount();
//8:分页查询
public List<Student> queryByPage(Integer startPage, Integer pageSize);
//9:使用Statement执行器编辑用户
public boolean updateStudentBySt(Student student);
//10:使用PreparedStatement执行器编辑用户
public boolean updateStudentByPst(Student student);
}
1.1.2 实现接口
-
时间的处理:
SimpleDateFormat类中,parse()是由字符串转为Date类型;format()由Date类型转为String类型。
在向执行器setDate()存储数据到数据库时,需要将Date类型转为其子类型java.sql.Date类型,需要将java.util.Date类型的数值getTime()转换为前者;从数据库获取数据时的格式为java.sql.Date,因为java.sql.Date继承于java.util.Date,故直接获取到而不需要再转型。 -
通常,Statement的sql是在最后执行execute***()时作为参数传入的,createStatement()时为空参;而PreparedStatement的参数是在prepareStatement()时作为参数传入,最后execute***()为空参。
StudentImpl.java
package com.test.dao;
import com.test.factory.Factory;
import com.test.po.Student;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
public class StudentImpl implements StudentIf{
//与数据库的链接
Connection connection;
//执行器
Statement statement;
//执行器
PreparedStatement preparedStatement;
//当进行查询操作时的结果集
ResultSet resultSet;
//需要执行的SQL语句
String sql;
//接收结果集的集合
List<Student> stuList = new ArrayList<>();
//除更新的一个方法外,执行器使用PreparedStatement
//具有异常处理的方法,返回值至少两个
//1:添加一个Student
@Override
public boolean addStudent(Student student) {
try {
//获取连接
connection = Factory.getConnection();
//设置带有占位符?的sql语句
//仅仅支持PreparedStatement这种执行器
//JDBC的占位符索引值从1开始
sql = "insert into student values (null, ?, ?, ?, ?)";
//获取执行器,并且加载sql语句
preparedStatement = connection.prepareStatement(sql);
//填充占位符
//从左到右的顺序从,列索引从1开始
//先绑定上让preparedStatement有值
preparedStatement.setString(1, student.getName());
preparedStatement.setString(2, student.getPassWd());
preparedStatement.setInt(3, student.getSalary());
/*
* java.sql.Date与java.util.Date
* 是一对父子,util为爸爸 sql为儿子
* 爸爸:年月日小时分钟秒
* 儿子:年月日
* 通过getTime()方法可以将爸爸转换为儿子
*
* 使用java.sql接收时间类型,
* 所以需要将前台的java.date-->java.sql
* */
//
preparedStatement.setDate(4, new java.sql.Date(student.getBirthday().getTime()));
System.out.println(sql);
return preparedStatement.executeUpdate() == 1;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
Factory.close(null, preparedStatement, connection);
}
}
//2:删除一个Student
@Override
public boolean delStudentById(Integer id) {
try {
//获取连接
connection = Factory.getConnection();
sql = "delete from student where id = ?";
//获取执行器的同时加载sql语句
preparedStatement = connection.prepareStatement(sql);
//填充占位符
preparedStatement.setInt(1, id);
System.out.println(sql);
//执行dml操作
return preparedStatement.executeUpdate() == 1;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
Factory.close(null, preparedStatement, connection);
}
}
//3:根据用户名删除,删除符合条件的全部数据
@Override
public boolean delStudentByName(String name) {
try {
connection = Factory.getConnection();
sql = "delete from student where name = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, name);
System.out.println(sql);
return preparedStatement.executeUpdate() == 1;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
Factory.close(null, preparedStatement, connection);
}
}
//4:拿取全部数据
@Override
public List<Student> queryAll() {
try {
connection = Factory.getConnection();
sql = "select * from student";
preparedStatement = connection.prepareStatement(sql);
//获取结果集
resultSet = preparedStatement.executeQuery();
//获取到一条数据用if,获取多条数据使用while
while(resultSet.next()){
stuList.add(new Student(resultSet.getInt(1),
resultSet.getString(2),
resultSet.getString(3),
resultSet.getInt(4),
//获取时的格式为Date,因为java.sql.Date继承于java.util.Date,故可以接到
resultSet.getDate(5)));
}
System.out.println(sql);
return stuList;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
Factory.close(resultSet, preparedStatement, connection);
}
}
//5:根据用户名查询
@Override
public boolean queryStudentByName(String name) {
try {
connection = Factory.getConnection();
sql = "select * from student where name = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, name);
resultSet = preparedStatement.executeQuery();
System.out.println(sql);
return resultSet.next();
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
Factory.close(resultSet, preparedStatement, connection);
}
}
//6:根据用户名和密码查询
@Override
public Student queryStudentByNameAndPasswd(String name, String passwd) {
try {
connection = Factory.getConnection();
String sql = "select * from student where name = ? and passwd = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, name);
preparedStatement.setString(2, passwd);
resultSet = preparedStatement.executeQuery();
System.out.println(sql);
//获取到一条数据用if,获取多条数据使用while
if(resultSet.next()){
return new Student(resultSet.getInt(1),
resultSet.getString(2),
resultSet.getString(3),
resultSet.getInt(4),
resultSet.getDate(5));
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
Factory.close(resultSet, preparedStatement, connection);
}
}
//7:拿取总记录数(包装类)
@Override
public Integer queryCount() {
try {
connection = Factory.getConnection();
sql = "select count(*) from student";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
//这个结果集是一个两行一列的表格
//使用next()指针向下即指向记录数
/*
* ----------
* |count(*)|
* ----------
* | 记录数 |
* ----------
* */
resultSet.next();
System.out.println(sql);
return resultSet.getInt(1);
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
Factory.close(resultSet, preparedStatement, connection);
}
}
//8:分页查询
//起始页从0开始,表示第一条数据
@Override
public List<Student> queryByPage(Integer startPage, Integer pageSize) {
try {
connection = Factory.getConnection();
sql = "select * from student limit ?, ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, startPage);
preparedStatement.setInt(2, pageSize);
resultSet = preparedStatement.executeQuery();
System.out.println(sql);
//获取到一条数据用if,获取多条数据使用while
while(resultSet.next()){
stuList.add(new Student(
resultSet.getInt(1),
resultSet.getString(2),
resultSet.getString(3),
resultSet.getInt(4),
resultSet.getDate(5)
));
}
return stuList;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
Factory.close(resultSet, preparedStatement, connection);
}
}
//9:编辑用户
@Override
public boolean updateStudentBySt(Student student) {
try {
connection = Factory.getConnection();
sql = "update student set ";
if(student.getName() != null) sql += "name = '" + student.getName() + "', ";
if(student.getPassWd() != null) sql += "passwd = '" + student.getPassWd() + "', ";
if(student.getSalary() != null) sql += "salary = " + student.getSalary().toString() + ", ";
if(student.getBirthday() != null) sql += "birthday = '" + new SimpleDateFormat("yyyy-MM-dd").format(student.getBirthday()) + "', ";
//去掉最后一位的逗号,因为拼接时后面多出一个空格,所以-2
sql = sql.substring(0, sql.length() - 2);
sql += " where id = " + student.getId().toString() + ";";
statement = connection.createStatement();
System.out.println(sql);
return statement.executeUpdate(sql) == 1;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
Factory.close(null, statement, connection);
}
}
//10:使用PreparedStatement执行器编辑用户
@Override
public boolean updateStudentByPst(Student student) {
try {
connection = Factory.getConnection();
String sql = "update student set name = ?, passwd = ?, salary = ?, birthday = ? where id = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, student.getName());
preparedStatement.setString(2, student.getPassWd());
preparedStatement.setInt(3, student.getSalary());
preparedStatement.setDate(4, new java.sql.Date(student.getBirthday().getTime()));
preparedStatement.setInt(5, student.getId());
return preparedStatement.executeUpdate() == 1;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
Factory.close(null, statement, connection);
}
}
}
1.1.3 测试
编写主方法调用dao实现类中的方法来测试方法编写是否正确,建议每写一个之后就对其测试。