第四十三天(JavaEE应用ORM框架SQL预编译JDBCMyBatisHibernateMaven)
#Maven配置
参考:IDEA配置maven_idea 配置maven-CSDN博客
Maven 是 Java 生态系统中广泛使用的项目管理和构建自动化工具,主要功能包括:
- 依赖管理:自动下载、管理项目所需的第三方库(JAR 包),并处理依赖之间的版本冲突,无需手动下载和复制 JAR 文件。
- 标准化项目结构:规定了统一的项目目录结构(如 src/main/java 存放源代码、src/test/java 存放测试代码等),使不同 Java 项目的结构保持一致,降低协作成本。
- 构建自动化:通过配置文件(pom.xml)定义项目的构建流程,支持编译、测试、打包(生成 JAR/WAR 等)、部署等一系列操作,可一键执行完整构建过程。
- 项目信息管理:在 pom.xml 中可维护项目的基本信息(如名称、版本、开发者等),并能与代码仓库、持续集成工具等集成。
根据参考文档配置好,测试是否正确配置环境
配置时要注意,mirror是否包含在mirrors里面,是否少了 ,如果少了,就会报错了,导致第三方库无法下载下来
#JDBC
参考:JavaEE-JDBC基础 - 简书
新建项目
选好版本和依赖
1、引用依赖(pom.xml)
https://mvnrepository.com/
选择第二个
选择一个版本,别选最新,不然可能不兼容,会报错
选择maven,然后将那段代码复制
将代码复制到里面,然后点击右侧的maven
打开之后点击和刷新很像的符号,然后要注意左侧是否将库下载进去了,如果没有就换一个MySQL存储库
新建一个软件包放服务器的东西,写一个接收id的值,然后写一个查询语句
2、注册数据库驱动
Class.forName("com.mysql.jdbc.Driver");
- MySQL 5.x 版本的驱动类名是 com.mysql.jdbc.Driver
- MySQL 8.x 版本才引入 com.mysql.cj.jdbc.Driver 这个新的驱动类名
写上注册语句,这里会报异常,直接用try ,catch 包裹就行,这里用的5.7版本的MySQL和5版本的驱动
3、建立数据库连接
String url ="jdbc:mysql://localhost:3306/phpstudy";
Connection connection=DriverManager.getConnection(url,"root","123456");
测试一下是否连接正常, 连接正常,输出乱码是因为没定义输出的格式
4、创建Statement执行SQL
Statement statement= connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
输入id值进行测试
5、结果ResultSet进行提取
while (rs.next()) {
resp.getWriter().println(rs.getString("id")); // 输出名称为id的列的值
resp.getWriter().println(rs.getString("username")); // 输出名称为username的列的值
resp.getWriter().println(rs.getString("password")); // 输出名称为password的列的值
resp.getWriter().println(rs.getString(3)); // 输出第三列的值
}
这里查询id为2 的数据,可以正常显示
安全注入例子:
预编译:PreparedStatement
安全写法(预编译): "select * from admin where id=?"
输入的sql注入语句没有生效 ,预编译中将查询语句写死了,在后面写的sql注入语句不会执行
完整代码:
@WebServlet("/jdbc")
public class JdbcServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id = req.getParameter("id");
//安全的拼接SQL语句
// String sql = "select * from admin where id = ?";
String url ="jdbc:mysql://localhost:3306/phpstudy";
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url,"root","654321");
PreparedStatement ps = conn.prepareStatement(sql); // 创建PreparedStatement对象
ps.setString(1,id); // 设置第一个参数的值
ResultSet rs = ps.executeQuery(); // 执行查询
while (rs.next()) {
resp.getWriter().println(rs.getString("id")); // 输出名称为id的列的值
resp.getWriter().println(rs.getString("username")); // 输出名称为username的列的值
resp.getWriter().println(rs.getString("password")); // 输出名称为password的列的值
resp.getWriter().println(rs.getString(3)); // 输出第三列的值
}
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
}
不安全写法(拼接): "select * from admin where id="+id
可以执行后面的sql注入语句,就可以开始注入了
完整代码
@WebServlet("/jdbc")
public class JdbcServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id = req.getParameter("id");
//不安全的拼接SQL语句,容易导致SQL注入攻击
String sql = "select * from admin where id = "+id;
String url ="jdbc:mysql://localhost:3306/phpstudy";
try {
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(url,"root","654321");
Statement statement = conn.createStatement(); // 创建Statement对象
ResultSet rs = statement.executeQuery(sql); // 执行查询
while (rs.next()) {
resp.getWriter().println(rs.getString("id")); // 输出名称为id的列的值
resp.getWriter().println(rs.getString("username")); // 输出名称为username的列的值
resp.getWriter().println(rs.getString("password")); // 输出名称为password的列的值
resp.getWriter().println(rs.getString(3)); // 输出第三列的值
}
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
}
#Hibernate
项目创建和jdbc 一样,就是项目名称改一下
这里勾选一下 hibernate ,等会就不用引用了 (但是测试时用这个依赖库时会报错,可能是后面的id什么对不上或者版本不兼容等)
1、引用依赖(pom.xml)
https://mvnrepository.com/
hibernate-core,mysql-connector-java
创建项目时勾选的引用库
自己写引用
2、Hibernate配置文件
src/main/resources/hibernate.cfg.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 数据库连接配置 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql://localhost:3306/phpstudy?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">654321</property>
<!-- 数据库方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL8Dialect</property>
<!-- 显示 SQL 语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 自动更新数据库表结构 -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 映射实体类 -->
<mapping class="com.example.hibermateddemo.entity.User"/>
</session-factory>
</hibernate-configuration>
3、映射实体类开发
用来存储获取数据:
src/main/java/com/example/entity/User.java
package com.example.hibermateddemo.entity;
import javax.persistence.*;
@Entity
@Table(name = "admin")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String username;
public User() {}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String toString() {
return "User{id=" + id + ", username=" + username + "}";
}
}
4、Hibernate工具类
用来Hibernate使用:
src/main/java/com/example/util/HibernateUtil.java
package com.example.hibermateddemo.util;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static final SessionFactory sessionFactory;
static {
try {
Configuration configuration = new Configuration().configure();
sessionFactory = configuration.buildSessionFactory();
} catch (Throwable ex) {
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
5、Servlet开发接受:
src/main/java/com/example/servlet/UserServlet.java
package com.example.hibermateddemo.servlet;
import com.example.hibermateddemo.entity.User;
import com.example.hibermateddemo.util.HibernateUtil;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.hibernate.Session;
import org.hibernate.Query;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@WebServlet("/user")
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
// 获取参数(这里假设根据用户名查询)
String username = request.getParameter("username");
// 打开 Hibernate Session
Session session = HibernateUtil.getSessionFactory().openSession();
try {
// 修改后的 HQL 语句,直接拼接字符串
// String hql = "FROM User WHERE username = '" + username + "'";
String hql = "FROM User WHERE username=:username";
// 创建查询对象
Query<User> query = session.createQuery(hql, User.class);
query.setParameter("username", username); // 设置参数值
// 执行查询
List<User> users = query.getResultList();
// 输出查询结果
out.println("<html><body>");
if (users.isEmpty()) {
out.println("<p>未找到匹配的用户。</p>");
} else {
for (User user : users) {
out.println("<p>" + user + "</p>");
}
}
out.println("</body></html>");
} catch (Exception e) {
e.printStackTrace();
out.println("<html><body><p>查询出错,请稍后重试。</p></body></html>");
} finally {
// 关闭 Session
session.close();
}
}
}
完整流程:
1. 启动阶段:Hibernate 初始化
- 加载配置文件
应用启动时,HibernateUtil类被加载,其静态代码块执行:- 通过new Configuration().configure()读取类路径下的hibernate.cfg.xml配置文件。
- 配置文件中包含:数据库连接信息(URL、用户名、密码、驱动)、Hibernate 方言、实体映射等。
- 创建 SessionFactory
- configuration.buildSessionFactory()根据配置信息创建SessionFactory(Hibernate 的核心工厂类,负责管理数据库连接池和会话)。
- SessionFactory是重量级对象,全局唯一,后续所有数据库操作都通过它创建Session。
2. 运行阶段:处理用户请求(以/user请求为例)
步骤 1:接收 HTTP 请求
- 前端通过/user?username=xxx发送 GET 请求,被@WebServlet("/user")注解的UserServlet拦截。
- doGet方法被调用,获取请求参数username(用户输入的查询条件)。
步骤 2:获取 Hibernate Session
- 通过HibernateUtil.getSessionFactory().openSession()从SessionFactory获取Session对象。
- Session是 Hibernate 与数据库交互的会话对象,类似 JDBC 的Connection,用于执行 CRUD 操作。
步骤 3:执行 HQL 查询
- 构建查询语句
- 拼接 HQL 语句:"FROM User WHERE username = '" + username + "'"(根据用户名查询User实体)。
- 创建查询对象
- 通过session.createQuery(hql, User.class)创建Query对象,指定返回结果为User类型。
- 执行查询
- 调用query.getResultList()执行查询,Hibernate 将 HQL 转换为对应数据库的 SQL(如SELECT * FROM user WHERE username = 'xxx'),并发送到数据库。
- 数据库返回结果后,Hibernate 将结果集映射为User对象列表。
步骤 4:响应查询结果
- 通过
- HTML 响应:
- 若查询结果为空,显示 “未找到匹配的用户”。
- 若有结果,遍历User列表并输出用户信息。
步骤 5:释放资源
- 在finally块中调用session.close()关闭Session,释放数据库连接资源。
安全注入例子: (修改UserServlet.java 中的语句实现效果)
安全写法:String hql = "FROM User WHERE username=:username";
输入正确的值是可以正常查询
用sqlmap 跑一下看是否存在 SQL 注入漏洞 用这个写法是不存在sql注入漏洞的
命令 : python sqlmap.py -u "http://localhost:8080/Hibermated_demo_war_exploded/user?username=xiaodi"
不安全写法:String hql = "FROM User WHERE username='"+username+"'";
这里用正确的username查询一下数据库的数据看一下
用sqlmap 跑一下看是否存在 SQL 注入漏洞 用这个写法是存在sql注入漏洞的
#MyBatis
1、引用依赖(pom.xml)
mybatis,mysql-connector-java
在pom.xml 中的 里面加入下面代码
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.17</version>
</dependency>
要确保引入的库加载到了外部库中,否则是没有成功的
2、MyBatis配置文件
src/main/resources/mybatis-config.xml 文件路径
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/phpstudy?serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="AdminMapper.xml"/>
</mappers>
</configuration>
3、AdminMapper.xml创建
src/main/resources/AdminMapper.xml 文件路径
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.jdbcdemo43.mapper.AdminMapper">
<select id="selectAdminById" resultType="com.example.jdbcdemo43.model.Admin">
SELECT * FROM admin WHERE id = #{id}
</select>
</mapper>
4、创建数据实体类
com/example/mybatisdemo43/model/Admin.java
package com.example.mybatisdemo.model;
public class Admin {
private int id;
private String username;
private String password;
// Getters and Setters
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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 "Admin{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
5、创建mapper实体类
com/example/mybatisdemo43/mapper/AdminMapper.java
package com.example.mybatisdemo.mapper;
import com.example.mybatisdemo.model.Admin;
import org.apache.ibatis.annotations.Param;
public interface AdminMapper {
Admin selectAdminById(@Param("name") String name);
}
6、创建servlet接受类
com/example/mybatisdemo43/servlet/SelectServlet.java
package com.example.mybatisdemo.servlet;
import com.example.mybatisdemo.mapper.AdminMapper;
import com.example.mybatisdemo.model.Admin;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
@WebServlet(name = "sql", value = "/sql")
public class SelectServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String name = req.getParameter("name");
if (name == null || name.isEmpty()) {
resp.getWriter().write("none");
return;
}
// 加载 MyBatis 配置文件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取 SqlSession
try (SqlSession session = sqlSessionFactory.openSession()) {
// 获取 Mapper 接口
AdminMapper mapper = session.getMapper(AdminMapper.class);
// 执行查询
Admin admin = mapper.selectAdminById(name);
// 输出结果
if (admin != null) {
resp.getWriter().println("ID: " + admin.getId());
resp.getWriter().println("用户名: " + admin.getUsername());
resp.getWriter().println("密码: " + admin.getPassword());
} else {
resp.getWriter().write("111111no");
}
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("shibai");
}
}
}
目录结构:
流程:
- mybatis-config.xml
(最先被加载)程序启动时,MyBatis 首先读取该核心配置文件,解析数据源、事务管理和 Mapper 路径等信息,为后续创建SqlSessionFactory做准备。 - SelectServlet.java(核心执行代码)
(接收请求后执行)当 Servlet 收到请求(doGet方法被调用)时:- 先加载mybatis-config.xml创建SqlSessionFactory
- 再通过SqlSessionFactory获取SqlSession
这是运行时触发查询的起点。
- AdminMapper.java
(获取代理对象时使用)通过SqlSession.getMapper(AdminMapper.class)获取接口的动态代理对象,此时 MyBatis 会关联接口与对应的映射文件。 - AdminMapper.xml
(执行 SQL 时使用)调用AdminMapper.selectAdminById()方法时,MyBatis 通过namespace+id找到该文件中定义的 SQL 语句,替换参数后执行查询。 - Admin.java
(结果返回时使用)SQL 执行完成后,MyBatis 根据resultType配置,将数据库返回的结果集自动映射为Admin对象,最终在 Servlet 中被处理并响应。
数据流向链路
请求触发 → SelectServlet加载mybatis-config.xml → 创建SqlSessionFactory → 获取SqlSession → 获取AdminMapper代理对象 → 调用方法匹配AdminMapper.xml中的SQL → 执行SQL查询 → 结果映射为Admin对象 → 响应结果
安全注入例子:
1、安全写法: select * from admin where id = #{id}
拿sqlmap跑一下看看是否有能注入 ,这里是没有的
2、不安全写法:select * from admin where id = ${id}
在AdminMapper.xml 写的查询语句是$
拿sqlmap跑一下看看是否有能注入 ,这里是有的
命令: (要进到sqlmap目录里面打开cmd输入下面命令)
python sqlmap.py -u "http://localhost:8080/mybatis_demo_war_exploded/sql?id=1"
#Spring JPA
由于涉及到开发框架,后续讲到,安全基本和Hibernate相似