SpringJPA入门学习(三)之多表查询

本文详细介绍了ORM概念及SpringData JPA在一对多和多对多关系中的应用,包括实体类定义、数据库配置、DAO层接口设计及测试案例,展示了如何通过注解配置表关系、中间表和外键,实现数据的级联操作。

一、一对多查询

一的一方:主表
多的一方:从表
外键:需要再从表上新建一列作为外键,他的取值来源于主表的主键
实体类中的关系
包含关系:可以通过实体类中的包含关系描述表关系
继承关系
applicationContext.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/data/jpa
		http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    <!-- spring 和 Spring data jpa的配置-->
    <!-- 1创建entityManagerFactory对象交给spring容器管理   -->
    <bean id="entityMangerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <!--  配置的扫描的包(实体类所在的包)-->
        <property name="packagesToScan" value="nju.software.domain"></property>
        <!--        jpa的实现厂家-->
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
        </property>
        <!--JPA的供应商适配器-->
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <!-- 配置是否自动创建表-->
                <property name="generateDdl" value="false"></property>
                <!-- 指定数据库类型-->
                <property name="database" value="MYSQL"></property>
                <!--数据库方言:支持的特有语法-->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>
                <!--是否显示sql-->
                <property name="showSql" value="true"></property>
            </bean>
        </property>
        <!--jpa的方言:高级的特性-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
        </property>
        <!--注入jpa的配置信息
              加载jpa的基本配置信息和jpa实现方式(hibernate)的配置信息
              hibernate.hbm2ddl.auto : 自动创建数据库表
                  create : 每次都会重新创建数据库表
                  update:有表不会重新创建,没有表会重新创建表
          -->
        <property name="jpaProperties" >
            <props>
                <prop key="hibernate.hbm2ddl.auto">create</prop>
            </props>
        </property>
    </bean>
    <!--2.创建数据库连接池-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///jpa"></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    </bean>

    <!--3整合spring datajpa-->
    <jpa:repositories base-package="nju.software.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityMangerFactory"></jpa:repositories>
    <!--4.配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityMangerFactory"></property>
    </bean>
    <!--5.声明式事务-->
    <!--6.配置包扫描(扫描注解)-->
    <context:component-scan base-package="nju.software"></context:component-scan>
</beans>

实体类Customer

package nju.software.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

/**
 * 1.实体类和表的关系
 *      @Entity
 *      @Table
 * 2.类中属性和表中字段的映射关系
 *      @Id
 *      @GeneratedValue
 *      @Colum
 */
@Entity
@Table(name = "cst_customer")
public class Customer {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "cust_id")
    private Long custId; //客户的主键
    @Column(name = "cust_name")
    private String custName;//客户名称
    @Column(name = "cust_source")
    private String custSource;//客户来源
    @Column(name="cust_level")
    private String custLevel;//客户级别

    @Column(name="cust_industry")
    private String custIndustry;//客户所属行业

    @Column(name="cust_phone")
    private String custPhone;//客户的联系方式

    @Column(name="cust_address")
    private String custAddress;//客户地址

    /**
     * 使用注解的形式配置多表关系
     *      1.声明关系
     *          @OneToMany : 配置一对多关系
     *              targetEntity :对方对象的字节码对象
     *      2.配置外键(中间表)
     *              @JoinColumn : 配置外键
     *                  name:外键字段名称(数据库中)
     *                  referencedColumnName:参照的主表的主键字段名称数据库中)
     *
     *  * 在客户实体类上(一的一方)添加了外键了配置,所以对于客户而言,也具备了维护外键的作用
     *
     */
//    @OneToMany(targetEntity = LinkMan.class)
//    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    /**
     * 放弃外键维护权
     *      mappedBy:对方配置关系的属性名称\
     * cascade : 配置级联(可以配置到设置多表的映射关系的注解上)
     *      CascadeType.all         : 所有
     *                  MERGE       :更新
     *                  PERSIST     :保存
     *                  REMOVE      :删除
     *
     * fetch : 配置关联对象的加载方式
     *          EAGER   :立即加载
     *          LAZY    :延迟加载

     */
            //这个customer指的是LinkMan中的customer引用
     @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL)
    private Set<LinkMan> linkMans = new HashSet<>();
    public Set<LinkMan> getLinkMans() {
        return linkMans;
    }

    public void setLinkMans(Set<LinkMan> linkMans) {
        this.linkMans = linkMans;
    }

    public Long getCustId() {
        return custId;
    }

    public void setCustId(Long custId) {
        this.custId = custId;
    }

    public String getCustName() {
        return custName;
    }

    public void setCustName(String custName) {
        this.custName = custName;
    }

    public String getCustSource() {
        return custSource;
    }

    public void setCustSource(String custSource) {
        this.custSource = custSource;
    }

    public String getCustLevel() {
        return custLevel;
    }

    public void setCustLevel(String custLevel) {
        this.custLevel = custLevel;
    }

    public String getCustIndustry() {
        return custIndustry;
    }

    public void setCustIndustry(String custIndustry) {
        this.custIndustry = custIndustry;
    }

    public String getCustPhone() {
        return custPhone;
    }

    public void setCustPhone(String custPhone) {
        this.custPhone = custPhone;
    }

    public String getCustAddress() {
        return custAddress;
    }

    public void setCustAddress(String custAddress) {
        this.custAddress = custAddress;
    }
}

实体类LinkMan

package nju.software.domain;

import javax.persistence.*;

@Entity
@Table(name = "cst_linkman")
public class LinkMan {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "lkm_id")
    private Long lkmId; //联系人编号(主键)
    @Column(name = "lkm_name")
    private String lkmName;//联系人姓名
    @Column(name = "lkm_gender")
    private String lkmGender;//联系人性别
    @Column(name = "lkm_phone")
    private String lkmPhone;//联系人办公电话
    @Column(name = "lkm_mobile")
    private String lkmMobile;//联系人手机
    @Column(name = "lkm_email")
    private String lkmEmail;//联系人邮箱
    @Column(name = "lkm_position")
    private String lkmPosition;//联系人职位
    @Column(name = "lkm_memo")
    private String lkmMemo;//联系人备注
    /**
     * 配置联系人到客户的多对一关系
     *     使用注解的形式配置多对一关系
     *      1.配置表关系
     *          @ManyToOne : 配置多对一关系
     *              targetEntity:对方的实体类字节码
     *      2.配置外键(中间表)
     *
     * * 配置外键的过程,配置到了多的一方,就会在多的一方维护外键
     *
     */
    @ManyToOne(targetEntity = Customer.class)
    @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
    private Customer customer;

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public Long getLkmId() {
        return lkmId;
    }

    public void setLkmId(Long lkmId) {
        this.lkmId = lkmId;
    }

    public String getLkmName() {
        return lkmName;
    }

    public void setLkmName(String lkmName) {
        this.lkmName = lkmName;
    }

    public String getLkmGender() {
        return lkmGender;
    }

    public void setLkmGender(String lkmGender) {
        this.lkmGender = lkmGender;
    }

    public String getLkmPhone() {
        return lkmPhone;
    }

    public void setLkmPhone(String lkmPhone) {
        this.lkmPhone = lkmPhone;
    }

    public String getLkmMobile() {
        return lkmMobile;
    }

    public void setLkmMobile(String lkmMobile) {
        this.lkmMobile = lkmMobile;
    }

    public String getLkmEmail() {
        return lkmEmail;
    }

    public void setLkmEmail(String lkmEmail) {
        this.lkmEmail = lkmEmail;
    }

    public String getLkmPosition() {
        return lkmPosition;
    }

    public void setLkmPosition(String lkmPosition) {
        this.lkmPosition = lkmPosition;
    }

    public String getLkmMemo() {
        return lkmMemo;
    }

    public void setLkmMemo(String lkmMemo) {
        this.lkmMemo = lkmMemo;
    }
}

dao层

package nju.software.dao;

import nju.software.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/**
 * 符合SpringDataJpa的dao层接口规范
 *      JpaRepository<操作的实体类类型,实体类中主键属性的类型>(点进去 ctrl+f12看成员方法)
 *          * 封装了基本CRUD操作
 *      JpaSpecificationExecutor<操作的实体类类型>
 *          * 封装了复杂查询(分页)
 *
 */
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
}

package nju.software.dao;


import nju.software.domain.LinkMan;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

/**
 * 联系人的dao接口
 */
public interface LinkManDao extends JpaRepository<LinkMan,Long>, JpaSpecificationExecutor<LinkMan> {
}

测试类

package nju.software.test;

import nju.software.dao.CustomerDao;
import nju.software.dao.LinkManDao;
import nju.software.domain.Customer;
import nju.software.domain.LinkMan;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class OneToManyTest {
    @Autowired
    private CustomerDao customerDao;
    @Autowired
    private LinkManDao linkManDao;
    /**
     * 保存一个客户,保存一个联系人
     *  效果:客户和联系人作为独立的数据保存到数据库中
     *      联系人的外键为空
     *  原因?
     *      实体类中没有配置关系
     */
    @Test
    @Transactional
    @Rollback(false)
    public void  testAdd(){
        Customer customer = new Customer();
        customer.setCustName("百度");
        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("小豪");
        /**
         * 配置了客户到联系人的关系
         *      从客户的角度上:发送两条insert语句,发送一条更新语句更新数据库(更新外键)
         * 由于我们配置了客户到联系人的关系:客户可以对外键进行维护
         */
        customer.getLinkMans().add(linkMan);
        customerDao.save(customer);
        linkManDao.save(linkMan);
    }

    @Test
    @Transactional
    @Rollback(false)
    public void  testAdd1(){
        //创建一个客户,创建一个联系人
        Customer customer = new Customer();
        customer.setCustName("百度");
        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("小李");

        /**
         * 配置联系人到客户的关系(多对一)
         *    只发送了两条insert语句
         * 由于配置了联系人到客户的映射关系(多对一)
         *
         *
         */
        linkMan.setCustomer(customer);
        customerDao.save(customer);
        linkManDao.save(linkMan);
    }
    /**
     * 会有一条多余的update语句
     *      * 由于一的一方可以维护外键:会发送update语句
     *      * 解决此问题:只需要在一的一方放弃维护权即可
     *
     */
    @Test
    @Transactional //配置事务
    @Rollback(false) //不自动回滚
    public void testAdd2() {
        //创建一个客户,创建一个联系人
        Customer customer = new Customer();
        customer.setCustName("百度");
        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("小李");
        linkMan.setCustomer(customer);//由于配置了多的一方到一的一方的关联关系(当保存的时候,就已经对外键赋值)
        customer.getLinkMans().add(linkMan);//由于配置了一的一方到多的一方的关联关系(发送一条update语句)
        customerDao.save(customer);
        linkManDao.save(linkMan);
    }
    /**
     * 级联添加:保存一个客户的同时,保存客户的所有联系人
     *      需要在操作主体的实体类上,配置casacde属性
     */
    @Test
    @Transactional //配置事务
    @Rollback(false) //不自动回滚
    public void testCascadeAdd() {
        Customer customer = new Customer();
        customer.setCustName("百度1");

        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("小李1");

        linkMan.setCustomer(customer);
        customer.getLinkMans().add(linkMan);
        //Customer插入数据后,对应的LinkMan中也会插入
        customerDao.save(customer);
    }


    /**
     * 级联删除:
     *      删除1号客户的同时,删除1号客户的所有联系人
     */
    @Test
    @Transactional //配置事务
    @Rollback(false) //不自动回滚
    public void testCascadeRemove() {
        //1.查询1号客户
        Customer customer = customerDao.findOne(1l);
        //2.删除1号客户
        customerDao.delete(customer);
    }
}

二、多对多查询

实体类USer

package nju.software.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "sys_user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "user_id")
    private Long userId;
    @Column(name = "user_name")
    private String userName;
    @Column(name = "age")
    private Integer age;
    /**
     * 配置用户到角色的多对多关系
     *      配置多对多的映射关系
     *          1.声明表关系的配置
     *              @ManyToMany(targetEntity = Role.class)  //多对多
     *                  targetEntity:代表对方的实体类字节码
     *          2.配置中间表(包含两个外键)
     *                @JoinTable
     *                  name : 中间表的名称
     *                  joinColumns:配置当前对象在中间表的外键
     *                      @JoinColumn的数组
     *                          name:外键名
     *                          referencedColumnName:参照的主表的主键名
     *                  inverseJoinColumns:配置对方对象在中间表的外键
     */
    @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL)
    @JoinTable(name = "sys_user_role",
                //joinColumns当前对象在中间表中的外键
                //name是中间表的外键名称,referencedColumnName是当前表对应的字段名
                joinColumns = {@JoinColumn(name = "sys_user_id" ,referencedColumnName = "user_id" )},
                //inverseJoinColumns,对方对象在中间表的外键
                inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")})
    private Set<Role> roles = new HashSet<>();

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

Role类

package nju.software.domain;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "sys_role")
public class Role {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "role_id")
    private Long roleId;
    @Column(name = "role_name")
    private String roleName;
    //配置用户到角色的多对多关系
//    @ManyToMany(targetEntity = User.class)
//    @JoinTable(name = "sys_user_role",
//            //joinColumns当前对象在中间表中的外键
//            //name是中间表的外键名称,referencedColumnName是当前表对应的字段名
//            joinColumns = {@JoinColumn(name = "sys_role_id" ,referencedColumnName = "role_id" )},
//            //inverseJoinColumns,对方对象在中间表的外键
//            inverseJoinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")})
    @ManyToMany(mappedBy = "roles")  //配置多表关系
    private Set<User> users = new HashSet<>();

    public Set<User> getUsers() {
        return users;
    }

    public void setUsers(Set<User> users) {
        this.users = users;
    }

    public Long getRoleId() {
        return roleId;
    }

    public void setRoleId(Long roleId) {
        this.roleId = roleId;
    }

    public String getRoleName() {
        return roleName;
    }

    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }
}

dao层

package nju.software.dao;

import nju.software.domain.Role;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface RoleDao extends JpaRepository<Role,Long>, JpaSpecificationExecutor<Role> {
}

package nju.software.dao;

import nju.software.domain.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

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

Test类

package nju.software.test;

import nju.software.dao.RoleDao;
import nju.software.dao.UserDao;
import nju.software.domain.Role;
import nju.software.domain.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ManyToManyTest {
    @Autowired
    private RoleDao roleDao;
    @Autowired
    private UserDao userDao;
    /**
     * 保存一个用户,保存一个角色
     * 多对多放弃维护权:被动的一方放弃
     */
    @Test
    @Transactional
    @Rollback(false)
    public void testAdd(){
        User user = new User();
        user.setUserName("小明2");
        Role role = new Role();
        role.setRoleName("程序猿2");
        //配置用户到角色关系,可以对中间表中的数据进行维护     1-1
        user.getRoles().add(role);
        //配置角色到用户的关系,可以对中间表的数据进行维护     1-1
        role.getUsers().add(user);
        userDao.save(user);
        //被动方放弃
//        roleDao.save(role);
    }
    //测试级联添加(保存一个用户的同时保存用户的关联角色)
    @Test
    @Transactional
    @Rollback(false)
    public void testCasCadeAdd(){
        User user = new User();
        user.setUserName("小明");
        Role role = new Role();
        role.setRoleName("程序猿");
        //配置用户到角色关系,可以对中间表中的数据进行维护     1-1
        user.getRoles().add(role);
        //配置角色到用户的关系,可以对中间表的数据进行维护     1-1
        role.getUsers().add(user);
        //在User上声明的级联,不能用RoleDao
        userDao.save(user);

    }
    /**
     * 案例:删除id为1的用户,同时删除他的关联对象
     */
    @Test
    @Transactional
    @Rollback(false)
    public void  testCasCadeRemove() {
        //查询1号用户
        User user = userDao.findOne(1l);
        //删除1号用户
        userDao.delete(user);

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值