1. 项目概述
作为一名从零开始学习JavaWeb后端开发的程序员,我在Chapter13中重点记录了MySQL和MyBatis这两个核心技术的系统学习过程。这不仅是JavaWeb后端开发的基础,更是构建企业级应用的关键技术栈。
MySQL作为最流行的开源关系型数据库,在JavaWeb开发中承担着数据持久化的重任。而MyBatis作为优秀的持久层框架,完美地解决了Java对象与数据库表之间的映射问题。两者的结合使用,能够显著提升开发效率和系统性能。
2. MySQL基础与实战
2.1 MySQL安装与配置
MySQL的安装是后端开发的第一步。我选择了MySQL 8.0社区版作为学习版本,这是目前最稳定的发行版之一。安装过程中有几个关键点需要注意:
- 安装类型选择"Developer Default",这会包含开发所需的所有组件
- 在Authentication Method步骤,选择"Use Strong Password Encryption"
- 务必记住设置的root密码,这是后续操作的基础
安装完成后,建议立即配置环境变量,将MySQL的bin目录添加到系统PATH中。这样可以在任意位置使用mysql命令行工具。
注意:MySQL 8.0默认使用caching_sha2_password认证插件,如果遇到客户端连接问题,可能需要修改为mysql_native_password。
2.2 数据库设计与操作
在实际项目中,良好的数据库设计至关重要。我通过一个简单的博客系统来练习数据库设计:
sql复制CREATE DATABASE blog_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE blog_system;
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE articles (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
user_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
这个设计体现了几个重要原则:
- 使用utf8mb4字符集支持完整的Unicode字符(包括emoji)
- 为关键字段添加NOT NULL约束
- 建立适当的外键关系
- 使用时间戳记录创建和修改时间
2.3 SQL优化技巧
随着数据量增长,SQL性能优化变得尤为重要。以下是我总结的几个实用技巧:
-
索引优化:为WHERE、JOIN、ORDER BY等操作涉及的列创建索引
sql复制CREATE INDEX idx_articles_user_id ON articles(user_id); -
EXPLAIN分析:使用EXPLAIN查看SQL执行计划
sql复制EXPLAIN SELECT * FROM articles WHERE user_id = 1; -
批量操作:使用批量插入代替单条插入
sql复制INSERT INTO users (username, password, email) VALUES ('user1', 'pass1', 'user1@example.com'), ('user2', 'pass2', 'user2@example.com'); -
**避免SELECT ***:只查询需要的列
sql复制SELECT id, title FROM articles WHERE user_id = 1;
3. MyBatis核心原理与实战
3.1 MyBatis环境搭建
在JavaWeb项目中使用MyBatis需要以下步骤:
-
添加Maven依赖:
xml复制<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> -
配置mybatis-config.xml:
xml复制<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/blog_system?useSSL=false&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="yourpassword"/> </dataSource> </environment> </environments> <mappers> <mapper resource="mapper/UserMapper.xml"/> </mappers> </configuration> -
创建SqlSessionFactory:
java复制String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
3.2 MyBatis映射文件详解
MyBatis的核心在于Mapper XML文件的编写。以UserMapper.xml为例:
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.dao.UserMapper">
<resultMap id="userResultMap" type="com.example.model.User">
<id property="id" column="id"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<result property="email" column="email"/>
<result property="createdAt" column="created_at"/>
</resultMap>
<select id="selectUserById" resultMap="userResultMap">
SELECT * FROM users WHERE id = #{id}
</select>
<insert id="insertUser" parameterType="com.example.model.User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users (username, password, email)
VALUES (#{username}, #{password}, #{email})
</insert>
<update id="updateUser" parameterType="com.example.model.User">
UPDATE users SET
username = #{username},
password = #{password},
email = #{email}
WHERE id = #{id}
</update>
<delete id="deleteUser" parameterType="int">
DELETE FROM users WHERE id = #{id}
</delete>
</mapper>
这个映射文件展示了MyBatis的几个核心功能:
- 结果集映射(ResultMap)
- 参数传递(#{}语法)
- 自动生成主键(useGeneratedKeys)
- CRUD操作定义
3.3 动态SQL与高级特性
MyBatis强大的动态SQL功能可以让我们编写更加灵活的SQL:
xml复制<select id="selectUsers" parameterType="map" resultMap="userResultMap">
SELECT * FROM users
<where>
<if test="username != null">
AND username LIKE CONCAT('%', #{username}, '%')
</if>
<if test="email != null">
AND email LIKE CONCAT('%', #{email}, '%')
</if>
</where>
<if test="orderBy != null">
ORDER BY ${orderBy}
</if>
<if test="limit != null">
LIMIT #{limit}
</if>
</select>
这里使用了<where>和<if>标签构建动态查询条件。注意${}和#{}的区别:
#{}是预编译参数,防止SQL注入${}是直接替换,适用于列名等非参数场景
4. 整合应用与性能优化
4.1 MyBatis与Spring整合
现代JavaWeb项目通常使用Spring框架,MyBatis与Spring的整合非常简便:
-
添加Spring和MyBatis-Spring依赖:
xml复制<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> -
配置Spring的applicationContext.xml:
xml复制<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/blog_system?useSSL=false&serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="yourpassword"/> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.example.dao"/> </bean> -
创建DAO接口:
java复制@Repository public interface UserMapper { User selectUserById(int id); List<User> selectUsers(Map<String, Object> params); int insertUser(User user); int updateUser(User user); int deleteUser(int id); }
4.2 事务管理与性能优化
在Web应用中,事务管理至关重要。Spring提供了声明式事务管理:
java复制@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Transactional
public void createUserWithArticle(User user, Article article) {
userMapper.insertUser(user);
article.setUserId(user.getId());
articleMapper.insertArticle(article);
}
}
性能优化方面,我总结了几个关键点:
-
连接池配置:使用高性能连接池如HikariCP
xml复制<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/blog_system"/> <property name="username" value="root"/> <property name="password" value="yourpassword"/> <property name="maximumPoolSize" value="20"/> </bean> -
二级缓存:配置MyBatis二级缓存
xml复制<cache eviction="LRU" flushInterval="60000" size="512" readOnly="true"/> -
批量操作:使用BatchExecutor
java复制SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH); try { UserMapper mapper = session.getMapper(UserMapper.class); for (User user : users) { mapper.insertUser(user); } session.commit(); } finally { session.close(); }
5. 常见问题与解决方案
在实际开发中,我遇到了许多问题并找到了解决方案:
5.1 MySQL连接问题
问题:使用MySQL 8.0时出现"Public Key Retrieval is not allowed"错误。
解决方案:在连接URL中添加参数:
code复制jdbc:mysql://localhost:3306/blog_system?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=UTC
5.2 MyBatis映射问题
问题:数据库字段名是created_at,Java属性是createdAt,无法自动映射。
解决方案:
- 在SQL中使用别名:
xml复制<select id="selectUserById" resultType="com.example.model.User"> SELECT id, username, created_at as createdAt FROM users WHERE id = #{id} </select> - 配置mapUnderscoreToCamelCase:
xml复制<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
5.3 事务不回滚问题
问题:在Service方法中添加了@Transactional注解,但异常时事务没有回滚。
解决方案:
- 确保抛出的是RuntimeException或Error
- 检查是否在同一个类中调用了带有@Transactional的方法(自调用问题)
- 配置rollbackFor属性:
java复制@Transactional(rollbackFor = Exception.class)
5.4 性能瓶颈分析
问题:系统响应慢,怀疑是数据库问题。
解决方案:
- 开启MySQL慢查询日志:
sql复制SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; - 使用EXPLAIN分析慢查询
- 使用MyBatis的日志功能查看实际执行的SQL:
xml复制<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
6. 项目实战经验分享
在完成博客系统的开发后,我总结了以下几点实战经验:
-
数据库设计先行:在编码前先设计好数据库结构,考虑好表关系和索引策略。我最初没有为user_id添加索引,导致文章列表查询性能很差。
-
MyBatis Generator:对于大型项目,可以使用MyBatis Generator自动生成基础CRUD代码:
xml复制<plugin> <groupId>org.mybatis.generator</groupId> <artifactId>mybatis-generator-maven-plugin</artifactId> <version>1.4.0</version> <configuration> <configurationFile>src/main/resources/generatorConfig.xml</configurationFile> <overwrite>true</overwrite> </configuration> </plugin> -
分页处理:实现高效的分页查询:
xml复制<select id="selectArticlesByPage" resultMap="articleResultMap"> SELECT * FROM articles ORDER BY created_at DESC LIMIT #{offset}, #{pageSize} </select>对于大数据量,考虑使用"游标分页"(基于最后一条记录的ID)。
-
类型处理器:自定义类型处理器处理特殊数据类型:
java复制public class JsonTypeHandler extends BaseTypeHandler<Map<String, Object>> { @Override public void setNonNullParameter(PreparedStatement ps, int i, Map<String, Object> parameter, JdbcType jdbcType) throws SQLException { ps.setString(i, JSON.toJSONString(parameter)); } @Override public Map<String, Object> getNullableResult(ResultSet rs, String columnName) throws SQLException { return JSON.parseObject(rs.getString(columnName)); } // 其他重载方法... } -
SQL注入防护:始终使用#{}而非${}接收用户输入,对于必须使用${}的场景(如动态排序字段),要进行严格的白名单验证。
通过这个项目的实践,我深刻理解了MySQL和MyBatis在JavaWeb后端开发中的核心地位。从最初的安装配置到性能优化,再到解决各种实际问题,这个过程让我积累了宝贵的实战经验。特别是当系统从开发环境迁移到生产环境时,遇到的性能问题和解决方案让我对数据库优化有了更深的认识。
