JAVAWEB知识点
——黄铭
1 JDBC
1.1 JDBC核心组件
DriverManager: 此类管理数据库驱动程序列表。使用通信协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配。
Driver:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用DriverManager对象来管理这种类型的对象。
**Connection:**该接口具有用于连接数据库的所有方法。连接对象表示通信上下文,数据库的所有通信仅通过连接对象。
Statement:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派生接口还接受参数。
**ResultSet:**在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。它作为一个迭代器,允许我们移动其数据。
**SQLException:**此类处理数据库应用程序中发生的任何异常。
1.2 JDBC初始
-
**导入JDBC驱动包:**需要下载包含数据库编程所需的JDBC的jar包
-
注册JDBC驱动程序: Class.forName(“com.mysql.jdbc.Driver”);
Driver myDriver = new com.mysql.jdbc.Driver();DriverManager.registerDriver( myDriver );
-
**创建连接:**需要使用*DriverManager.getConnection()*方法创建一个Connection对象,该对象表示与数据库的物理连接。
-
**执行查询:**需要使用类型为Statement的对象来构建和提交SQL语句到数据库。
-
**从结果集中提取数据:**需要使用相应的*ResultSet.getXXX()*方法从结果集中检索数据。
-
**释放资源:**需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。
1.3 SQL语句执行
- boolean execute(String SQL):如果可以检索到ResultSet对象,则返回一个布尔值true; 否则返回false。使用此方法执行SQL DDL语句或需要使用真正的动态SQL时。
- int executeUpdate(String SQL):返回受SQL语句执行影响的行数。使用此方法执行预期会影响多个行的SQL语句,例如INSERT,UPDATE或DELETE语句。
- ResultSet executeQuery(String SQL):返回一个ResultSet对象。当您希望获得结果集时,请使用此方法,就像使用SELECT语句一样。
1.4 案例演示
DML操作
public class JdbcDemo01 {
public static void main(String[] args) throws Exception{
ArrayList<Student> list = new ArrayList<>();
Student student = null;
// 驱动
Class.forName("com.mysql.jdbc.Driver");
// 连接
String url = "jdbc:mysql://localhost:3306/gp";
Connection conn = DriverManager.getConnection(url, "root", "1111");
if (conn != null){
System.out.println("连接成功!");
}
// 操作
String sql = "select * from student";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
// 结果集
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
int sno = resultSet.getInt("sno");
String name = resultSet.getString("name");
int age = resultSet.getInt("age");
String gender = resultSet.getString("gender");
Date birth = resultSet.getDate("birth");
String adddress = resultSet.getString("address");
student = new Student(sno,name,age,gender,birth,adddress);
list.add(student);
}
// 关闭资源
resultSet.close();
preparedStatement.clearParameters();
conn.close();
// 打印
for (Student student1 : list) {
System.out.println(student1);
}
}
}
DQL操作
public class JdbcDemo02 {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/gp", "root", "1111");
Statement stat = conn.createStatement();
//int insert = stat.executeUpdate("insert into student values(112,'xf',17,'女','1999-08-15','八百弓')",Statement.RETURN_GENERATED_KEYS);
int delete = stat.executeUpdate("delete from student where sno = '112';");
// ResultSet generatedKeys = stat.getGeneratedKeys();
// if (generatedKeys.next()){
// String sno = generatedKeys.getString(1);
// System.out.println(sno);
// }
//System.out.println(insert);
System.out.println(delete);
stat.close();
conn.close();
}
}
1.5 SQL注入
就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。
使用statment的子类preparedStatment可以防止SQL注入,该对象采用预编译,其sql语句的每个?都是一个占位符,用来赋值。
public class JdbcDemo01 {
public static void main(String[] args) throws Exception{
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.nextLine();
System.out.println("请输入密码:");
String password = scanner.nextLine();
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/gp", "root", "1111");
// Statement stat = conn.createStatement();
//
// ResultSet resultSet = stat.executeQuery("select * from user where username ='" + username + "' and password = '" + password + "';");
PreparedStatement preparedStatement = conn.prepareStatement("select * from user where username = ? and password = ?");
preparedStatement.setString(1, username);
preparedStatement.setString(2, password);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()){
System.out.println("登录成功");
}else {
System.out.println("登陆失败");
}
}
}
1.6 三层架构
1.6.1 DAO(Date Access Object)
此包中的类和接口的作用是,对数据库的表进行操作并返回结果。
1.6.2 service
此包主要处理业务逻辑,即把DAO
中得到的数据进行处理。也是开启事务的层,提交,回滚的层。
1.6.3 view
此包是对进行了业务逻辑处理后的数据展示出来,
1.7 事务
事务是一个原子操作,在同一个事务中,要么都执行成功,要么都执行失败。开启事务需要设置setAutoCommit(false)
。并且在一个事务中需要连接同一个Connection
,所以需要一个ThreadLocal
类来绑定Connection
。当一个失误中当升错误是,需要执行回滚操作。
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
//(2)获取连接
public static Connection getConnection() {
try {
//1判断线程有没有连接,第一次执行一定是null
Connection conn = threadLocal.get();
if (conn == null) {
conn = DriverManager.getConnection(url, user, password);
//2绑定到线程
threadLocal.set(conn);
System.out.println("绑定了一个连接:" + conn.hashCode());
}
return conn;
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
//与事务相关的四个方法
//1开启事务
public static void beginTransaction() throws SQLException {
Connection connection = getConnection();
if (connection != null) {
connection.setAutoCommit(false);
}
}
//2提交事务
public static void commit() throws SQLException {
Connection connection = getConnection();
if (connection != null) {
connection.commit();
}
}
//3回滚事务
public static void rollback() throws SQLException {
Connection connection = getConnection();
if (connection != null) {
connection.rollback();
}
}
//4关闭连接
public static void close() throws SQLException {
Connection connection = getConnection();
if (connection != null) {
threadLocal.remove();//解除绑定
connection.close();
//System.out.println("close解除绑定,并关闭连接"+ connection.hashCode());
}
}
1.8 优化DbUtils
在DAO层中,对于增删改查有许多冗余代码,所以可以在DbUtils工具类中封装增删改,和查的方法。
1.8.1 增删改
public static int executeUpdate(String sql, Object... params) throws SQLException {
//sql ?
Connection conn = null;
PreparedStatement pstat = null;
try {
conn = getConnection();
pstat = conn.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
pstat.setObject(i + 1, params[i]);
}
}
return pstat.executeUpdate();
} finally {
closeAll(null, pstat, conn);
}
}
1.8.2 通过RowMapper封装查询方法
RowMapper
是一个借口,此接扣的方法是T getRow(Result rs)()
,每一个实现类都是一个查询对象的操作。
public static <T> List<T> executeQuery(RowMapper rowMapper, String sql, Object... params) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = getConnection();
preparedStatement = connection.prepareStatement(sql);
if (params != null) {
for (int i = 0; i < params.length; i++) {
preparedStatement.setObject(i + 1, params[i]);
}
}
resultSet = preparedStatement.executeQuery();
List<T> list = new ArrayList<>();
while (resultSet.next()) {
T t = (T) rowMapper.getRow(resultSet);
list.add(t);
}
} catch (Exception e) {
throw new BookException("查询失败", e);
} finally {
closeAll(resultSet, preparedStatement, connection);
}
return null;
}
public interface RowMapper<T> {
T getRow(ResultSet rs);
}
public class BookRowMapper implements RowMapper<Book> {
@Override
public Book getRow(ResultSet rs) {
try {
int id = rs.getInt("id");
String _title = rs.getString("title");
String author = rs.getString("author");
Date publicDate = rs.getTimestamp("publicDate");
String publisher = rs.getString("publisher");
String isbn = rs.getString("isbn");<