Java案例如何实现一对一查询?

wen java案例 27

本文目录导读:

Java案例如何实现一对一查询?

  1. 使用 MyBatis 实现一对一查询
  2. 使用 JPA/Hibernate 实现一对一查询
  3. 纯 JDBC 手动实现(适合简单场景)
  4. 三种方式对比
  5. 注意事项

在Java中实现一对一查询(One-to-One Relationship)通常有两种常见方式:ORM框架(如MyBatis或JPA/Hibernate)纯JDBC手动关联,下面分别给出案例。


使用 MyBatis 实现一对一查询

假设有两张表:user(用户)和 id_card(身份证),每个用户对应一张身份证。

数据库表结构

CREATE TABLE user (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50)
);
CREATE TABLE id_card (
    id INT PRIMARY KEY AUTO_INCREMENT,
    card_number VARCHAR(18) UNIQUE,
    user_id INT UNIQUE,   -- 一对一关键:唯一约束
    FOREIGN KEY (user_id) REFERENCES user(id)
);

Java实体类

public class User {
    private int id;
    private String name;
    private IdCard idCard;  // 一对一关联
    // getter/setter
}
public class IdCard {
    private int id;
    private String cardNumber;
    private int userId;
    // getter/setter
}

MyBatis Mapper(XML方式)

UserMapper.xml

<mapper namespace="com.example.mapper.UserMapper">
    <!-- 方式1:嵌套结果(推荐,性能较好) -->
    <select id="getUserWithIdCard" resultMap="userWithCardMap">
        SELECT 
            u.id, u.name,
            c.id as card_id, c.card_number, c.user_id
        FROM user u
        LEFT JOIN id_card c ON u.id = c.user_id
        WHERE u.id = #{id}
    </select>
    <resultMap id="userWithCardMap" type="User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <!-- 一对一关联:使用 association -->
        <association property="idCard" javaType="IdCard">
            <id property="id" column="card_id"/>
            <result property="cardNumber" column="card_number"/>
            <result property="userId" column="user_id"/>
        </association>
    </resultMap>
    <!-- 方式2:嵌套查询(N+1问题风险,谨慎使用) -->
    <select id="getUserWithIdCardByNested" resultMap="userWithCardNestedMap">
        SELECT * FROM user WHERE id = #{id}
    </select>
    <resultMap id="userWithCardNestedMap" type="User">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <association property="idCard" column="id" 
                     select="com.example.mapper.IdCardMapper.findById"/>
    </resultMap>
</mapper>

调用示例

User user = userMapper.getUserWithIdCard(1);
System.out.println(user.getName() + " - " + user.getIdCard().getCardNumber());

使用 JPA/Hibernate 实现一对一查询

实体类注解

@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    private String name;
    @OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
    private IdCard idCard;
    // getter/setter
}
@Entity
@Table(name = "id_card")
public class IdCard {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column(name = "card_number")
    private String cardNumber;
    @OneToOne
    @JoinColumn(name = "user_id", unique = true)
    private User user;
    // getter/setter
}

Repository查询

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
    // 自动支持延迟加载,可直接调用
}
// 使用
User user = userRepository.findById(1).orElse(null);
System.out.println(user.getIdCard().getCardNumber());  // 自动加载身份证

纯 JDBC 手动实现(适合简单场景)

public User getUserWithIdCard(int userId) {
    String sql = "SELECT u.*, c.* FROM user u LEFT JOIN id_card c ON u.id = c.user_id WHERE u.id = ?";
    User user = null;
    try (Connection conn = dataSource.getConnection();
         PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setInt(1, userId);
        try (ResultSet rs = ps.executeQuery()) {
            if (rs.next()) {
                user = new User();
                user.setId(rs.getInt("id"));
                user.setName(rs.getString("name"));
                IdCard card = new IdCard();
                card.setId(rs.getInt("c.id"));
                card.setCardNumber(rs.getString("card_number"));
                card.setUserId(rs.getInt("user_id"));
                user.setIdCard(card);
            }
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return user;
}

三种方式对比

方式 优点 缺点 适用场景
MyBatis 灵活控制SQL,性能可控 需要写XML映射 复杂查询、SQL优化敏感
JPA/Hibernate 开发快,自动维护关联 学习曲线略高,延迟加载可能N+1 标准CRUD、快速开发
纯JDBC 无框架依赖 代码重复多,难维护 极简单需求或框架限制

注意事项

  1. 数据库层面:一对一的本质是 外键 + 唯一约束(或主键即外键)
  2. 性能考虑
    • MyBatis嵌套结果(JOIN一次)优于嵌套查询(多次查询)
    • JPA建议使用 @EntityGraphJOIN FETCH 避免N+1
  3. 双向关联:避免JSON序列化循环,使用 @JsonIgnore 或DTO

选择哪种方式取决于项目的技术栈和具体需求,大多数Java项目会用 MyBatisJPA,很少再用纯JDBC。

抱歉,评论功能暂时关闭!