BaldHead`s Blog

spring-data-jpa ----OneToMany 一对多

环境搭建

导入依赖  maven3.6.3

 <properties>
        <spring.version>5.2.5.RELEASE</spring.version>
        <hibernate.version>5.4.10.Final</hibernate.version>
        <slf4j.version>1.7.30</slf4j.version>
        <log4j.version>2.12.1</log4j.version>
        <druid.version>1.1.21</druid.version>
        <mysql.version>5.1.6</mysql.version>

    </properties>

    <dependencies>
        <!-- spring beg -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.5</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- spring对orm框架的支持包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- hibernate beg -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>${hibernate.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.validator</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>6.1.2.Final</version>
            <exclusions>
                <exclusion>
                    <artifactId>classmate</artifactId>
                    <groupId>com.fasterxml</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- hibernate end -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>${mysql.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>
        <!-- spring data jpa 的坐标 -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.2.6.RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>slf4j-api</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--  el beg 使用spring data jpa 必须引入 -->
        <dependency>
            <groupId>javax.el</groupId>
            <artifactId>javax.el-api</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.el</artifactId>
            <version>3.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>

编写配置文件

<?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"
       xmlns:contxt="http://www.springframework.org/schema/context"
       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">


    <!-- 配置实体类管理工厂 -->
    <bean id="entityManagerFactoryBean" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"/>

        <property name="packagesToScan" value="com.ytfs.entity"/>
        <property name="persistenceProvider">
            <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/>
        </property>

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="true"/>
                <property name="database" value="MYSQL"/>
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
                <property name="generateDdl" value="false"/>
            </bean>
        </property>
    </bean>

    <!-- 配置事务管理器 -->

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 整合jpa -->
    <jpa:repositories base-package="com.ytfs.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactoryBean"/>


    <!-- spring的包扫描 -->

    <context:component-scan base-package="com.ytfs"/>
    <!-- 整合数据源 -->
    <context:property-placeholder location="classpath:jdbcConfig.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
        <property name="url" value="${jdbc.url}"/>
    </bean>
</beans>

实体类编写

 

注解说明

@OneToMany:
       作用:建立一对多的关系映射
    属性:
        targetEntityClass:指定多的多方的类的字节码
        mappedBy:指定从表实体类中引用主表对象的名称。
        cascade:指定要使用的级联操作
        fetch:指定是否采用延迟加载
        orphanRemoval:是否使用孤儿删除

@ManyToOne
    作用:建立多对一的关系
    属性:
        targetEntityClass:指定一的一方实体类字节码
        cascade:指定要使用的级联操作
        fetch:指定是否采用延迟加载
        optional:关联是否可选。如果设置为false,则必须始终存在非空关系。

@JoinColumn
     作用:用于定义主键字段和外键字段的对应关系。
     属性:
        name:指定外键字段的名称
        referencedColumnName:指定引用主表的主键字段名称
        unique:是否唯一。默认值不唯一
        nullable:是否允许为空。默认值允许。
        insertable:是否允许插入。默认值允许。
        updatable:是否允许更新。默认值允许。
        columnDefinition:列的定义信息。

 

Customer

package xyz.ytfs.entity;

import org.springframework.test.context.ContextConfiguration;

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

/**
 * @Classname Customer
 * @Description TODO(客户的实体类)
 * @Date 2020/5/8 23:20
 * @Created by ytfs
 */

@Entity
@Table(name = "cst_customer")
public class Customer {
    /**
     * CREATE TABLE `cst_linkman` (
     * `lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)',
     * `lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
     * `lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
     * `lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
     * `lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
     * `lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
     * `lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
     * `lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
     * `lkm_cust_id` bigint(32) NOT NULL COMMENT '客户id(外键)',
     * PRIMARY KEY (`lkm_id`),
     * KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
     * CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`) REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
     * ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
     */

    @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_industry")
    private String custIndustry;

    @Column(name = "cust_level")
    private String custLevel;

    @Column(name = "cust_address")
    private String custAddress;

    @Column(name = "cust_phone")
    private String custPhone;

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

    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 getCustIndustry() {
        return custIndustry;
    }

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

    public String getCustLevel() {
        return custLevel;
    }

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

    public String getCustAddress() {
        return custAddress;
    }

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

    public String getCustPhone() {
        return custPhone;
    }

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

    public Set<LinkMan> getLinkMans() {
        return linkMans;
    }

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

    @Override
    public String toString() {
        return "Customer{" +
                "custId=" + custId +
                ", custName='" + custName + '\'' +
                ", custSource='" + custSource + '\'' +
                ", custIndustry='" + custIndustry + '\'' +
                ", custLevel='" + custLevel + '\'' +
                ", custAddress='" + custAddress + '\'' +
                ", custPhone='" + custPhone + '\'' +
                '}';
    }
}

LinkMan

package xyz.ytfs.entity;

import javax.persistence.*;

/**
 * @Classname LinkMan
 * @Description TODO(联系人的实体类)
 * @Date 2020/5/8 23:16
 * @Created by ytfs
 */

@Entity
@Table(name = "cst_linkman")
public class LinkMan {
    /**
     * CREATE TABLE `cst_customer` (
     *   `cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
     *   `cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
     *   `cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
     *   `cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
     *   `cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
     *   `cust_address` varchar(128) DEFAULT NULL COMMENT '客户联系地址',
     *   `cust_phone` varchar(64) DEFAULT NULL COMMENT '客户联系电话',
     *   PRIMARY KEY (`cust_id`)
     * ) ENGINE=InnoDB AUTO_INCREMENT=94 DEFAULT CHARSET=utf8;
     */
    @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;

    /*
    表达多对一的关系
     */
    @ManyToOne(targetEntity = Customer.class)
    @JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
    private 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;
    }

    public Customer getCustomer() {
        return customer;
    }

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

    @Override
    public String toString() {
        return "LinkMan{" +
                "lkmId=" + lkmId +
                ", lkmName='" + lkmName + '\'' +
                ", lkmGender='" + lkmGender + '\'' +
                ", lkmPhone='" + lkmPhone + '\'' +
                ", lkmMobile='" + lkmMobile + '\'' +
                ", lkmEmail='" + lkmEmail + '\'' +
                ", lkmPosition='" + lkmPosition + '\'' +
                ", lkmMemo='" + lkmMemo + '\'' +
                '}';
    }
}

测试类

package xyz.ytfs.test;

import org.hibernate.loader.plan.build.internal.returns.CollectionFetchableIndexAnyGraph;
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 xyz.ytfs.dao.ICustomerDao;
import xyz.ytfs.dao.ILinkMan;
import xyz.ytfs.entity.Customer;
import xyz.ytfs.entity.LinkMan;

import javax.transaction.Transactional;
import javax.validation.constraints.AssertFalse;
import java.util.List;
import java.util.Optional;

/**
 * @Classname JpaOnetoManyTest
 * @Description TODO(JPA多对一的测试类)
 * @Date 2020/5/9 0:10
 * @Created by ytfs
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class JpaOnetoManyTest {

    @Autowired
    private ICustomerDao customerDao;

    @Autowired
    private ILinkMan linkManDao;


    @Test
    @Transactional     //配置事务
    @Rollback(false)       //不自动回滚
    public void testSave1() {

        Customer customer = new Customer();
        customer.setCustName("雨听风说");

        LinkMan linkMan = new LinkMan();
        linkMan.setLkmName("小邓");

        /*
         * 配置了客户到联系人的关系
         *      从客户的角度上:发送两条insert语句,发送一条更新语句更新数据库(更新外键)
         * 由于我们配置了客户到联系人的关系:客户可以对外键进行维护
         */

        customer.getLinkMans().add(linkMan);

        this.customerDao.save(customer);
        this.linkManDao.save(linkMan);
    }

    @Test
    @Transactional //配置事务
    @Rollback(false) //不自动回滚
    public void testSave2() {
        //创建一个客户,创建一个联系人
        Customer customer = new Customer();
        customer.setCustName("百度");

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

        /**
         * 配置联系人到客户的关系(多对一)
         *    只发送了两条insert语句
         * 由于配置了联系人到客户的映射关系(多对一)
         *
         *
         */
        linkMan.setCustomer(customer);

        this.customerDao.save(customer);
        this.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语句)

        this.customerDao.save(customer);
        this.linkManDao.save(linkMan);
    }

    /**
     * 级联添加:保存一个客户的同时,保存客户的所有联系人
     * 需要在操作主体(这里就是Customer)的实体类上,配置casacde属性
     */
    @Test
    @Transactional
    @Rollback(false)
    public void testSaveCasacde() {

        Customer customer = new Customer();
        customer.setCustName("雨听风说1");


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

        /*
        表达关系
         */

        customer.getLinkMans().add(linkMan);
        linkMan.setCustomer(customer);

        /*
             级联操作得时候在保存客户得同时会自动保存客户对应得联系人,但是需要在一的一方配置级联操作
         */
        this.customerDao.save(customer);
    }

    @Test
    @Transactional //配置事务
    @Rollback(false) //不自动回滚
    public void testCascadeRemove() {
        //1.查询1号客户
        Customer customer =  customerDao.getOne(1l);
        //2.删除1号客户
        System.out.println(customer);
        customerDao.delete(customer);
    }


}

 对象导航查询测试

package xyz.ytfs.test;

import org.aspectj.weaver.ast.Var;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import xyz.ytfs.dao.ICustomerDao;
import xyz.ytfs.dao.ILinkMan;
import xyz.ytfs.entity.Customer;
import xyz.ytfs.entity.LinkMan;

import java.util.Optional;
import java.util.Set;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class ObjectQueryTest {
    @Autowired
    private ICustomerDao customerDao;

    @Autowired
    private ILinkMan linkManDao;

    //could not initialize proxy - no Session
    //测试对象导航查询(查询一个对象的时候,通过此对象查询所有的关联对象)
    @Test
    @Transactional // 解决在java代码中的no session问题
    public void  testQuery1() {
        //查询id为1的客户
        Customer customer = this.customerDao.getOne(1L);
        //对象导航查询,此客户下的所有联系人
        Set<LinkMan> linkMans = customer.getLinkMans();
        linkMans.forEach(System.out::println);

    }
    /**
     * 对象导航查询:
     *      默认使用的是延迟加载的形式查询的
     *          调用get方法并不会立即发送查询,而是在使用关联对象的时候才会差和讯
     *      延迟加载!
     * 修改配置,将延迟加载改为立即加载
     *      fetch,需要配置到多表映射关系的注解上
     *
     */

    @Test
    @Transactional // 解决在java代码中的no session问题
    public void  testQuery2() {
        //查询id为1的客户
        Customer customer = this.customerDao.getOne(1L);
        //对象导航查询,此客户下的所有联系人
        Set<LinkMan> linkMans = customer.getLinkMans();
        linkMans.forEach(System.out::println);

    }

    /**
     * 从联系人对象导航查询他的所属客户
     *      * 默认 : 立即加载
     *  延迟加载:
     *
     */
    @Test
    @Transactional // 解决在java代码中的no session问题
    public void  testQuery3() {
        //对象导航查询所属的客户
        LinkMan linkMan = this.linkManDao.getOne(1L);

        Customer customer = linkMan.getCustomer();

        System.out.println("customer = " + customer);
    }

}

 

posted @ 2020-05-11 23:40  BaldHead  阅读(815)  评论(0编辑  收藏  举报