一、抽取JDBC工具类
在上一章中,在dao层的StudentDaoImpl.java文件中,查询全部数据、根据id查询数据、增删改数据等方法对于数据库的连接和释放存在大量的重复代码,因此可以将其中的部分代码抽出来,写到一个工具类中,需要连接释放数据库时直接调用这个工具类即可。
1.创建配置文件
首先在项目根目录下创建config.properties配置文件。配置文件中存放连接数据库所需要的信息。
要使用里面的配置信息,使用java类加载器得到文件字节流,然后使用Properties类加载流对象信息,使用getXXX()即可拿到配置信息。
2.创建JDBCUtils工具类
在项目目录中新建一个utils的目录,专门存放各类工具类,新建JDBCUtils工具类。创建静态代码块,实现加载配置文件、注册驱动等功能。静态代码块在类创建时只执行一次,在静态代码块里将config.properties里的数据读取并赋值给静态变量,然后通过静态方法进行数据库连接和释放。
public class JDBCUtils {
//1.私有构造方法
private JDBCUtils(){}
//2.声明所需配置变量
private static String driverClass;
private static String url;
private static String username;
private static String password;
private static Connection con;
//3.提供静态代码块。功能:读取配置文件,注册驱动。
static {
try {
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("config.properties");
Properties prop = new Properties();
prop.load(is);
driverClass = prop.getProperty("driverClass");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password");
//注册驱动,可不写
Class.forName(driverClass);
} catch (Exception e) {
e.printStackTrace();
}
}
//4.提供获取数据库连接的方法
public static Connection getConnection(){
try {
con = DriverManager.getConnection(url, username, password);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return con;
}
//5.提供释放资源的方法
//用于增删改
public static void close(Connection con, Statement stat, ResultSet rs){
if(con != null){
try {
con.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stat != null){
try {
stat.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
//用于查
public static void close(Connection con, Statement stat){
if(con != null){
try {
con.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(stat != null) {
try {
stat.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
这样,原有dao层的连接、释放数据库功能便被抽象出来:
Connection connection = JDBCUtils.getConnection();
JDBCUtils.close(connection, statement, resultSet);
二、解决SQL注入
JDBC里的PreparedStatement可以解决SQL注入的问题。首先在执行SQL语句之前,预编译SQL语句,此时明确了SQL语句的格式,剩下的都将被当做参数。参数使用"?"作为占位符。
- 为参数赋值的方法:setXxx(参数1,参数2);
- 参数1:?的位置编号(编号从1开始)
- 参数2:?的实际参数
- 执行sql语句的方法
- 执行insert、update、delete语句:int executeUpdate();
- 执行select语句:ResultSet executeQuery();
PreparedStatement pstm = null;
ResultSet rs = null;
String sql = "SELECT * FROM user WHERE loginname=? AND password=?";
pstm = conn.prepareStatement(sql);
//3.设置参数
pstm.setString(1,loginName);
pstm.setString(2,password);
System.out.println(sql);
//4.执行sql语句,获取结果集
rs = pstm.executeQuery();
三、事务管理
在service层进行数据库连接,开启事务及事务的提交和回滚,将connection对象传入dao层进行数据IO。
public void batchAdd(List<User> users) {
//获取数据库连接
Connection connection = JDBCUtils.getConnection();
try {
//开启事务
connection.setAutoCommit(false);
for (User user : users) {
//1.创建ID,并把UUID中的-替换
String uid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
//2.给user的uid赋值
user.setUid(uid);
//3.生成员工编号
user.setUcode(uid);
//模拟异常
//int n = 1 / 0;
//4.保存
userDao.save(connection,user);
}
//提交事务
connection.commit();
}catch (Exception e){
try {
//回滚事务
connection.rollback();
}catch (Exception ex){
ex.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(connection,null,null);
}
}