Spring DataJpa 的使用小结

Spring Data Jpa 的使用

什么是spring Data jpa

Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架,可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能,且易于扩展!学习并使用 Spring Data JPA 可以极大提高开发效率!

Spring Data Jpa 的入门

一,引入SpringData Jpa 的坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>jap-spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <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>4.2.4.RELEASE</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>

    </dependencies>
</project>

二,创建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:jdbc="http://www.springframework.org/schema/jdbc"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">
    <!--spring 和配置spring data jpa -->
    <!--扫描包-->
    <context:component-scan base-package="com.wym"></context:component-scan>
    <!--配置entityManagerFactory对象交给spring 容器-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <!--引入dataSoucre-->
        <property name="dataSource" ref="dataSource"/>
        <!--配置扫描实体类的包-->
        <property name="packagesToScan" value="com.wym.domain"></property>
        <!--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>
                <property name="database" value="MYSQL"></property>
                <!--方言 支持的特有语法 这里我使用的Mysql数据库-->
                <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"></property>
                <property name="showSql" value="true"></property>
            </bean>
        </property>
        <!--jpa 的方言:高级的特性-->
        <property name="jpaDialect">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
        </property>

        <!--注入jpa 的基本配置信息
            加载jap 的基本信息和jpa 实现方式 (hibernate) 的配置信息
            hibernate.hbm2ddl.auto :自动创建数据库表
        -->
        <property name="jpaProperties">
            <props>
                <!--一加载就重新创建表-->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>

    </bean>
    <!--c3p0数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="root"></property>
        <property name="password" value="1024"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/customer"></property>
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    </bean>
    <!--整合spring dataJpa-->
    <!--提供dao层的包位置-->
    <jpa:repositories base-package="com.wym.dao" transaction-manager-ref="transactionManager"
                      entity-manager-factory-ref="entityManagerFactory"></jpa:repositories>
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
    </bean>
</beans>

三,配置 实体类的属性和数据库进行对应

这里我以Customer为例

package com.wym.domain;

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

/**
 * 客户表
 * 实体类
 * 一的一方
 */
@Entity//表示这个类是实体类
@Table(name="customer_table")
public class Customer {
    
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @Column(name = "addr")
    private String addr;
    @Column(name = "name")
    private String name;
    @Column(name = "sex")
    private String sex;
    @Column(name = "tell")
    private Integer tell;
    
    
    public int getId() {
    
    
        return id;
    }

    public void setId(int id) {
    
    
        this.id = id;
    }

    public String getAddr() {
    
    
        return addr;
    }

    public void setAddr(String addr) {
    
    
        this.addr = addr;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public String getSex() {
    
    
        return sex;
    }

    public void setSex(String sex) {
    
    
        this.sex = sex;
    }

    public Integer getTell() {
    
    
        return tell;
    }

    public void setTell(Integer tell) {
    
    
        this.tell = tell;
    }

    @Override
    public String toString() {
    
    
        return "Customer{" +
                "id=" + id +
                ", addr='" + addr + '\'' +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", tell=" + tell +
                ", likemans=" + likemans +
                '}';
    }
}

四,配置Dao 接口

创建一个接口类继承 JpaRepository,和JpaSpecificationExecutor 获得 CRUD 方法和复杂查询方法

下面以CustomerDao 为例:

package com.wym.dao;

import com.wym.domain.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

/**
 * Jpa实现 Dao 接口
 * JpaRepository<实体类类型,主键类型>:用来完成基本CRUD操作
 * JpaSpecificationExecutor<实体类类型>:用于复杂查询(分页等查询操作)
 */
public interface CustomerDao  extends JpaRepository<Customer,Integer> , JpaSpecificationExecutor<Customer>{
    
    
   
}

五,测试Spring Data Jpa

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")//自己的配置文件的类路径
public class testJpa {
    
    

    @Autowired
    private CustomerDao customerDao;

    /**
     * 保存客户信息
     * save() 若有id 属性 则为更新 否则为 保存
     * 更新 :会先清空一遍数据
     */
    @Test
    public void testSave(){
    
    
        Customer customer = new Customer();
        customer.setName("爬虫");
        customerDao.save(customer);
    }
}

Spring Data Jpa 进行查询

一,简单的CRUD 查询使用JpaRepository中的方法

因为CustomerDao继承了JpaRepository 接口所以也获得了Repository 中的方法

 /**
     * 保存客户信息
     * save() 若有id 属性 则为更新 否则为 保存
     * 更新 :会先清空一遍数据
     */
    @Test
    public void testSave(){
    
    
        Customer customer = new Customer();
        customer.setName("爬虫");
        customerDao.save(customer);
    }

    /**
     * 删除
     */
    @Test
    public void  testDelete(){
    
    
        customerDao.delete(1);

    }
    /**
     * 根据id 查询
     */
    @Test
    public  void testFindById(){
    
    
        customerDao.findOne(2);
    }
    @Test
    public  void  testUpdateById(){
    
    
        customerDao.
    }

二,使用JPQL 查询

Dao的配置

在Dao 的接口创建接口方法 中接口方法上配置注解@Query

 @Query(value = "from  Customer ")
    public List<Customer> findAllCustomer();

    @Query(value = "from Customer  where  name =?1")
    public Customer findCustomer(String custName);
    /**
     * 更新
     * 要加这个标志
     */
    @Query(value = "update  Customer  set  name= ?  where id =?")
    @Modifying
    public void UpdateByCustomer(String name,int id);
测试方法
/**
 * 使用JPQL 方式查询
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class testJPQL {
    
    
    @Autowired
    private CustomerDao customerDao;

    /**
     * 测试jpql的更新和删除操作
     *  * springDataJpa中使用jpql完成 更新/删除操作
     *         * 需要手动添加事务的支持
     *         * 默认会执行结束之后,回滚事务
     *   @Rollback : 设置是否自动回滚
     *          false | true
     */
    @Test
    @Transactional
    @Rollback(value = false)
    public void testUpdateCustomer(){
    
    
       customerDao.UpdateByCustomer("翁艳敏", 1);
    }
}

三,使用SQL 查询

在@Query注解中有一个nativeQuery=true 打开

 /**
     * 使用sql
     * nativeQuery = true
 */
    @Query(value = "select  * from customer_table" ,nativeQuery = true)
    public Customer SqlFindAll();

使用方法和JPQL一样

四,方法命名规则查询

顾名思义,方法命名规则查询就是根据方法的名字,就能创建查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询

例如:

    //方法命名方式查询(根据客户名称查询客户)
    public Customer findByCustName(String custName);
Keyword Sample JPQL
And findByLastnameAndFirstname … where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname … where x.lastname = ?1 or x.firstname = ?2
Is,Equals findByFirstnameIs, findByFirstnameEquals … where x.firstname = ?1
Between findByStartDateBetween … where x.startDate between ?1 and ?2
LessThan findByAgeLessThan … where x.age < ?1
LessThanEqual findByAgeLessThanEqual … where x.age ⇐ ?1
GreaterThan findByAgeGreaterThan … where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual … where x.age >= ?1
After findByStartDateAfter … where x.startDate > ?1
Before findByStartDateBefore … where x.startDate < ?1
IsNull findByAgeIsNull … where x.age is null
IsNotNull,NotNull findByAge(Is)NotNull … where x.age not null
Like findByFirstnameLike … where x.firstname like ?1
NotLike findByFirstnameNotLike … where x.firstname not like ?1
StartingWith findByFirstnameStartingWith … where x.firstname like ?1 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection ages) … where x.age in ?1
NotIn findByAgeNotIn(Collection age) … where x.age not in ?1
TRUE findByActiveTrue() … where x.active = true
FALSE findByActiveFalse() … where x.active = false
IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

具体的关键字,使用方法和生产成SQL参考上面,具体使用方式参考JPQL

五,使用Specifications动态查询

有时我们在查询某个实体的时候,给定的条件是不固定的,这时就需要动态构建相应的查询语句,在Spring Data JPA中可以通过JpaSpecificationExecutor接口查询。相比JPQL,其优势是类型安全,更加的面向对象。

下面是JpaSpecificationExecutor中定义的方法

public interface JpaSpecificationExecutor<T> {
    
    
   	//根据条件查询一个对象
 	T findOne(Specification<T> spec);	
   	//根据条件查询集合
 	List<T> findAll(Specification<T> spec);
   	//根据条件分页查询
 	Page<T> findAll(Specification<T> spec, Pageable pageable);
   	//排序查询查询
 	List<T> findAll(Specification<T> spec, Sort sort);
   	//统计查询
 	long count(Specification<T> spec);
}

对于JpaSpecificationExecutor,这个接口基本是围绕着Specification接口来定义的。我们可以简单的理解为,Specification构造的就是查询条件

 //构造查询条件
    /**
    *	root	:Root接口,代表查询的根对象,可以通过root获取实体中的属性
    *	query	:代表一个顶层查询对象,用来自定义查询
    *	cb		:用来构建查询,此对象里有很多条件方法
    **/
    public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
使用Specifications 来动态查询的例子
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class testSpecifications {
    
    
    @Autowired
    private CustomerDao customerDao;

    /**
     * 使用JpaSpecificationExecutor
     * 模糊查询
     */
    @Test
    public  void  testSpecifications(){
    
    
        
        //使用匿名内部类的方式,创建一个Specifications的实现类,并实现toPredicate 方法

        Specification<Customer> spec = new Specification<Customer>() {
    
    
            //构造查询条件
            /**
             *	root	:Root接口,代表查询的根对象,可以通过root获取实体中的属性
             *	query	:代表一个顶层查询对象,用来自定义查询
             *	cb		:用来构建查询,此对象里有很多条件方法
             **/
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
    
    
                //cd :构造条件
                //root 从实体Customer 对象中按属性来查询
                Predicate predicate = cb.like(root.get("name").as(String.class), "翁%");
                return predicate;
            }
        };
        Customer test = customerDao.findOne(spec);
        System.out.println(test);
    }

    /**
     * 分页查询
     */
    @Test
    public  void testPage(){
    
    
        Specification<Customer> scp = new Specification<Customer>() {
    
    
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
    
    
                Predicate like = criteriaBuilder.like(root.get("name").as(String.class), "翁%");
                return like;
            }
        };
        /**
         * 构造分页参数
         *  Pageable :接口
         *  PageRequest实现了Pageable接口,调用构造方法的形式构造
         * 		第一个参数:页码(从0开始)
         * 		第二个参数:每页查询条数
         */
        PageRequest pageRequest = new PageRequest(0, 1);
        Page<Customer> all = customerDao.findAll(scp,pageRequest);
//        all.getTotalPages();//获取总页数
//        all.getTotalElements(); //获取总记录数
//        all.getContent();//获取列表数据
        System.out.println(all.getContent());
    }
}
Specifications 方法对应关系
方法名称 Sql对应关系
equle filed = value
gt(greaterThan ) filed > value
lt(lessThan ) filed < value
ge(greaterThanOrEqualTo ) filed >= value
le( lessThanOrEqualTo) filed <= value
notEqule filed != value
like filed like value
notLike filed not like value

Spring Data Jpa 多表的关联

在数据库中多表关系有三种,分别是一对一一对多多对多,其中使用最多的是一对多和多对多,而一对一这则很少,具体步骤则为:

第一步:首先确定两张表之间的关系如果关系确定错了,后面做的所有操作就都不可能正确。

第二步:在数据库中实现两张表的关系

第三步:在实体类中描述出两个实体的关系

第四步:配置出实体类和数据库表的关系映射

Spring Data Jpa中的一对多

在一对多关系中,我们习惯把一的一方称之为主表,把多的一方称之为从表。在数据库中建立一对多的关系,需要使用数据库的外键约束

这里我使用客户和联系人的关系

​ 一个客户有多个联系人,而一个联系人则有一个客户

一,配置这两张表的实体类关系

一的一方配置多的一方 集合 (客户表)

/**
 * 客户表
 * 实体类
 * 一的一方
 */
@Entity
@Table(name="customer_table")
public class Customer {
    
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @Column(name = "addr")
    private String addr;
    @Column(name = "name")
    private String name;
    @Column(name = "sex")
    private String sex;
    @Column(name = "tell")
    private Integer tell;
    /**
     * 配置一对多
     */
    //建立一对多的关系映射
	@OneToMany(targetEntity = LikeMan.class)//指定多的多方的类的字节码
    //用于定义主键字段和外键字段的对应关系。
    @JoinColumn(name = "lik_cust_id",referencedColumnName = "id")
    //name:指定外键字段的名称 ,referencedColumnName:指定引用主表的主键字段名称
    private Set<LikeMan>  likemans=new HashSet<>();
    public Set<LikeMan> getLikemans() {
    
    
        return likemans;
    } 
    get&set 方法省略
}

多的一方配置一的一方的对象 (联系人表)

package com.wym.domain;

import javax.persistence.*;

/**
 * 联系人表
 * 多的一方
 */
@Entity
@Table(name = "linkman_table")
public class LikeMan {
    
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name="lid")
    private Integer lid ;
    @Column(name="lname")
    private String lname;
    @Column(name="lphone")
    private String lphone;

    /*配置多对一方*/
    @ManyToOne(targetEntity = Customer.class)//指定一的一方的字节码属性
    @JoinColumn(name = "lik_cust_id",referencedColumnName = "id")//referencedColumnName:指定引用主表的主键字段名称
    private Customer customer;//用它的主键,对应联系人表中的外键
    get和set 方法省略
}

二, 测试一对多的关系

测试前要配置 他们的Dao接口 ,继承Jpa的接口 ,我上面有相关的讲述

/**
 * 测试一对多
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class testOneToMany {
    
    

    @Autowired
    private CustomerDao customerDao;

    @Autowired
    private LikeManDao likeManDao;

    @Test
    @Transactional//开启事务
    @Rollback(false)//设置为不回滚
    public  void  testSave(){
    
    
        Customer customer=new Customer();
        customer.setName("翁艳萍");

        LikeMan l=new LikeMan();
        l.setLname("翁艳萍的员工");
        //建立关系
        customer.getLikemans().add(l);
        l.setCustomer(customer);

        customerDao.save(customer);
        likeManDao.save(l);
    }
}

这样就完成了Jpa 一对多的配置

三,优化一对多的关系

通过保存的案例,我们可以发现在设置了双向关系之后,会发送两条insert语句,一条多余的update语句,那我们的解决是思路很简单,就是一的一方放弃维护权一般是主表放弃维护权**@OneToMany(mappedBy = “customer”)**,并配置了级联属性(在实际开发中,级联删除请慎用),和立即加载属性

@Entity
@Table(name="customer_table")
public class Customer {
    
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @Column(name = "addr")
    private String addr;
    @Column(name = "name")
    private String name;
    @Column(name = "sex")
    private String sex;
    @Column(name = "tell")
    private Integer tell;
    
    /**
     * 配置一对多
     */
    /**
     * cascade:配置级联操作
     * 		CascadeType.MERGE	级联更新
     * 		CascadeType.PERSIST	级联保存:
     * 		CascadeType.REFRESH 级联刷新:
     * 		CascadeType.REMOVE	级联删除:
     * 		CascadeType.ALL		包含所有
     */
   /* @OneToMany(targetEntity = LikeMan.class)
    @JoinColumn(name = "lik_cust_id",referencedColumnName = "id")*/
   //fetch =FetchType.EAGER 立即加载
   @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch =FetchType.EAGER)//配置属性名
    private Set<LikeMan>  likemans=new HashSet<>();
    
    get和set省略
}

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

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

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

Spring Data Jpa 中的多对多

在数据库中多对多的关系一般的解决方法是创建一张中间表 来管理 多对多的关系

下面我使用 角色用户的关系来表示 多对多的关系

一,配置用户表和角色表的实体关系

用户表默认放弃了关联维护权

package com.wym.domain;

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

/**
 * 角色表
 */
@Table(name = "sys_role")
@Entity
public class Role {
    
    

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "rid")
    private Integer rid;

    @Column(name = "roldName")
    private String roldName;

    @Column(name = "roldMemo")
    private String roldMemo;

    /*配置多对多
     * mappedBy = "users" 对方的属性名
     *放弃维护权
     * */
    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<>();
    
}

角色表来维护多表的关系

package com.wym.domain;

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

/**
 * 用户表
 */
@Table(name = "sys_user")
@Entity
public class User {
    
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "uid")
    private Integer uid;

    @Column(name = "userCode")
    private String userCode;

    @Column(name = "userName")
    private String userName;

    @Column(name = "userPwd")
    private String userPwd;

    @Column(name = "userState")
    private String userState;

    @ManyToMany
    @JoinTable(name = "user_role_rel",//中間表類型
            //中间表user_role_rel字段关联sys_role表的主键字段role_id
            //中间表的外键字段关联当前实体类所对应表的主键字段
            joinColumns = {
    
    @JoinColumn(name = "uid", referencedColumnName = "uid")},
            //中间表user_role_rel的字段关联sys_user表的主键user_id
            //中间表的外键字段关联对方表的主键字段
            inverseJoinColumns = {
    
    @JoinColumn(name = "rid", referencedColumnName = "rid")}
    )
    private Set<Role> roles = new HashSet<>();
}

二,测试多对多的关系
import com.wym.dao.RoleDao;
import com.wym.dao.UserDao;
import com.wym.domain.Role;
import com.wym.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("classpath:applicationContext.xml")
public class testManyToMany {
    
    

    @Autowired
    private RoleDao roleDao;

    @Autowired
    private UserDao userDao;

    @Test
    @Transactional
    @Rollback(false)
    public  void  testSave(){
    
    
        Role role=new Role();
        role.setRoldName("翁艳敏");
        role.setRoldMemo("weqwe");

        User user =new User();
        user.setUserName("weng");
        user.setUserCode("100");
		//建立关联
        role.getUsers().add(user);

        user.getRoles().add(role);

        roleDao.save(role);
        userDao.save(user);

    }
}
三,注解的属性说明
@ManyToMany
	作用:用于映射多对多关系
	属性:
		cascade:配置级联操作。
		fetch:配置是否采用延迟加载。
    	targetEntity:配置目标的实体类。映射多对多的时候不用写。

@JoinTable
    作用:针对中间表的配置
    属性:
    	nam:配置中间表的名称
    	joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段			  			
    	inverseJoinColumn:中间表的外键字段关联对方表的主键字段
    	
@JoinColumn
    作用:用于定义主键字段和外键字段的对应关系。
    属性:
    	name:指定外键字段的名称
    	referencedColumnName:指定引用主表的主键字段名称
    	unique:是否唯一。默认值不唯一
    	nullable:是否允许为空。默认值允许。
    	insertable:是否允许插入。默认值允许。
    	updatable:是否允许更新。默认值允许。
    	columnDefinition:列的定义信息。

Spring Data Jpa 的多表查询

有了上面的多表关系与之而来的就是多表查询了,而Spring Data Jpa 中有这对象导航查询,从而不使用SQL 而是对象的方式来处理查询

/**
 * 测试对象导航查询
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
class testObjectQuery {
    
    
    @Autowired
    private CustomerDao customerDao;

    @Autowired
    private LikeManDao likeManDao;

    /**
     * 测试关系对象导航(查询一个对象的时候,通过此对象查询所有的关联对象)
     */
    @Test
    @Transactional// 解决在java代码中的no session问题
    public void testQuery1() {
    
    
        //查询客户为一的客户
        Customer one = customerDao.getOne(1);
        //查询客户下的所有联系人
        Set<LikeMan> likemans = one.getLikemans();
        for (LikeMan likeMan : likemans) {
    
    
            System.out.println(likeMan);
        }
    }
	
	
    /**
     * 使用Specification的多表查询
     */
    @Test
    public void testFind() {
    
    
        Specification<LikeMan> spec = new Specification<LikeMan>() {
    
    
            @Override
            public Predicate toPredicate(Root<LikeMan> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
    
    
                //创建的过程中,第一个参数为关联对象的属性名称,第二个参数为连接查询的方式(left,inner,right)
                //JoinType.LEFT : 左外连接,JoinType.INNER:内连接,JoinType.RIGHT:右外连接
                Join<LikeMan, Customer> customer = root.join("customer", JoinType.INNER);
                return criteriaBuilder.like(customer.get("name").as(String.class), "weng%");
            }
        };
        List<LikeMan> all = likeManDao.findAll(spec);
        for (LikeMan likeMan :
                all) {
    
    
            System.out.println(likeMan);
        }
    }
}

​ 2010-1-15

​ weng@

猜你喜欢

转载自blog.csdn.net/weng74/article/details/103995380
今日推荐