Loading...

SpringBoot入门(三)——SpringData JPA

  iwehdio的博客园:https://www.cnblogs.com/iwehdio/

1、JPA

  • ORM:对象关系映射。

    • 主要目的:操作实体类就相当于操作数据库表。
    • 需要映射关系:
      • 建立实体类和表的关系。
      • 建立实体类中属性和表中字段的关系。
    • 不需要再重点关注sql语句。
  • JPA规范:

    • ORM框架的规范,内部由接口和抽象类组成。
    • 使用一些注解表示映射方式
    • 类比JDBC:
      • MySQL驱动和Oracle驱动都实现了JDBC规范。
      • 实现了JDBC的接口,更换数据库Java代码不需要改变。
    • 优势:标准化、容器级特性的支持、简单方便、查询能力、高级特性。
  • 搭建环境的过程:

    • 导入maven坐标。

    • 配置JPA核心配置文件:

      • 需要配置到类路径下的 META-INF 文件夹下。
      • 命名为 persistence.xml。
      <?xml version="1.0" encoding="UTF-8"?>
      <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
          <!--持久化单元:
          name持久化单元名称;
          transaction-type:事务管理的方式。RESOURCE_LOCAL本地事务管理;JTA:分布式事务管理-->
          <persistence-unit name="myJpa" transaction-type="RESOURCE_LOCAL">
      
              <!--  jpa的实现方式  -->
              <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
              <properties>
                  <!-- 数据库信息-->
                  <property name="javax.persistence.jdbc.user" value="root"/>
                  <property name="javax.persistence.jdbc.password" value="root"/>
                  <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
                  <property name="javax.persistence.jdbc.url" value="jdbc:mysql:///jpa"/>
                  <!-- 可选配置:配置JPA实现方式(Hibernate)的配置 -->
                  <!-- 显示sql(true,flase);自动创建数据库表(create,update,none) -->
                  <property name="hibernate.show_sql" value="true"/>
                  <property name="hibernate.hbm2ddl.auto" value="update"/>
              </properties>
          </persistence-unit>
      
      </persistence>
      
    • 编写客户的实体类。

    • 用JPA注解配置实体类和表、类中属性和表中字段的映射。

      • 实体类和表的映射:
        • 实体类上声明@Entity该类是一个实体类。
        • 映射关系@Table(name="数据库表名")
      • 类中属性和表中字段的映射:
        • 主键对应的属性上加@Id
        • 主键如果是自增的,还需要加@GeneratedValue(strategy = GenerationType.IDENTITY)
        • 指定数据库表中的字段@Column(name = "表中字段")
        • 普通属性只需要加@Column
      @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;
      }
      
    • JPA的操作步骤(保存一个客户到数据库):

      • 加载配置文件,创建实体管理器工厂对象。
      • 通过工厂获取实体管理器。
      • 获取事务对象,开启事务。
      • 完成增删改查操作。
      • 提交/回滚事务。释放资源。
      @Test
      public void testSave() {
      	EntityManagerFactory factory = Persistence.createEntityManagerFactory("myJpa");
          EntityManager entityManager = factory.createEntityManager();
          EntityTransaction transaction = entityManager.getTransaction();
          transaction.begin();
          Customer customer = new Customer();
          customer.setCustName("iwehdio");
          customer.setCustIndustry("java");
          entityManager.persist(customer);
          transaction.commit();
          entityManager.close();
          factory.close();
      }
      
    • entityManager.persist()方法:保存对象,传入一个实体类对象。

  • API对象:

    • Persistence的静态方法createEntityManagerFactory:根据持久化单元名称创建实体管理器工厂EntityManagerFactory。
    • 实体管理器工厂EntityManagerFactory:创建实体管理器EntityManager。
      • EntityManagerFactory内部维护了数据库信息、缓存信息、实体管理器对象等。
      • 创建过程比较浪费资源。
      • 是线程安全的访问对象。
      • 可以创建一个公共的实体管理器工程,借助静态代码块的形式。
    • EntityManager实体类管理器对象:getTransaction创建事务对象EntityTransaction。
      • EntityManager实体类管理器对象是真正与数据库进行操作的对象。
      • presist保存、merge更新、remove删除、find/getReference根据id查询。
    • EntityTransaction事务对象:开启、提交、回滚。
  • 主键生成策略:

    • 使用注解@GenratedValue
    • 自增:GenerationType.INDENTITY,前提是底层数据库支持自动增长方式,对id自增(MySQL)。
    • 序列:GenerationType.SEQUENCE,前提是底层数据库支持序列(Oracle)。
    • JPA提供的机制,通过一张数据库表的形式完成主键自增:GenerationType.TABLE
    • 程序自动选择主键生成测量:GenerationType.AUTO
  • 抽取JpaUtils:

    public class JapUtils {
        private static EntityManagerFactory factory;
        static {
            //加载配置文件,创建实体管理器工厂
            factory = Persistence.createEntityManagerFactory("myJpa");
        }
        //获取实体管理器工厂的方法
        public static EntityManager getEntityManager() {
            return factory.createEntityManager();
        }
    }
    
    • 第一次访问,静态代码块创建工厂对象,在调用方法创建管理器对象。
  • 根据id查询客户:

    • entityManager.find()方法:第一个参数是查询数据的结果需要封装的实体类对象的字节码,第二个的主键的取值。

      @Test
      public void testfind() {
          EntityManager entityManager = JpaUtils.getEntityManager();
          EntityTransaction transaction = entityManager.getTransaction();
          transaction.begin();
          Customer customer = entityManager.find(Customer.class, 1L);
          System.out.println(customer);
          transaction.commit();
          entityManager.close();
      }
      
    • entityManager.getReference():与find类似,也是查询方法,参数相同。

    • find方法查询的对象就是当前客户对象本身,在调用find方法时就发送SQL语句查询,也就是立即加载。

    • 而getReference方法查询的对象是客户对象的动态代理对象,调用getReference方法不会立即发送SQL语句查询。在调用查询对象时,才发送SQL语句,也就是延迟加载。

  • 删除客户:

    • entityManager.remove()方法:传入的参数是一个对象。
    • 需要先根据id查询,再将查到的对象传入remove方法。
  • 更新客户:

    • entityManager.merge()方法:传入的参数是一个对象。
    • 需要先根据id查询,再将查到的对象更新数据后传入merge方法。
  • jpql查询:

    • JPA查询语句,查询的是实体类和类中的属性,与SQL语句语法相似。

    • 查询步骤:

      • 根据jpql语句创建Query对象。
      • 对参数赋值。
      • 发送查询,封装结果。
    • 查询全部:

      • jpql="from Customer"语句:查询所有。支持简写也支持全类名。
      • entityManager.createQuery(jpql)方法:创建Query查询对象,是执行jpql的对象。
      • query.getResultList()方法:发送查询,并封装结果集为List。
      String jpql = "from Customer";
      Query query = entityManager.createQuery(jpql);
      List resultList = query.getResultList();
      for(Object object :resultList) {
          System.out.println(object);
      }
      
    • 根据id倒序查询:

      • jpql = "from Customer order by custId desc "语句:倒序查询所有。
    • 统计查询:

      • jpql = "select count(custId) from Customer"语句:统计客户总数。
      • query.getSingleResult()方法:发送查询,获取单个结果。
    • 分页查询:

      • jpql语句还是查询所有。
      • query.setFirstResult()方法:分页查询的起始页码。
      • query.setMaxResults()方法:分页查询的每页条数。
    • 条件查询:

      • jpql = "from Customer where custName like ?"语句:对名字进行模糊查询。
      • query.setParameter()方法:设置占位符,第一个是占位符索引从1开始,第二个是占位符的值。

2、SpringData JPA

  • Spring基于ORM框架、JPA规范基础上封装的JPA应用框架,摆脱了DAO层的操作。

  • 环境搭建:

    • maven坐标:

      <properties>
          <spring.version>4.2.4.RELEASE</spring.version>
          <hibernate.version>5.0.7.Final</hibernate.version>
          <slf4j.version>1.6.6</slf4j.version>
          <log4j.version>1.2.12</log4j.version>
          <c3p0.version>0.9.1.2</c3p0.version>
          <mysql.version>5.1.6</mysql.version>
      </properties>
      
      <dependencies>
          <!-- junit单元测试 -->
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.9</version>
              <scope>test</scope>
          </dependency>
      
          <!-- spring beg -->
          <dependency>
              <groupId>org.aspectj</groupId>
              <artifactId>aspectjweaver</artifactId>
              <version>1.6.8</version>
          </dependency>
      
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-aop</artifactId>
              <version>${spring.version}</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-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>
      
          <!-- spring end -->
      
          <!-- hibernate beg -->
          <dependency>
              <groupId>org.hibernate</groupId>
              <artifactId>hibernate-core</artifactId>
              <version>${hibernate.version}</version>
          </dependency>
          <dependency>
              <groupId>org.hibernate</groupId>
              <artifactId>hibernate-entitymanager</artifactId>
              <version>${hibernate.version}</version>
          </dependency>
          <dependency>
              <groupId>org.hibernate</groupId>
              <artifactId>hibernate-validator</artifactId>
              <version>5.2.1.Final</version>
          </dependency>
          <!-- hibernate end -->
      
          <!-- c3p0 beg -->
          <dependency>
              <groupId>c3p0</groupId>
              <artifactId>c3p0</artifactId>
              <version>${c3p0.version}</version>
          </dependency>
          <!-- c3p0 end -->
      
          <!-- log end -->
          <dependency>
              <groupId>log4j</groupId>
              <artifactId>log4j</artifactId>
              <version>${log4j.version}</version>
          </dependency>
      
          <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-api</artifactId>
              <version>${slf4j.version}</version>
          </dependency>
      
          <dependency>
              <groupId>org.slf4j</groupId>
              <artifactId>slf4j-log4j12</artifactId>
              <version>${slf4j.version}</version>
          </dependency>
          <!-- log end -->
      
      
          <dependency>
              <groupId>mysql</groupId>
              <artifactId>mysql-connector-java</artifactId>
              <version>${mysql.version}</version>
          </dependency>
      
          <dependency>
              <groupId>org.springframework.data</groupId>
              <artifactId>spring-data-jpa</artifactId>
              <version>1.9.0.RELEASE</version>
          </dependency>
      
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-test</artifactId>
              <version>${spring.version}</version>
          </dependency>
      
          <!-- el beg 使用spring data jpa 必须引入 -->
          <dependency>  
              <groupId>javax.el</groupId>  
              <artifactId>javax.el-api</artifactId>  
              <version>2.2.4</version>  
          </dependency>  
      
          <dependency>  
              <groupId>org.glassfish.web</groupId>  
              <artifactId>javax.el</artifactId>  
              <version>2.2.4</version>  
          </dependency> 
          <!-- el end -->
      </dependencies>
      
    • 配置Spring配置文件:

      <?xml version="1.0" encoding="UTF-8"?>
      <beans> 
          <!-- 1.dataSource 配置数据库连接池-->
          <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
              <property name="driverClass" value="com.mysql.jdbc.Driver" />
              <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/jpa" />
              <property name="user" value="root" />
              <property name="password" value="root" />
          </bean>
      
          <!-- 2.配置entityManagerFactory -->
          <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
              <property name="dataSource" ref="dataSource" />
              <!-- 配置扫描的实体类所在的包-->
              <property name="packagesToScan" value="cn.iwehdio.domain" />
              <!-- jpa的实现厂家-->
              <property name="persistenceProvider">
                  <bean class="org.hibernate.jpa.HibernatePersistenceProvider" />
              </property>
              <!--JPA的供应商适配器-->
              <property name="jpaVendorAdapter">
                  <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                      <!--配置是否自动创建数据库表-->
                      <property name="generateDdl" value="false" />
                      <!--数据库类型-->
                      <property name="database" value="MYSQL" />
                      <!-- 数据库方言(特有语法) -->
                      <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
                      <!-- 是否显示SQL语句 -->
                      <property name="showSql" value="true" />
                  </bean>
              </property>
              <!-- jpa方言 -->
              <property name="jpaDialect">
                  <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect" />
              </property>
          </bean>
      
      
          <!-- 3.事务管理器-->
          <!-- JPA事务管理器,配置实体管理器工厂  -->
          <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
              <property name="entityManagerFactory" ref="entityManagerFactory" />
          </bean>
      
          <!-- 整合spring data jpa,配置dao接口包、事务和实体管理器工厂-->
          <jpa:repositories base-package="cn.iwehdio.dao"
                            transaction-manager-ref="transactionManager"
                            entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
      
          <!-- 4.txAdvice-->
          <tx:advice id="txAdvice" transaction-manager="transactionManager">
              <tx:attributes>
                  <tx:method name="save*" propagation="REQUIRED"/>
                  <tx:method name="insert*" propagation="REQUIRED"/>
                  <tx:method name="update*" propagation="REQUIRED"/>
                  <tx:method name="delete*" propagation="REQUIRED"/>
                  <tx:method name="get*" read-only="true"/>
                  <tx:method name="find*" read-only="true"/>
                  <tx:method name="*" propagation="REQUIRED"/>
              </tx:attributes>
          </tx:advice>
      
          <!-- 5.aop-->
          <aop:config>
              <aop:pointcut id="pointcut" expression="execution(* cn.iwehdio.service.*.*(..))" />
              <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" />
          </aop:config>
          <!-- 配置包扫描注解   -->
          <context:component-scan base-package="cn.iwehdio"></context:component-scan>
      
      </beans>
      
    • 编写实体类,使用jpa注解配置映射关系。

  • 编写dao接口:

    • 符合SpringData JPA的接口规范:

      • 继承接口JpaRepositoryJpaSpecificationExecutor
      • 需要提供相应的泛型。
        • 接口JpaRepository
          • 第一个泛型:操作的实体类类型。
          • 第二个泛型:实体类中主键属性的类型。
          • 封装了基本CRUD操作。
        • 接口JpaSpecificationExecutor
          • 泛型:操作的实体类类型。
          • 封装了复杂查询。
      public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> {
      }
      
    • 测试:

      • 测试环境:根据主键查询一个。

        @RunWith(SpringJUnit4ClassRunner.class)
        @ContextConfiguration(locations = "classpath:applicationContext.xml")
        public class DaoTest {
        
            @Autowired
            private CustomerDao customerDao;
        
            @Test
            public void test1() {
                Customer one = customerDao.findOne(2L);
                System.out.println(one);
            }
        }
        
      • findOne()方法:根据主键查询一个。

      • save()方法:保存或更新。如果传入的对象存在主键,则为更新;没有主键,则为保存。

      • delete()方法:根据主键删除。

      • findAll()方法:查询所有。

  • SpringData JPA运行过程:

    • 真正发挥作用的接口的实现类,在程序运行过程中,自动帮助我们动态生成了接口的实现类对象。
    • 通过动态代理,可以生成基于接口的实现类对象。
    • 最终是调用JPA的实体管理器进行操作。
    • 运行流程:
      • 通过JDKDynamicAopProxy的invoke方法创建动态代理对象SimpleJpaRepository。
      • SimpleJpaRepository实现了之前dao中继承的两个接口JpaRepository和JpaSpecificationExecutor。
      • 通过entityManager完成操作。
      • JPA规范调用hibernate封装的jdbc操作数据库。
    • 配置文件中<jpa:repositories base-package=“”> 指定对此包下的dao接口进行动态代理增强。
  • 查询操作:

    • 借助接口中定义好的方法:

      • count()方法:查询总数量。
      • exists()方法:传入主键,判断是否存在此条记录。是通过查询count实现的。
      • getOne()方法:根据主键从数据库查询。
        • 单元测试需要加上事务支持@Transactional
        • 是延迟加载。
    • 支持jpql的查询方式:

      • 需要将jpql语句使用注解@Query的形式,配置到接口方法上。

      • 按名称查询:

        @Query(value = "from Customer where custName = ?")
        public Customer findByName(String custName);
        
      • 按名称的主键id查询:

        @Query(value = "from Customer where custName=? and custId=?")
        public Customer findByNameAndId(String name, Long id);
        
      • 多个参数传入,默认情况下jpql语句中的占位符赋值顺序与方法的参数列表一致。

      • 也可以指定顺序,在jpql语句中使用?索引。索引就是方法参数列表中的顺序,从1开始。

    • 支持jpql的更新方式:

      • 使用注解@Modifying表示当前是一个更新方法。

      • 进行更新或删除操作,需要事务支持。但是默认会回滚事务,需要加上@Rollback(value=false)

      • 按主键id更新名称:

        @Query(value = "update Customer set custName=?2 where custId=?1")
        @Modifying
        public void updateName(Long id, String name);
        
    • 支持sql的查询方式:

      • 使用注解@Query,使用属性nativeQuery指定哪种语句。true为本地查询表示使用sql,false(默认)表示使用jpql。

      • 查询所有:

        @Query(value = "select * from cst_customer", nativeQuery = true)
        public List<Customer> findAllAsSQL();
        
      • 多个参数的赋值方式与jpql相同。

    • 方法名称规则查询:

      • 是对jpql更加深入的一层封装,只需要按照SpringData JPA提供的方法名称规则定义方法,不需要配置查询语句即可完成查询。

      • 方法命名规则:

        • findBy开头的表示查询:

          • 之后为对象中的属性名(首字母大写),表示查询的条件。

          • findByCustName表示根据客户名称查询。

            public Customer findByCustName(String name);
            
          • 在之后可再加查询方式,比如Like。

            public List<Customer> findByCustNameLike(String name);
            
          • 多条件查询findBy+属性名+查询方式+条件连接符(Or或者And)+其他属性查询。

3、复杂操作

  • specificaions 动态查询:

    • 通过JpaSpecificationExcutor接口中的方法进行查询。

    • 所有方法都需要传入参数Specification<T> spec作为查询条件。

      • 是一个用于描述条件的接口。提供泛型,就是查询的对象类型。
      • 在查询时需要自定义实现类,实现toPredicate()方法封装查询条件。包括三个参数:
        • root:查询的根对象,查询的任何属性都可以从根对象中获取。
        • CriteriaQuery:顶层查询对象,一般不用。
        • CriteriaBuilder:查询的构造器,内部封装了许多查询条件。
    • 查询条件:

      • 查询方式:在CriteriaBuilder中。
      • 比较的属性名称:在root中。
      • root.get("属性名")获取比较的属性。
    • 动态查询:

      • 根据客户名称查询:

        • criteriaBuilder.equal()方法:第一个参数需要比较的属性,第二个参数属性的取值。

          @Test
          public void testfindOne() {
              //匿名内部类
              Specification<Customer> spec = new Specification<Customer>() {
                  @Override
                  public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                      //1、获取比较的属性(使用实体类中的属性名)
                      Path<Object> custName = root.get("custName");
                      //2、构造查询条件
                      Predicate predicate = criteriaBuilder.equal(custName, "iwehdio");
                      return predicate;
                  }
              };
              Customer customer = customerDao.findOne(spec);
              System.out.println(customer);
          }
          
      • 多条件拼接:根据名称和行业查询。

        • 分别构造名称和行业的查询,然后将两个查询联系起来。

        • 将多个查询条件进行组合:criteriaBuilder.and()方法或criteriaBuilder.or()方法,传入多个查询条件。

          @Test
          public void testmulti() {
              Specification<Customer> spec = new Specification<Customer>() {
                  @Override
                  public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                      Path<Object> custName = root.get("custName");
                      Path<Object> custIndustry = root.get("custIndustry");
                      Predicate predicate1 = criteriaBuilder.equal(custName, "iwehdio");
                      Predicate predicate2 = criteriaBuilder.equal(custIndustry, "a");
                      Predicate and = criteriaBuilder.and(predicate1, predicate2);
                      return and;
                  }
              };
              Customer customer = customerDao.findOne(spec);
              System.out.println(customer);
          }
          
      • 模糊查询:

        • criteriaBuilder.like()方法:模糊查询。除了equal方法以外,都还需要使用path.as(.class)传入比较的参数类型。

          @Test
          public void testlike() {
              Specification<Customer> spec = new Specification<Customer>() {
                  @Override
                  public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                      Path<Object> custName = root.get("custName");
                      Predicate predicate = criteriaBuilder.like(custName.as(String.class), "iwe%");
                      return predicate;
                  }
              };
              List<Customer> all = customerDao.findAll(spec);
              System.out.println(all);
          }
          
      • 指定数据顺序:

        • 添加排序Sort对象。

        • 创建排序对象Sort,构造方法传入参数。第一个参数传入升序还是降序,第二个参数是排序的属性名称。

          Sort sort = new Sort(Sort.Direction.DESC, "custId");
          List<Customer> all = customerDao.findAll(spec, sort);
          
      • 分页查询:

        • 添加分页参数Pageable对象,返回Page分页对象。

        • 创建分页参数对象PageRequest,构造方法传入参数。第一个参数当前查询的页数(从0开始),第二个参数每页查询的数量。

          @Test
          public void testpage() {
              Specification<Customer> spec = null;
              Pageable pageable = new PageRequest(0, 2);
              Page<Customer> all = customerDao.findAll(spec, pageable);
              System.out.println(all.getContent());
          
          }
          
        • .getContent()方法:得到数据列表。

  • 多表关系:

    • 可以通过实体类中的包含关系,描述表关系。

    • 步骤:

      • 通过外键和中间表确定表关系,
      • 编写实体类,在实体类中用包含关系描述表关系。
      • 配置映射关系。
    • 一对多操作:

      • 客户到联系人的一对多关系:

        • 声明关系:@OneToMany注解,targetEntity 指定对方对象的字节码。

        • 配置外键:@JoinColumn注解,name指定外键字段名称,referencedColumnName指定参照的主表的主键字段名称。

          @OneToMany(targetEntity = LinkMan.class)
          @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id")
          private Set<LinkMan> linkMans = new HashSet<>();
          
      • 联系人到客户的多对一关系:

        @ManyToOne(targetEntity = Customer.class)
        @JoinColumn(name = "lkm_cust_id", referencedColumnName = "cust_id")
        private Customer customer;
        
        • 配置联系人的实体类和Dao接口。
      • 在一的一方放弃外键维护:

        @OneToMany(mappedBy = "customer")
        private Set<LinkMan> linkMans = new HashSet<>();
        
        • 指定外键中,对方配置关系的属性名称。
      • 删除操作:

        • 删除从表数据,可以随时任意删除。
        • 删除主表数据:
          • 有从表数据的话:
            • 默认情况下,将外键置为null,删除主表数据。
            • 如果一的一方放弃了外键维护,则不能删除。
            • 如果还要删除,需要使用级联删除引用。
          • 没有从表数据引用,随便删除。
        • 级联操作:
          • 需要区分操作主体。
          • 需要在操作主体的实体类上,添加级联属性,使用@OneToMany(cascade=)配置级联。
          • 可选值包括:ALL所有,MERGE更新,PERSIST保存,REMOVE删除。
    • 多对多操作:

      • 用户与角色之间的多对多关系:

        • 声明关系:使用@ManyToMany注解,targetEntity 指定对方对象的字节码。

        • 配置中间表:使用@JoinTable注解,name指定中间表名称,joinColumns指定当前对象在中间表中的外键,inverseJoinColumns指定对方对象在中间表的外键。

          @ManyToMany(targetEntity = Role.class)
          @JoinTable(name = "sys_user_role",
                     joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")},
                     inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")})
          private Set<Role> role = new HashSet<>();
          
      • 多对多放弃维护权:被动的一方放弃维护权。设置同一对多。

      • 多对多级联操作:设置同一对多。

  • 对象导航查询:

    • 通过查询一个对象,查询其所有的关联对象。
    • 通过的方法是调用这个对象的get()方法来查询。
    • 从一方查询多方默认使用延迟加载。调用get()方法并不会立即进行查询。
    • 关闭延迟加载:修改配置为立即加载,配置到多表映射的注解中,即@OneToMany等注解中的fetch属性。EAGER表示立即加载,LAZY表示延迟加载。
    • 从多方查询一方默认使用立即加载。

iwehdio的博客园:https://www.cnblogs.com/iwehdio/
posted @ 2020-08-04 22:15  iwehdio  阅读(123)  评论(0编辑  收藏  举报