数据访问:JPA关联&MyBatis

目录

1.1 JPA多表查询

1.1.1 多表连接查询

1.1.2 关联映射

单项多对一关联

双向一对多关联

1.2 Spring Boot集成MyBatis

1.2.1 回顾MyBatis

MyBatis的优点

MyBatis的缺点

MyBatis的工作流程

1.2.2 Spring Boot集成MyBatis

MyBatis-Spring-Boot-Starter

注解配置版集成

1.1 JPA多表查询

        多表查询在Spring Data JPA中有两种实现方式,第一种是创建一个结果集的接口来接收夺标连接查询后的结果,第二种是利用JPA的关联映射来实现。

1.1.1 多表连接查询

1、创建实体类(Role、User)

@Entity
@Table(name = "sys_user")
// @NamedQueries(@NamedQuery(name = "User.findUsersByName", query = "select u from User u where u.usrName = ?1"))
@Data
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "usr_id")
    private Long usrId;
    @Column(name = "usr_name")
    private String usrName;
    @Column(name = "usr_password")
    private String usrPassword;
    @Column(name = "usr_role_id")
    private Long usrRoleId;
    @Column(name = "usr_flag")
    private Integer usrFlag;


    public User(String usrName, String usrPassword, Long usrRoleId, Integer usrFlag) {
        this.usrName = usrName;
        this.usrPassword = usrPassword;
        this.usrRoleId= usrRoleId;
        this.usrFlag = usrFlag;
    }

    public User() {

    }
}
@Entity
@Table(name = "sys_role")
@Data
public class Role implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long roleId;
    @Column(name = "role_name")
    private String roleName;
    @Column(name = "role_desc")
    private String roleDesc;
    @Column(name = "role_flag")
    private Long roleFlag;

    public Role() {
    }

    public Role(String roleName, String roleDesc, Long roleFlag) {
        this.roleName = roleName;
        this.roleDesc = roleDesc;
        this.roleFlag = roleFlag;
    }

 2、创建UserInfo接口,里面提供所需数据的getter方法,其中包括用户数据和角色名称(roleName)

/**
 * UserInfo
 *
 * @author LQW
 * @since 2024/8/30
 */
public interface UserInfo {
    Long getUsrId();
    String getUsrName();
    String getUsrPassword();
    Long getUsrRoleId();
    Integer getUsrFlag();
    // 角色名称
    String getRoleName();
}

3、在UserRepository中添加查询方法,返回类型设置为UserInfo

    /**
     * 多表查询角色名称
     *
     * @param usrId
     * @return
     */
    @Query("select u.usrId as usrId,u.usrName as usrName,u.usrPassword as usrPassword, u.role.roleId as usrRoleId, u.usrFlag as usrFlag, r.roleName as roleName from User u,Role r " +
            "where u.role.roleId = r.roleId and u.usrId = ?1")
    UserInfo getUserInfo(Long usrId);

4、测试验证

    @Test
    public void testGetUserInfo() {
        UserInfo userInfo = userRepository.getUserInfo(1L);
        System.out.println("usrName:" + userInfo.getUsrName());
        System.out.println("roleName:" + userInfo.getRoleName());
    }

1.1.2 关联映射

        在软件开发中,类与类之间最普遍的关系就是关联关系,而且关联是有方向的,以角色(Role)和用户(User)为例,一个角色下有多个用户,而一个用户只能属于一个角色。

        从User到Role的关联就是多对一关联,这就意味着每个User对象只会引用一个Role对象,因此在User类中应该定义一个Role类型的属性,来引用所关联的Role对象。

        从Role到User是一对多关联,这就意味着每个Role对象会引用一组User对象,因此在Role类中应该定义一个集合类型的属性,来引用所有关联的User对象。

        如果仅有从User到Role的关联,或者仅有从Role到User的关联,就称为单向关联。如果同时包含两种关联,就称为双向关联。

        数据之间一对多或者多对一的关系,通常涉及两张表,“多”方表通过外键引用“一”方表的主键来实现一对多的关联。

        本博客结合具体的例子来介绍如何映射以下关联关系:

  • 以User和Role为例,介绍如何映射多对一单向关联映射
  • 以Role和User为例,介绍如何映射一对多双向关联映射

单项多对一关联

修改User实体类,添加关联Role对象

@Entity
@Table(name = "sys_user")
// @NamedQueries(@NamedQuery(name = "User.findUsersByName", query = "select u from User u where u.usrName = ?1"))
@Data
public class User implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "usr_id")
    private Long usrId;
    @Column(name = "usr_name")
    private String usrName;
    @Column(name = "usr_password")
    private String usrPassword;
    // @Column(name = "usr_role_id")
    // private Long usrRoleId;
    @ManyToOne(targetEntity = Role.class)
    @JoinColumn(name = "usr_role_id")
    private Role role;
    @Column(name = "usr_flag")
    private Integer usrFlag;


    public User(String usrName, String usrPassword, Role role, Integer usrFlag) {
        this.usrName = usrName;
        this.usrPassword = usrPassword;
        this.role = role;
        this.usrFlag = usrFlag;
    }

    public User() {

    }
}

2.创建UserRepository

public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

3.测试基本的CRUD

    @Test
    public void testGet() {
        User user = userRepository.findById(1L).get();
        System.out.println("usrName:" + user.getUsrName());
        System.out.println("roleName:" + user.getRole().getRoleName());
    }

双向一对多关联

1.修改Role实体类,添加关联的User对象集合

@Entity
@Table(name = "sys_role")
@Data
public class Role implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long roleId;
    @Column(name = "role_name")
    private String roleName;
    @Column(name = "role_desc")
    private String roleDesc;
    @Column(name = "role_flag")
    private Long roleFlag;

    @OneToMany(targetEntity = User.class, fetch = FetchType.EAGER, cascade = CascadeType.PERSIST, mappedBy = "role")
    private Set<User> users = new HashSet<>();

    public Role() {
    }

    public Role(String roleName, String roleDesc, Long roleFlag, Set<User> users) {
        this.roleName = roleName;
        this.roleDesc = roleDesc;
        this.roleFlag = roleFlag;
        this.users = users;
    }

    public Role(String roleName, String roleDesc, Long roleFlag) {
        this.roleName = roleName;
        this.roleDesc = roleDesc;
        this.roleFlag = roleFlag;
    }

2.创建RoleRepository

public interface RoleRepository extends JpaRepository<Role,Long> {
}

3.测试查询

    @Test
    public void testGet() {
        Role role = roleRepository.findById(1L).get();
        System.out.println("roleName:" + role.getRoleName());
    }

4.测试级联操作:级联新增

    @Test
    public void testAdd() {
        Role role = new Role("测试角色", "演示级联新增角色和用户", 1L);
        User user1 = new User("测试用户1", "123456", role, 1);
        User user2 = new User("测试用户2", "123456", role, 1);
        role.getUsers().add(user1);
        role.getUsers().add(user2);
        roleRepository.save(role);
    }

5.测试级联操作:级联删除

   @Test
    public void testDelete() {
        Role role = roleRepository.getOne(33L);
        roleRepository.delete(role);
    }

1.2 Spring Boot集成MyBatis

1.2.1 回顾MyBatis

MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集的需要。MyBatis使用简单的XML或注解用于配置和原始映射,将接口和Java的POJOs(Plain Old Java Objects, 普通的Java对象)映射成数据库中的记录。

MyBatis的优点
  1. 灵活性高:MyBatis允许开发人员使用XML或注解来编写SQL语句,提供了较大的灵活性。开发人员可以根据业务需求进行定制化开发,而不必受到框架的过多限制。

  2. 性能优秀:MyBatis是一个轻量级的框架,执行速度快,并极大降低了内存使用需求。它采用轻量级的JDBC操作来进行数据库交互,从而保证了高效的性能。

  3. 易于学习和使用:MyBatis的API简单易懂,学习曲线较低,适合初学者使用。通过文档和源代码,开发人员可以比较容易地掌握它的设计思路和实现。

  4. 易于扩展:MyBatis提供了插件机制,可以方便地扩展框架的功能。开发人员可以根据自身业务量创建插件,以满足不同的业务需求。

  5. 易于与其他框架整合:MyBatis可以与Spring等框架无缝整合,提高开发效率。同时,它也支持多种数据库,如MySQL、Oracle、PostgreSQL等。

  6. 良好的可读性:MyBatis的SQL映射文件非常容易理解和修改,它们可以以更符合人类阅读习惯的方式呈现。开发人员可以使用注释、内置的逻辑标签和条件语句等方式,使代码更加清晰易懂。

  7. 提供丰富的映射标签:MyBatis支持对象与数据库的ORM字段关系映射,也支持对象关系组建维护。同时,它还提供了xml标签,支持编写动态SQL,这些都极大地提高了开发的便利性和效率。

MyBatis的缺点
  1. 需要手动编写SQL语句:相比于Hibernate等ORM框架,MyBatis需要开发人员手动编写SQL语句。对于不擅长SQL的开发人员来说,可能会增加开发成本。

  2. SQL映射复杂:虽然MyBatis提供了灵活的SQL映射方式,但这也意味着需要开发者自己解决一些查询语句上的问题。在复杂场景下,可能需要较高的SQL能力才能完成正确的映射。

  3. XML配置维护困难:MyBatis的配置文件需要通过XML来进行书写,这种方式与Java代码相比可能不够直观。随着项目规模的增大,配置文件可能会变得十分庞大且难以维护。

  4. 编码效率较低:由于需要手动编写SQL查询语句和参数映射,MyBatis对开发人员的编码技术要求较高。这可能会增加开发工作量并降低开发效率。

  5. 缺乏自动化的数据库操作机制:MyBatis没有提供自动化的数据库操作机制,开发人员需要手动处理对象和数据库之间的映射关系。这可能会增加开发的复杂性和出错的可能性。

  6. 社区和文档支持相对较弱:尽管MyBatis是一个流行的ORM框架,但与其他一些框架相比,其中文资料较少且不清晰,同时社区活跃度也相对较低。这可能会给开发者在使用过程中带来一些不便。

MyBatis的工作流程

1. 加载配置文件

MyBatis首先会加载全局配置文件(如mybatis-config.xml),这个文件包含了MyBatis的运行环境信息,如数据源、事务管理器、类型别名、插件等配置。同时,MyBatis还会加载SQL映射文件(如mapper.xml),这些文件配置了与数据库操作相关的SQL语句和映射规则。

2. 构造会话工厂

通过读取配置文件的信息,MyBatis会构造出一个会话工厂(SqlSessionFactory)。这个工厂是创建会话(SqlSession)的工厂类,它包含了数据库连接池等重要的数据库连接信息。

3. 创建会话

通过会话工厂,MyBatis可以创建出会话对象(SqlSession)。SqlSession是MyBatis的核心接口,它提供了执行SQL命令所需的所有方法,如增删改查等。每个线程都应该有它自己的SqlSession实例,因为SqlSession不是线程安全的。

4. 创建执行器

虽然SqlSession提供了数据库操作的方法,但它本身并不直接操作数据库。相反,它使用了一个叫做执行器(Executor)的接口来执行数据库操作。执行器是MyBatis的核心,它负责SQL语句的生成和查询缓存的维护。MyBatis提供了多种执行器实现,如SimpleExecutorReuseExecutorBatchExecutor等,以满足不同的执行需求。

5. 封装SQL对象

在执行SQL语句之前,MyBatis会将待处理的SQL信息封装到一个MappedStatement对象中。这个对象包含了SQL语句、输入参数映射信息(如Java简单类型、HashMap或POJO)和输出结果映射信息(如Java简单类型、HashMap或POJO)。这样,MyBatis就可以根据这些信息来动态地生成SQL语句,并执行它。

6. 操作数据库

拥有了执行器和SQL信息封装对象后,MyBatis就可以使用它们来访问数据库了。执行器会根据MappedStatement对象中的信息来生成SQL语句,并设置参数,然后执行这个SQL语句。执行完成后,MyBatis还会根据结果映射信息将结果集映射成Java对象,并返回给调用者。

7. 释放资源

在操作完成后,MyBatis会释放数据库连接等资源,以确保资源的有效利用和避免资源泄露。

1.2.2 Spring Boot集成MyBatis

MyBatis-Spring-Boot-Starter

1.创建项目

 

2.pom.xml文件配置依赖项

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.18</version>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <version>3.3.3</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>

3.application.properties 配置相关信息

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/crm_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC  
spring.datasource.username=root
spring.datasource.password=luoqiangwu

mybatis.config-location=classpath:mybatis/mybatis-config.xml
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
mybatis.type-aliases-package=com.bdqn.mybatis_spring_boot.entity

logging.level.root=warn
logging.level.com.bdqn.mybatis_spring_boot.mapper=trace
logging.pattern.console=%p%m%n

4.启动类

@SpringBootApplication
@MapperScan("com.bdqn.mybatis_spring_boot.mapper")
public class MyBatisSpringBootApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyBatisSpringBootApplication.class, args);
    }

}

5.编码

配置一些全局属性

    <!-- 全局配置文件 -->
    <settings>
        <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true" />
        <!-- 开启控制台日志 -->
        <setting name="logImpl" value="STDOUT_LOGGING" />
        <setting name="lazyLoadingEnabled" value="true" />
        <!--全自动映射级别-->
        <setting name="autoMappingBehavior" value="FULL" />
    </settings>

编写Mapper接口

public interface UserMapper {
    void insert(User user);
    void delete(Long id);
    void update(User user);
    User get(Long id);
    List<User> findAll();
}

编写UserMapper.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.bdqn.mybatis_spring_boot.mapper.UserMapper">

    <resultMap id="UserResultMap" type="com.bdqn.mybatis_spring_boot.entity.User">
        <id column="usr_id" property="usrId" />
        <result column="usr_name" property="usrName" />
        <result column="usr_password" property="usrPassword" />
        <result column="usr_role_id" property="usrRoleId" />
        <result column="usr_flag" property="usrFlag" />
    </resultMap>

    <sql id="columns">
        usr_id,usr_name,usr_password,usr_role_id,usr_flag
    </sql>

    <insert id="insert">
        insert into sys_user(usr_name, usr_password, usr_role_id, usr_flag)
        values (#{usrName}, #{usrPassword}, #{usrRoleId}, #{usrFlag})
    </insert>
    <update id="update">
        update sys_user
        <set>
            <if test="usrName != null">usr_name = #{usrName},</if>
            <if test="usrPassword != null">usr_password = #{usrPassword},</if>
            <if test="usrRoleId != null">usr_role_id = #{usrRoleId},</if>
            <if test="usrFlag != null">usr_flag = #{usrFlag},</if>
        </set>
        where usr_id = #{usrId}
    </update>
    <delete id="delete">
        delete
        from sys_user
        where usr_id = #{usrId}
    </delete>
    <select id="get" resultMap="UserResultMap">
        select
        <include refid="columns" />
        from sys_user
        where usr_id = #{usrId}
    </select>
    <select id="findAll" resultMap="UserResultMap">
        select
        <include refid="columns" />
        from sys_user
    </select>
</mapper>

6.测试

@SpringBootTest
public class UserMapperTester {

    @Resource
    private UserMapper userMapper;

    @Test
    public void testInsert(){
        userMapper.insert(new User("ktjiaoyu","123453",2L,1));
    }

    @Test
    public void testGet(){
        User user = userMapper.get(2L);
        System.out.println("usrName:"+user.getUsrName());
    }

    @Test
    public void testDelete(){
        userMapper.delete(88L);
    }

    @Test
    public void testUpdate(){
        userMapper.update(new User(90L,"JPA","12345",1L,1));
    }

    @Test
    public void testSelect(){
        List<User> all = userMapper.findAll();
        for (User user : all){
            System.out.println(user);
        }
    }
}
注解配置版集成

给Mapper接口内添加注解

public interface UserMapper {

    @Insert("insert into sys_user(usr_name, usr_password, usr_role_id, usr_flag) values (#{usrName}, #{usrPassword}, #{usrRoleId}, #{usrFlag})")
    void insert(User user);

    @Delete("delete \n" +
            "        from sys_user \n" +
            "        where usr_id = #{usrId}")
    void delete(Long id);

    @Update("<script>" + "update sys_user \n" +
            "        <set> \n" +
            "            <if test=\"usrName != null\">usr_name = #{usrName},</if> \n" +
            "            <if test=\"usrPassword != null\">usr_password = #{usrPassword},</if> \n" +
            "            <if test=\"usrRoleId != null\">usr_role_id = #{usrRoleId},</if> \n" +
            "            <if test=\"usrFlag != null\">usr_flag = #{usrFlag},</if> \n" +
            "        </set> \n" +
            "        where usr_id = #{usrId}" +
            "</script>")
    void update(User user);
    //
    @Select("select \n" +
            "         usr_id,usr_name,usr_password,usr_role_id,usr_flag\n" +
            "        from sys_user \n" +
            "        where usr_id = #{usrId}")
    @Results({
            @Result(column = "usr_id", property = "usrId"),
            @Result(column = "usr_name", property = "usrName"),
            @Result(column = "usr_password", property = "usrPassword"),
            @Result(column = "usr_role_id", property = "usrRoleId"),
            @Result(column = "usr_flag", property = "usrFlag")
    })
    User get(Long id);

    @Select("select \n" +
            "        usr_id,usr_name,usr_password,usr_role_id,usr_flag \n" +
            "        from sys_user")
    @Results({
            @Result(column = "usr_id", property = "usrId"),
            @Result(column = "usr_name", property = "usrName"),
            @Result(column = "usr_password", property = "usrPassword"),
            @Result(column = "usr_role_id", property = "usrRoleId"),
            @Result(column = "usr_flag", property = "usrFlag")
    })
    List<User> findAll();

}

(注意:使用注解配置版集成要先把UserMapper.xml里面的sql注释才能运行成功,不然会报错!!!)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值