数据访问层注解

JPA/Hibernate 核心注解(实体映射)

JPA(Java Persistence API)是 Java 的 ORM 标准,Hibernate 是其最主流的实现,这类注解主要用于将 Java 实体类映射到数据库表 / 列,定义实体关系。

基础映射注解

注解 核心作用 关键参数 / 示例
@Entity 标记类为 JPA 实体,对应数据库中的一张表(必须配合@Id使用) @Entity public class User { ... }
@Table 自定义实体对应的数据库表信息(省略时表名默认与类名一致) @Table(name = "t_user", schema = "test")(表名 t_user,库名 test)
@Id 标记字段为实体的主键(必须) @Id private Long id;
@GeneratedValue 指定主键生成策略(自增、UUID 等) @GeneratedValue(strategy = GenerationType.IDENTITY)(MySQL 自增)
@Column 自定义字段对应的数据库列信息(省略时列名默认与字段名一致) @Column(name = "user_name", length = 50, nullable = false)(列名 user_name,长度 50

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 实体类 → 数据库t_user表
@Entity
@Table(name = "t_user", indexes = {@Index(name = "idx_name", columnList = "user_name")}) // 加索引
public class User {
// 主键,MySQL自增策略
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

// 用户名,列名user_name,非空,长度50,唯一
@Column(name = "user_name", nullable = false, length = 50, unique = true)
private String userName;

// 年龄,列名age,默认值18(数据库层面)
@Column(name = "age", columnDefinition = "int default 18")
private Integer age;

// 邮箱,可空,长度100
@Column(name = "email", length = 100, nullable = true)
private String email;

// 无参构造器(JPA必须)、getter/setter/toString 省略
}

主键生成策略说明

策略 适用场景
IDENTITY MySQL、PostgreSQL 等支持自增列的数据库(推荐)
SEQUENCE Oracle、DB2 等支持序列的数据库(需配合@SequenceGenerator)
TABLE 所有数据库(通过中间表生成主键,性能差,不推荐)
AUTO 由 JPA 自动选择策略(跨数据库时用)

关联关系注解(一对一 / 一对多 / 多对多)
用于定义实体间的关联关系(对应数据库的外键),核心是维护对象模型与关系模型的映射。

注解 作用 核心参数
@OneToOne 一对一关联(如 User ↔ UserDetail)
@OneToMany 一对多关联(如 User ↔ Order)
@ManyToOne 多对一关联(如 Order ↔ User)
@ManyToMany 多对多关联(如 User ↔ Role)

示例 1:一对多(User ↔ Order)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 一方(User)
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String userName;

// 一对多关联Order,mappedBy指定Order中的user字段(非主控方),懒加载
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Order> orders = new ArrayList<>();

// getter/setter 省略
}

// 多方(Order)
@Entity
@Table(name = "t_order")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String orderNo;

// 多对一关联User,主控方(维护外键t_order.user_id)
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "user_id") // 外键列名user_id
private User user;

// getter/setter 省略
}

示例 2:多对多(User ↔ Role)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// User实体
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String userName;

// 多对多关联Role,指定中间表t_user_role
@ManyToMany
@JoinTable(
name = "t_user_role", // 中间表名
joinColumns = @JoinColumn(name = "user_id"), // 当前实体外键
inverseJoinColumns = @JoinColumn(name = "role_id") // 关联实体外键
)
private Set<Role> roles = new HashSet<>();

// getter/setter 省略
}

// Role实体
@Entity
@Table(name = "t_role")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String roleName;

// 反向关联(可选)
@ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>();

// getter/setter 省略
}

Spring Data JPA 注解(数据访问层)

@Repository - 数据访问层标
作用:标记接口 / 类为数据访问层(DAO) 组件,Spring 会自动扫描并注册为 Bean,同时会将数据访问异常转换为 Spring 统一的DataAccessException。

举例:

1
2
3
4
5
6
// 继承JpaRepository获得CRUD功能,@Repository可省略(Spring Data JPA自动识别)
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 自定义查询方法(基于方法名解析)
User findByUserName(String userName);
}

注意:

  • Spring Data JPA 的 Repository 接口(如JpaRepository/CrudRepository)无需手动加@Repository,Spring 会自动识别;
  • 非 Spring Data JPA 的自定义 DAO 实现类,需加@Repository让 Spring 扫描。

@Query - 自定义 JPQL/SQL 查询
作用:在 Repository 方法上自定义查询语句(JPQL 或原生 SQL),替代 “方法名解析” 的局限性,支持动态参数。

关键参数

参数 作用
value 查询语句(JPQL 或 SQL)
nativeQuery 是否为原生 SQL(默认 false,JPQL)
countQuery 分页查询时的总数查询语句

示例 1:JPQL 查询(面向实体)

1
2
3
4
5
6
7
8
public interface UserRepository extends JpaRepository<User, Long> {
// 自定义JPQL查询,参数用:变量名
@Query("SELECT u FROM User u WHERE u.userName LIKE %:name% AND u.age > :age")
List<User> findByUserNameLikeAndAgeGreaterThan(@Param("name") String name, @Param("age") Integer age);

// 示例:查询用户名包含"张"且年龄>20的用户
// 调用:userRepository.findByUserNameLikeAndAgeGreaterThan("张", 20);
}

示例 2:原生 SQL 查询(面向表)

1
2
3
4
5
6
7
8
9
10
11
public interface UserRepository extends JpaRepository<User, Long> {
// 原生SQL,nativeQuery=true
@Query(value = "SELECT * FROM t_user WHERE email = ?1", nativeQuery = true)
User findByEmail(String email); // ?1对应第一个参数

// 分页查询(配合Pageable)
@Query(value = "SELECT * FROM t_user WHERE age BETWEEN ?1 AND ?2",
countQuery = "SELECT COUNT(*) FROM t_user WHERE age BETWEEN ?1 AND ?2",
nativeQuery = true)
Page<User> findByAgeBetween(Integer minAge, Integer maxAge, Pageable pageable);
}

@Modifying - 标记修改型查询
作用:配合@Query使用,标记该查询是增 / 删 / 改操作(非查询),需配合@Transactional使用。

举例:

1
2
3
4
5
6
7
8
9
10
11
public interface UserRepository extends JpaRepository<User, Long> {
// 修改操作,必须加@Modifying和@Transactional
@Modifying
@Query("UPDATE User u SET u.age = :age WHERE u.id = :id")
int updateAgeById(@Param("id") Long id, @Param("age") Integer age);

// 删除操作
@Modifying
@Query("DELETE FROM User u WHERE u.userName = :name")
void deleteByUserName(@Param("name") String name);
}

注意

  • 返回值:增删改操作可返回int(受影响行数)或void;
  • 必须在方法或类上添加@Transactional,否则抛TransactionRequiredException。

@Transactional - 事务管理
作用:标记方法 / 类需要事务支持,Spring 会自动为该方法开启、提交 / 回滚事务,是数据操作的核心注解。

关键参数

参数 作用 默认值
readOnly 是否为只读事务(查询操作建议设为 true,优化性能) false
rollbackFor 指定触发回滚的异常类型(默认仅 RuntimeException 回滚)
propagation 事务传播行为(如 REQUIRED、REQUIRES_NEW) REQUIRED
isolation 事务隔离级别(如 READ_COMMITTED、REPEATABLE_READ) 数据库默认

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Service
public class UserService {
@Autowired
private UserRepository userRepository;

// 只读事务(查询)
@Transactional(readOnly = true)
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}

// 写事务,发生任何Exception都回滚
@Transactional(rollbackFor = Exception.class)
public void updateUser(User user) {
userRepository.save(user);
// 若抛异常,事务回滚
if (user.getAge() < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
}
}

MyBatis 核心注解(数据访问)

@Mapper - MyBatis 映射器
作用:标记接口为MyBatis Mapper 接口,MyBatis 会动态生成该接口的实现类,Spring 可自动扫描并注入。

两种启用方式

  • 方式 1:每个 Mapper 接口加@Mapper
  • 方式 2:主启动类加@MapperScan(“com.example.demo.mapper”)(批量扫描,推荐)。

举例1:

1
2
3
4
5
6
7
// 标记为MyBatis Mapper
@Mapper
public interface UserMapper {
// 自定义SQL注解
@Select("SELECT * FROM t_user WHERE id = #{id}")
User selectById(Long id);
}

举例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 扫描指定包下的所有Mapper接口
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

// Mapper接口无需加@Mapper
public interface UserMapper {
@Select("SELECT * FROM t_user WHERE id = #{id}")
User selectById(Long id);
}

@Select/@Insert/@Update/@Delete - SQL 注解
作用:直接在 Mapper 方法上定义查询 / 插入 / 更新 / 删除的 SQL 语句,替代 XML 中的

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Mapper
public interface UserMapper {
// 查询:根据ID查用户
@Select("SELECT id, user_name as userName, age, email FROM t_user WHERE id = #{id}")
User selectById(Long id);

// 插入:新增用户,返回自增主键
@Insert("INSERT INTO t_user(user_name, age, email) VALUES (#{userName}, #{age}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id") // 自增主键回填到实体id字段
int insert(User user);

// 更新:修改用户信息
@Update("UPDATE t_user SET user_name = #{userName}, age = #{age} WHERE id = #{id}")
int update(User user);

// 删除:根据ID删用户
@Delete("DELETE FROM t_user WHERE id = #{id}")
int deleteById(Long id);

// 动态参数:多条件查询(配合@Param)
@Select("SELECT * FROM t_user WHERE user_name LIKE CONCAT('%', #{name}, '%') AND age > #{age}")
List<User> selectByCondition(@Param("name") String name, @Param("age") Integer age);
}