Specification动态查询 前提 Dao接口必须继承JpaSpecificationExecutor接口
能够使用动态查询的方法 1 2 3 4 5 6 7 8 9 10 11 12 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) ; }
环境搭建 创建项目 引入依赖[配置pom.xml] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 <?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 > cn.itcast</groupId > <artifactId > jpa-day2</artifactId > <version > 1.0-SNAPSHOT</version > <properties > <spring.version > 5.0.2.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 > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <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 > <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 > <dependency > <groupId > c3p0</groupId > <artifactId > c3p0</artifactId > <version > ${c3p0.version}</version > </dependency > <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 > <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 > <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 >
创建包 cn.itcast.service
cn.itcast.dao
cn.itcast.domain
创建applicationContext.xml
Spring整合Jpa就两步
1.由spring创建entityManagerFactory
2.使用标签jpa:repositories和spring进行整合
提示:该文件应该在java/main/resource目录下
注意修改数据库密码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 <?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" > <context:component-scan base-package ="cn.itcast" > </context:component-scan > <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 > <bean id ="transactionManager" class ="org.springframework.orm.jpa.JpaTransactionManager" > <property name ="entityManagerFactory" ref ="entityManagerFactoty" > </property > </bean > <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 > <aop:config > <aop:pointcut id ="pointcut" expression ="execution(* cn.itcast.service.*.*(..))" /> <aop:advisor advice-ref ="txAdvice" pointcut-ref ="pointcut" /> </aop:config > <bean id ="entityManagerFactoty" class ="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > <property name ="dataSource" ref ="dataSource" /> <property name ="packagesToScan" value ="cn.itcast.domain" /> <property name ="persistenceProvider" > <bean class ="org.hibernate.jpa.HibernatePersistenceProvider" /> </property > <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" /> <property name ="showSql" value ="true" /> </bean > </property > <property name ="jpaDialect" > <bean class ="org.springframework.orm.jpa.vendor.HibernateJpaDialect" /> </property > <property name ="jpaProperties" > <props > <prop key ="hibernate.hbm2ddl.auto" > update</prop > </props > </property > </bean > <jpa:repositories base-package ="cn.itcast.dao" transaction-manager-ref ="transactionManager" entity-manager-factory-ref ="entityManagerFactoty" > </jpa:repositories > </beans >
快速入门 创建表cst_customer
提示:day01已经创建过了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 USE `jpa` ;DROP TABLE IF EXISTS `cst_customer` ;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 DEFAULT CHARSET =utf8; insert into `cst_customer` (`cust_id` ,`cust_name` ,`cust_source` ,`cust_industry` ,`cust_level` ,`cust_address` ,`cust_phone` ) values (1 ,'黑马程序员' ,NULL ,'教育' ,NULL ,NULL ,NULL ),(2 ,'传智播客' ,NULL ,'教育' ,NULL ,NULL ,NULL ),(3 ,'酷丁鱼' ,NULL ,'教育' ,NULL ,NULL ,NULL ),(4 ,'传智汇' ,NULL ,'教育' ,NULL ,NULL ,NULL ),(5 ,'传智专修学院' ,NULL ,'教育' ,NULL ,NULL ,NULL ),(6 ,'谷歌' ,NULL ,'教育' ,NULL ,NULL ,NULL );
创建实体类Customer 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package cn.itcast.domain;import javax.persistence.*;public class Customer { private Long custId; private String custName; private String custSource; private String custLevel; private String custIndustry; private String custPhone; private String custAddress; 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; } @Override public String toString () { return "Customer{" + "custId=" + custId + ", custName='" + custName + '\'' + ", custSource='" + custSource + '\'' + ", custLevel='" + custLevel + '\'' + ", custIndustry='" + custIndustry + '\'' + ", custPhone='" + custPhone + '\'' + ", custAddress='" + custAddress + '\'' + '}' ; } }
通过注解配置映射关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 package cn.itcast.domain;import javax.persistence.*;@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; 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; } @Override public String toString () { return "Customer{" + "custId=" + custId + ", custName='" + custName + '\'' + ", custSource='" + custSource + '\'' + ", custLevel='" + custLevel + '\'' + ", custIndustry='" + custIndustry + '\'' + ", custPhone='" + custPhone + '\'' + ", custAddress='" + custAddress + '\'' + '}' ; } }
创建CustomerDao接口 1 2 3 4 5 6 7 8 9 10 11 12 13 package cn.itcast.dao;import cn.itcast.domain.Customer;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.JpaSpecificationExecutor;public interface CustomerDao extends JpaRepository <Customer ,Long >, JpaSpecificationExecutor <Customer > {}
编写测试类
需求:查询姓名=李四的用户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package cn.itcast.dao;import cn.itcast.domain.Customer;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.jpa.domain.Specification;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import javax.persistence.criteria.*;import java.util.List;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:applicationContext.xml" )public class CustomerDaoSpecificationTest { @Autowired private CustomerDao customerDao; @Test public void findByCustNameQueryTest () { Customer customer = customerDao.findOne(new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Path<Object> custName = root.get("custName" ); Predicate predicate = criteriaBuilder.equal(custName, "李四" ); return predicate; } }); System.out.println(customer); } }
运行结果
单条件查询 请查看1.4内容
多条件查询
需求:查询姓名=李四,电话=1390000000的客户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 @Test public void manyConditionsQueryTest () { Customer customer = customerDao.findOne(new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Predicate p1 = criteriaBuilder.equal(root.get("custName" ), "李四" ); Predicate p2 = criteriaBuilder.equal(root.get("custPhone" ), "1390000000" ); Predicate p3 = criteriaBuilder.and(p1, p2); return p3; } }); System.out.println(customer); }
运行结果
模糊匹配 种类 模糊匹配有like,>, < ,>=,<=
对应的方法就是like , gt,lt,ge,le
使用步骤 1.从root中获取要操作的属性
2.调用模糊匹配方法,传入两个参数,第一个是path.as(属性的类型.class),第二个是属性值
测试代码
查询客户名包含传智播客,并且ID>=2的用户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void dimQuery () { Customer cus = customerDao.findOne(new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { Predicate p1 = criteriaBuilder.like(root.get("custName").as(String.class), "%传智播客%"); Predicate p2 = criteriaBuilder.ge(root.get("custId" ).as(Long.class ), 2L ) ; return criteriaBuilder.and(p1, p2); } }); System.out.println(cus); }
运行结果
排序 一个排序条件
需求:按照ID降序查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void orderByTest1 () { Sort sort = new Sort(Sort.Direction.DESC,"custId" ); List<Customer> all = customerDao.findAll(null , sort); for (Customer customer : all) { System.out.println(customer); } }
运行结果
多个排序条件
按照电话降序排序,如果电话相同按照ID升序排序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 @Test public void orderByTest2 () { Sort.Order o1 = new Sort.Order(Sort.Direction.DESC, "custPhone" ); Sort.Order o2 = new Sort.Order(Sort.Direction.ASC, "custId" ); ArrayList orders = new ArrayList(); orders.add(o1); orders.add(o2); Sort sort = new Sort(orders); List<Customer> all = customerDao.findAll(null , sort); for (Customer customer : all) { System.out.println(customer); } }
使用CriteriaQuery完成排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void orderTest3 () { List<Customer> all = customerDao.findAll(new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) { query.where(cb.like(root.get("custName").as(String.class),"%传%")); query.orderBy(cb.desc(root.get("custPhone" )), cb.asc(root.get("custId" ))); return query.getRestriction(); } }); for (Customer customer : all) { System.out.println(customer); } }
运行结果
分页
需求 查询ID=4,5,6的数据
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void pageTest () { PageRequest pageRequest = new PageRequest(1 , 3 ); Page<Customer> all = customerDao.findAll(null , pageRequest); for (Customer customer : all) { System.out.println(customer); } }
运行结果
案例
需求,网页需要一个搜索功能,如果用输入客户名,则可以根据客户名搜索,如果输入电话,则可以根据电话搜索,如果都输入了就根据用户名和电话搜索,问,service层中的方法应该如何实现
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public List<Customer> search (String custName, String custPhone) { List<Customer> customers = customerDao.findAll(new Specification<Customer>() { @Override public Predicate toPredicate (Root<Customer> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) { List<Predicate> list = new ArrayList<>(); if (custName!=null && !custName.trim().equals("" )){ Predicate p = criteriaBuilder.like(root.get("custName").as(String.class), "%" + custName + "%"); list.add(p); } if (custPhone!=null && !custPhone.trim().equals("" )){ Predicate p = criteriaBuilder.like(root.get("custPhone").as(String.class), "%" + custPhone + "%"); list.add(p); } Predicate p = criteriaBuilder.and(list.toArray(new Predicate[0 ])); return p; } }); return customers; } @Test public void searchTest () { List<Customer> customers = search("传" , "1" ); for (Customer customer : customers) { System.out.println(customer); } }
运行结果
多表查询 一对多和多对一 需求 一个Customer[这里用户表示公司]对应多个联系人
创建表
为了避免和之前的数据产生冲突,在讲多表查询的时候表名和实体类名后面都会加数字进行区分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 DROP TABLE IF EXISTS `cst_customer2` ;CREATE TABLE `cst_customer2` ( `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=117 DEFAULT CHARSET =utf8; DROP TABLE IF EXISTS `cst_linkman2` ;CREATE TABLE `cst_linkman2` ( `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 ) DEFAULT NULL COMMENT '客户id(外键)' , PRIMARY KEY (`lkm_id` ), KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id` ) ) ENGINE =InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET =utf8;
创建实体类及映射关系
客户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 package cn.itcast.domain;import javax.persistence.*;import java.util.HashSet;import java.util.Set;@Entity @Table (name="cst_customer2" )public class Customer2 { @Id @GeneratedValue (strategy = GenerationType.IDENTITY) @Column (name="cust_id" ) private Long custId; @Column (name="cust_address" ) private String custAddress; @Column (name="cust_industry" ) private String custIndustry; @Column (name="cust_level" ) private String custLevel; @Column (name="cust_name" ) private String custName; @Column (name="cust_phone" ) private String custPhone; @Column (name="cust_source" ) private String custSource; public Long getCustId () { return custId; } public void setCustId (Long custId) { this .custId = custId; } public String getCustAddress () { return custAddress; } public void setCustAddress (String custAddress) { this .custAddress = custAddress; } 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 getCustName () { return custName; } public void setCustName (String custName) { this .custName = custName; } public String getCustPhone () { return custPhone; } public void setCustPhone (String custPhone) { this .custPhone = custPhone; } public String getCustSource () { return custSource; } public void setCustSource (String custSource) { this .custSource = custSource; } @Override public String toString () { return "Customer{" + "custId=" + custId + ", custAddress='" + custAddress + '\'' + ", custIndustry='" + custIndustry + '\'' + ", custLevel='" + custLevel + '\'' + ", custName='" + custName + '\'' + ", custPhone='" + custPhone + '\'' + ", custSource='" + custSource + '\'' + '}' ; } }
联系人
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 package cn.itcast.domain;import javax.persistence.*;@Entity @Table (name = "cst_linkman2" )public class LinkMan2 { @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; 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; } @Override public String toString () { return "LinkMan{" + "lkmId=" + lkmId + ", lkmName='" + lkmName + '\'' + ", lkmGender='" + lkmGender + '\'' + ", lkmPhone='" + lkmPhone + '\'' + ", lkmMobile='" + lkmMobile + '\'' + ", lkmEmail='" + lkmEmail + '\'' + ", lkmPosition='" + lkmPosition + '\'' + ", lkmMemo='" + lkmMemo + '\'' + '}' ; } }
一对多关系配置及测试
使用一对多的好处:
通过一的一方可以直接获取多的一方
保存一的一方的时候多的一方也会被顺便保存
具体使用方式请查看测试类
1、说明
客户是一的一方
联系人是多的一方
2.在客户实体类(Customer2)中添加集合属性,存储所有的联系人
1 2 3 4 5 6 7 8 9 private Set<LinkMan2> linkMan2s = new HashSet<>();public Set<LinkMan2> getLinkMan2s () { return linkMan2s; } public void setLinkMan2s (Set<LinkMan2> linkMan2s) { this .linkMan2s = linkMan2s; }
3.配置映射关系·
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @OneToMany (targetEntity = LinkMan2.class ) @JoinColumn (name ="lkm_cust_id" ,referencedColumnName = "cust_id" )private Set<LinkMan2> linkMan2s = new HashSet<>();public Set<LinkMan2> getLinkMan2s () { return linkMan2s; } public void setLinkMan2s (Set<LinkMan2> linkMan2s) { this .linkMan2s = linkMan2s; }
4.创建Dao接口
1 2 3 4 5 6 7 8 9 package cn.itcast.dao;import cn.itcast.domain.Customer;import cn.itcast.domain.Customer2;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.JpaSpecificationExecutor;public interface CustomerDao2 extends JpaRepository <Customer2 ,Long >,JpaSpecificationExecutor <Customer2 > {}
1 2 public interface LinkManDao2 extends JpaRepository <LinkMan2 ,Long >,JpaSpecificationExecutor <LinkMan2 > {}
5.测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package cn.itcast.dao;import cn.itcast.domain.Customer2;import cn.itcast.domain.LinkMan2;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;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Set;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:applicationContext.xml" )public class One2ManyTest { @Autowired private CustomerDao2 customerDao2 ; @Autowired private LinkManDao2 linkManDao2; @Test @Transactional @Rollback (false ) public void add () { LinkMan2 lm1 = new LinkMan2(); lm1.setLkmEmail("kefuA@foxmail.com" ); lm1.setLkmGender("男" ); lm1.setLkmMobile("110" ); lm1.setLkmName("客服A" ); LinkMan2 lm2 = new LinkMan2(); lm2.setLkmEmail("kefuB@foxmail.com" ); lm2.setLkmGender("女" ); lm2.setLkmMobile("119" ); lm2.setLkmName("客服B" ); Customer2 customer2 = new Customer2(); customer2.setCustName("JACK" ); customer2.getLinkMan2s().add(lm1); customer2.getLinkMan2s().add(lm2); customerDao2.save(customer2); linkManDao2.save(lm1); linkManDao2.save(lm2); } }
运行结果
多对一关系配置及测试
1.说明
客户(Customer2)[主表]是一的一方
联系人(LinkMan2)[副表]是多的一方
我们需要在多的一方中配置关系
2.在LinkMan2中添加属性CUstomer2
1 2 3 4 5 6 7 8 9 private Customer2 customer2;public Customer2 getCustomer2 () { return customer2; } public void setCustomer2 (Customer2 customer2) { this .customer2 = customer2; }
3.添加映射关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @ManyToOne (targetEntity = Customer2.class ) @JoinColumn (name ="lkm_cust_id" ,referencedColumnName = "cust_id" )private Customer2 customer2;public Customer2 getCustomer2 () { return customer2; } public void setCustomer2 (Customer2 customer2) { this .customer2 = customer2; }
4.测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 package cn.itcast.dao;import cn.itcast.domain.Customer2;import cn.itcast.domain.LinkMan2;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;import java.util.List;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:applicationContext.xml" )public class Many2OneTest { @Autowired private CustomerDao2 customerDao2; @Autowired private LinkManDao2 linkManDao2; @Test public void findAllTest () { List<LinkMan2> all = linkManDao2.findAll(); for (LinkMan2 linkMan2 : all) { Customer2 customer2 = linkMan2.getCustomer2(); System.out.println(customer2); } } @Test @Transactional @Rollback (false ) public void addTest () { LinkMan2 lm = new LinkMan2(); lm.setLkmEmail("kefuB@foxmail.com" ); lm.setLkmGender("女" ); lm.setLkmMobile("119" ); lm.setLkmName("客服BBBB" ); Customer2 customer2 = new Customer2(); customer2.setCustName("JACKBBB" ); lm.setCustomer2(customer2); customerDao2.save(customer2); linkManDao2.save(lm); } }
运行结果
放弃关系维护
为什么要放弃关系维护
在多对一的情况下保存用户和客户只需要2条SQL,都是INSERT
在一对多的情况下保存用户和客户需要3条SQL语句,2条INSERT,1条UPDATE
如果双向一对多的情况下,可能会出现一下情况
问题代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 package cn.itcast.dao;import cn.itcast.domain.Customer2;import cn.itcast.domain.LinkMan2;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 ManyToOne2 { @Autowired private CustomerDao2 customerDao2 ; @Autowired private LinkManDao2 linkManDao2; @Test @Transactional @Rollback (false ) public void addTest () { LinkMan2 linkMan = new LinkMan2(); linkMan.setLkmEmail("郝美丽@foxmail.com" ); linkMan.setLkmGender("女" ); linkMan.setLkmMobile("10086" ); linkMan.setLkmName("郝美丽" ); Customer2 customer = new Customer2(); customer.setCustName("客户" ); linkMan.setCustomer2(customer); customer.getLinkMan2s().add(linkMan); customerDao2.save(customer); linkManDao2.save(linkMan); } }
执行结果
问题
上面代码两个实体类都在维护关系,如果由联系人维护只需要执行2条SQL,如果有客户联系只需要执行3条SQL。运行后发现执行了3条SQL,说明是有客户实体类维护关系的,如果可以由联系人实体类维护关系岂不是更好。
解决办法
其中一个实体类放弃关系的维护,最好客户实体类放弃,因为联系人维护关系可以少执行一条SQL语句
修改客户实体类的OneToMany注解,并删除@JoinColumn注解,代码如下
1 2 3 @OneToMany (mappedBy = "customer2" )private Set<LinkMan2> linkMan2s = new HashSet<>();
级联操作
概念
操作一个实体类对象的同时会操作另一个实体类。
注意事项
1.级联操作一定明确操作主体
2.明确了操作主体后,需要在操作主体上指明允许级联操作
操作方式:在@OnetToMany、@ManyToOne、@ManyToMany注解中添加属性cascade = CascadeType.ALL
cascade 属性说明
定义源实体 和关联的目标实体 间的级联关系。当对源实体 进行操作时,是否对关联的目标实体 也做相同的操作。默认没有级联操作。该参数的可选值有:CascadeType.PERSIST(级联新建)CascadeType.REMOVE(级联删除)CascadeType.REFRESH(级联刷新)CascadeType.MERGE(级联更新)CascadeType.ALL(包含以上四项)
级联保存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 @Test @Transactional @Rollback (false ) public void add () { LinkMan2 lm1 = new LinkMan2(); lm1.setLkmEmail("kefuA@foxmail.com" ); lm1.setLkmGender("男" ); lm1.setLkmMobile("110" ); lm1.setLkmName("客服A" ); LinkMan2 lm2 = new LinkMan2(); lm2.setLkmEmail("kefuB@foxmail.com" ); lm2.setLkmGender("女" ); lm2.setLkmMobile("119" ); lm2.setLkmName("客服B" ); Customer2 customer2 = new Customer2(); customer2.setCustName("JACK" ); customer2.getLinkMan2s().add(lm1); customer2.getLinkMan2s().add(lm2); lm1.setCustomer2(customer2); lm2.setCustomer2(customer2); customer2Dao.save(customer2); }
级联删除 1 2 3 4 5 6 7 8 9 @Test @Transactional @Rollback (false )public void delete () { Customer2 c = customerDao2.findOne(13L ); customerDao2.delete(c); }
多对多 需求 一个用户(User)对应多个角色(Role)
一个角色(Role)可以对应多个用户(User)
创建表 这里不手动创建了,直接由框架自己生成表
编写实体类及映射关系
用户
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 package cn.itcast.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; 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; } }
角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package cn.itcast.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; 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; } }
多对多关系配置
1.给用户实体类(User)添加属性及映射关系
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @ManyToMany (targetEntity = Role.class ,cascade = CascadeType.ALL)@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> roles = new HashSet<>();public Set<Role> getRoles () { return roles; } public void setRoles (Set<Role> roles) { this .roles = roles; }
2.角色(Role)实体类添加属性及映射关系
关系由用户维护就好了,所以在角色实体类中用mapppedBy
1 2 3 4 5 6 7 8 9 10 @ManyToMany (mappedBy = "roles" ) private Set<User> users = new HashSet<>();public Set<User> getUsers () { return users; } public void setUsers (Set<User> users) { this .users = users; }
编写接口
UserDao
1 2 3 4 5 6 7 8 package cn.itcast.dao;import cn.itcast.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 > {}
RoleDao
1 2 3 4 5 6 7 8 9 package cn.itcast.dao;import cn.itcast.domain.Role;import cn.itcast.domain.User;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.data.jpa.repository.JpaSpecificationExecutor;public interface RoleDao extends JpaRepository <Role ,Long > ,JpaSpecificationExecutor <Role > {}
级联保存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package cn.itcast.dao;import cn.itcast.domain.Role;import cn.itcast.domain.User;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;@RunWith (SpringJUnit4ClassRunner.class ) @ContextConfiguration (locations = "classpath:applicationContext.xml" )public class Many2ManyTest { @Autowired private UserDao userDao; @Autowired private RoleDao roleDao; @Test public void addTest () { Role role = new Role(); role.setRoleName("管理员" ); User user = new User(); user.setAge(13 ); user.setUserName("老王" ); user.getRoles().add(role); userDao.save(user); } }
运行结果
级联删除 1 2 3 4 5 6 @Test public void deleteTest () { User user = userDao.findOne(1L ); userDao.delete(user); }
运行结果
级联查询(对象导航查询) 1 2 3 4 5 6 7 8 @Test public void findTest () { User user = userDao.findOne(2L ); Set<Role> roles = user.getRoles(); for (Role role : roles) { System.out.println(role.getRoleName()); } }
报错
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:
解决方案1:在User实体类的@ManyToMany注解中添加值fetch
1 @ManyToMany(targetEntity = Role.class,cascade = CascadeType.ALL,fetch = FetchType.EAGER)
解决方案2:在junit测试方法findTest()上加@Transactional
一对多关系对象查询总结 通过一的一方查询多的一方默认是延迟查询,因为多的一方可能数据非常多,浪费时间,浪费内存
通过多的一方查询一的一方默认是立即查询,因为一的一方就一条数据,性能可以忽略不计
推荐:使用默认设置,如果使用默认设置,在进行对象查询的时候可能会报org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:,原因是事务关闭了,我们需要加@Transactional