零基础学Java——第九章:数据库编程(三)

第九章:数据库编程 - ORM框架(下)

在上一部分中,我们学习了ORM框架的基础知识和Hibernate框架。在这一部分中,我们将继续学习其他流行的ORM框架,包括MyBatis和Spring Data JPA。

1. MyBatis框架

1.1 MyBatis简介

MyBatis(前身是iBatis)是一款优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射。MyBatis消除了几乎所有的JDBC代码和参数的手动设置以及结果集的检索。MyBatis可以使用简单的XML或注解来配置和映射原生信息,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录。

生活中的例子:

如果说Hibernate是一位不允许你干预的全能翻译官(它会自动将对象转换为SQL),那么MyBatis则像是一位允许你提供翻译指南的翻译官。你可以告诉MyBatis:"当我说'找出所有年龄大于18岁的用户'时,请使用这个特定的SQL语句。"这样,你既享受了ORM的便利,又保留了对SQL的控制权。

1.2 MyBatis的核心组件

1.2.1 SqlSessionFactory

SqlSessionFactory是MyBatis的核心组件,它负责创建SqlSession对象。通常,应用程序中只需要一个SqlSessionFactory实例。

java 复制代码
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
1.2.2 SqlSession

SqlSession是MyBatis的主要接口,通过它可以执行SQL命令、获取映射器和管理事务。

java 复制代码
try (SqlSession session = sqlSessionFactory.openSession()) {
    // 执行SQL操作
    User user = session.selectOne("com.example.UserMapper.selectUser", 1);
    
    // 提交事务
    session.commit();
}
1.2.3 Mapper接口

Mapper接口是MyBatis中定义SQL操作的接口,通过它可以将SQL操作与Java方法关联起来。

java 复制代码
public interface UserMapper {
    User selectUser(int id);
    List<User> selectAllUsers();
    void insertUser(User user);
    void updateUser(User user);
    void deleteUser(int id);
}

1.3 MyBatis配置

1.3.1 核心配置文件

MyBatis的核心配置文件包含了影响MyBatis行为的设置和属性。

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/mydb"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="com/example/UserMapper.xml"/>
  </mappers>
</configuration>
1.3.2 映射文件

映射文件包含了SQL语句和结果映射的定义。

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.UserMapper">
  <select id="selectUser" resultType="com.example.User">
    SELECT * FROM users WHERE id = #{id}
  </select>
  
  <select id="selectAllUsers" resultType="com.example.User">
    SELECT * FROM users
  </select>
  
  <insert id="insertUser" parameterType="com.example.User">
    INSERT INTO users (username, email) VALUES (#{username}, #{email})
  </insert>
  
  <update id="updateUser" parameterType="com.example.User">
    UPDATE users SET username = #{username}, email = #{email} WHERE id = #{id}
  </update>
  
  <delete id="deleteUser" parameterType="int">
    DELETE FROM users WHERE id = #{id}
  </delete>
</mapper>

1.4 MyBatis的查询方式

1.4.1 基于XML的查询
java 复制代码
try (SqlSession session = sqlSessionFactory.openSession()) {
    // 使用命名空间和SQL ID执行查询
    User user = session.selectOne("com.example.UserMapper.selectUser", 1);
}
1.4.2 基于接口的查询
java 复制代码
try (SqlSession session = sqlSessionFactory.openSession()) {
    // 获取Mapper接口
    UserMapper userMapper = session.getMapper(UserMapper.class);
    
    // 通过接口方法执行查询
    User user = userMapper.selectUser(1);
}

1.5 MyBatis的动态SQL

MyBatis提供了强大的动态SQL功能,可以根据条件动态生成SQL语句。

xml 复制代码
<select id="findUsers" resultType="com.example.User">
  SELECT * FROM users
  <where>
    <if test="username != null">
      AND username LIKE #{username}
    </if>
    <if test="email != null">
      AND email = #{email}
    </if>
  </where>
</select>

1.6 MyBatis的缓存机制

MyBatis提供了两级缓存机制:一级缓存(SqlSession级别)和二级缓存(命名空间级别)。

1.6.1 一级缓存

一级缓存是SqlSession级别的缓存,默认开启。当我们使用同一个SqlSession多次执行相同的SQL语句时,只有第一次会访问数据库,后续的查询会直接从缓存中获取结果。

java 复制代码
try (SqlSession session = sqlSessionFactory.openSession()) {
    // 第一次查询,会访问数据库
    User user1 = session.selectOne("com.example.UserMapper.selectUser", 1);
    
    // 第二次查询,直接从一级缓存获取,不会访问数据库
    User user2 = session.selectOne("com.example.UserMapper.selectUser", 1);
}
1.6.2 二级缓存

二级缓存是命名空间级别的缓存,可以在多个SqlSession之间共享数据。需要在映射文件中显式配置才能启用。

xml 复制代码
<!-- 在映射文件中启用二级缓存 -->
<cache/>
java 复制代码
// 实体类需要实现Serializable接口
public class User implements Serializable {
    // ...
}

1.7 MyBatis与Spring的集成

MyBatis可以与Spring框架无缝集成,简化配置和使用。

xml 复制代码
<!-- 在Spring配置文件中配置MyBatis -->
<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/mydb" />
    <property name="username" value="root" />
    <property name="password" value="password" />
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mapperLocations" value="classpath:com/example/*Mapper.xml" />
</bean>

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example" />
</bean>

1.8 MyBatis最佳实践

  • 使用接口绑定:使用Mapper接口而非字符串命名空间和SQL ID,提高类型安全性
  • 合理使用动态SQL:利用动态SQL特性,减少重复代码
  • 使用ResultMap:对于复杂查询结果,使用ResultMap进行映射
  • 批量操作:对于大批量数据操作,使用批量插入、更新或删除
  • 合理配置缓存:根据应用场景合理配置和使用缓存

2. Spring Data JPA

2.1 Spring Data JPA简介

Spring Data JPA是Spring Data家族的一部分,它提供了一种简化JPA(Java Persistence API)开发的方式。Spring Data JPA通过减少数据访问层的代码量,使开发者能够更加专注于业务逻辑。

生活中的例子:

想象一下,如果Hibernate是一位需要你提供详细指令的翻译官,那么Spring Data JPA就像是一位能够理解你意图的智能助手。你只需告诉它:"我想要根据用户名查找用户",它就能自动理解并执行这个操作,而不需要你详细说明如何执行。

2.2 Spring Data JPA的核心组件

2.2.1 Repository接口

Repository是Spring Data JPA的核心接口,它提供了一组通用的数据访问方法。

java 复制代码
public interface UserRepository extends JpaRepository<User, Long> {
    // 无需实现,Spring Data JPA会自动提供基本的CRUD操作
}
2.2.2 实体类

实体类是与数据库表对应的Java类,使用JPA注解进行映射。

java 复制代码
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String username;
    private String email;
    
    // 构造函数、getter和setter方法
}

2.3 Spring Data JPA配置

2.3.1 Maven依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
2.3.2 Spring Boot配置
properties 复制代码
# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

2.4 Spring Data JPA的查询方法

2.4.1 方法名查询

Spring Data JPA支持通过方法名自动生成查询语句。

java 复制代码
public interface UserRepository extends JpaRepository<User, Long> {
    // 根据用户名查找用户
    User findByUsername(String username);
    
    // 根据邮箱查找用户
    User findByEmail(String email);
    
    // 查找用户名包含指定字符串的用户
    List<User> findByUsernameContaining(String username);
    
    // 查找用户名以指定字符串开头且邮箱以指定字符串结尾的用户
    List<User> findByUsernameStartingWithAndEmailEndingWith(String usernamePrefix, String emailSuffix);
}
2.4.2 @Query注解

对于复杂查询,可以使用@Query注解自定义查询语句。

java 复制代码
public interface UserRepository extends JpaRepository<User, Long> {
    // 使用JPQL查询
    @Query("SELECT u FROM User u WHERE u.username = :username AND u.email = :email")
    User findByUsernameAndEmail(@Param("username") String username, @Param("email") String email);
    
    // 使用原生SQL查询
    @Query(value = "SELECT * FROM users WHERE username = ?1", nativeQuery = true)
    User findByUsernameNative(String username);
    
    // 更新查询
    @Modifying
    @Query("UPDATE User u SET u.email = :email WHERE u.id = :id")
    int updateEmail(@Param("id") Long id, @Param("email") String email);
}

2.5 Spring Data JPA的分页和排序

Spring Data JPA提供了简单的分页和排序支持。

java 复制代码
// 在Repository接口中定义分页查询方法
public interface UserRepository extends JpaRepository<User, Long> {
    Page<User> findByUsernameContaining(String username, Pageable pageable);
}

// 在Service中使用分页查询
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public Page<User> findUsersByUsername(String username, int page, int size) {
        // 创建分页请求
        PageRequest pageRequest = PageRequest.of(page, size, Sort.by("username").ascending());
        
        // 执行分页查询
        return userRepository.findByUsernameContaining(username, pageRequest);
    }
}

2.6 Spring Data JPA的关联关系

2.6.1 一对一关系
java 复制代码
@Entity
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "address_id")
    private Address address;
    
    // 构造函数、getter和setter方法
}

@Entity
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String street;
    private String city;
    
    // 构造函数、getter和setter方法
}
2.6.2 一对多关系
java 复制代码
@Entity
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
    private List<Employee> employees = new ArrayList<>();
    
    // 构造函数、getter和setter方法
}

@Entity
public class Employee {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @ManyToOne
    @JoinColumn(name = "department_id")
    private Department department;
    
    // 构造函数、getter和setter方法
}
2.6.3 多对多关系
java 复制代码
@Entity
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinTable(
        name = "student_course",
        joinColumns = @JoinColumn(name = "student_id"),
        inverseJoinColumns = @JoinColumn(name = "course_id")
    )
    private Set<Course> courses = new HashSet<>();
    
    // 构造函数、getter和setter方法
}

@Entity
public class Course {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String name;
    
    @ManyToMany(mappedBy = "courses")
    private Set<Student> students = new HashSet<>();
    
    // 构造函数、getter和setter方法
}

2.7 Spring Data JPA的审计功能

Spring Data JPA提供了审计功能,可以自动记录实体的创建时间、最后修改时间、创建者和最后修改者。

java 复制代码
// 启用JPA审计
@Configuration
@EnableJpaAuditing
public class JpaConfig {
    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> Optional.of("当前用户");
    }
}

// 在实体类中使用审计注解
@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String username;
    private String email;
    
    @CreatedDate
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    private LocalDateTime updatedAt;
    
    @CreatedBy
    private String createdBy;
    
    @LastModifiedBy
    private String updatedBy;
    
    // 构造函数、getter和setter方法
}

2.8 Spring Data JPA最佳实践

  • 使用方法名查询:对于简单查询,使用方法名查询可以减少代码量
  • 使用@Query注解:对于复杂查询,使用@Query注解自定义查询语句
  • 合理使用关联关系:根据业务需求合理设计实体之间的关联关系
  • 使用分页和排序:对于大量数据,使用分页和排序提高性能
  • 启用审计功能:使用审计功能自动记录实体的变更历史

3. ORM框架对比

3.1 Hibernate vs MyBatis

特性 Hibernate MyBatis
学习曲线 较陡峭 较平缓
SQL控制 自动生成,控制较少 手动编写,控制较多
性能 对于简单查询较好 对于复杂查询较好
灵活性 较低 较高
开发效率 较高 中等
适用场景 简单CRUD操作 复杂查询和存储过程

3.2 Spring Data JPA vs Hibernate

特性 Spring Data JPA Hibernate
学习曲线 较平缓 较陡峭
代码量 较少 较多
功能丰富度 中等
灵活性 中等
开发效率 很高
适用场景 标准CRUD操作 复杂映射和查询

3.3 如何选择ORM框架

选择合适的ORM框架应考虑以下因素:

  • 项目复杂度:对于简单项目,Spring Data JPA可能是最佳选择;对于复杂项目,Hibernate或MyBatis可能更合适
  • 团队经验:考虑团队成员对各框架的熟悉程度
  • 性能要求:对于性能要求高的场景,MyBatis可能更合适
  • SQL控制:如果需要精确控制SQL,MyBatis是更好的选择
  • 开发效率:如果追求开发效率,Spring Data JPA是不错的选择

总结

在本章中,我们学习了三种主流的ORM框架:Hibernate、MyBatis和Spring Data JPA。这些框架各有特点,适用于不同的场景。

Hibernate是一个功能强大的ORM框架,它通过对象关系映射技术,使开发者能够使用面向对象的方式操作数据库,但学习曲线较陡峭。

MyBatis是一个灵活的持久层框架,它允许开发者精确控制SQL语句,适合于复杂查询和对性能要求较高的场景。

Spring Data JPA是一个简化JPA开发的框架,它通过约定大于配置的方式,大大减少了数据访问层的代码量,提高了开发效率。

在实际项目中,应根据项目需求和团队情况选择合适的ORM框架。有时候,甚至可以在同一个项目中混合使用多种框架,以发挥各自的优势。

相关推荐
纪元A梦42 分钟前
华为OD机试真题——荒岛求生(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现
java·c语言·javascript·c++·python·华为od·go
苹果酱05671 小时前
iview 表单验证问题 Select 已经选择 还是弹验证提示
java·vue.js·spring boot·mysql·课程设计
电商数据girl3 小时前
【Python爬虫电商数据采集+数据分析】采集电商平台数据信息,并做可视化演示
java·开发语言·数据库·爬虫·python·数据分析
夏季疯3 小时前
学习笔记:黑马程序员JavaWeb开发教程(2025.3.30)
java·笔记·学习
LUCIAZZZ3 小时前
简单介绍分布式定时任务XXL-JOB
java·spring boot·分布式·spring·操作系统·定时任务
bing_1583 小时前
Spring MVC Controller 方法的返回类型有哪些?
java·spring·mvc
奔驰的小野码3 小时前
SpringAI实现AI应用-内置顾问
java·人工智能·后端·spring
普通人zzz~3 小时前
SpringBoot记录用户操作日志
java·spring boot·后端
大三开学菜鸟3 小时前
记录一下spring-cloud-starter-alibaba-nacos-config 2023.0.3.2与springboot版本及配置问题
java·spring boot·后端·spring·intellij-idea