spring-data jpa
笔记
1.什么是jpa
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
持久化:
何谓"持久化" 持久(Persistence),即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的数据存储在关系型的数据库中
2.为什么要学***a
优势:
标准化
JPA 是 JCP 组织发布的 Java EE 标准之一,因此任何声称符合 JPA 标准的框架都遵循同样的架构,提供相同的访问API,这保证了基于JPA开发的企业应用能够经过少量的修改就能够在不同的JPA框架下运行。
容器级特性的支持
JPA框架中支持大数据集、事务、并发等容器级事务,这使得 JPA 超越了简单持久化框架的局限,在企业应用发挥更大的作用。
简单方便
JPA的主要目标之一就是提供更加简单的编程模型:在JPA框架下创建实体和创建Java 类一样简单,没有任何的约束和限制,只需要使用 javax.persistence.Entity进行注释,JPA的框架和接口也都非常简单,没有太多特别的规则和设计模式的要求,开发者可以很容易地掌握。JPA基于非侵入式原则设计,因此可以很容易地和其它框架或者容器集成。
查询能力
高级特性
小结:
EJB 3.0和JPA 毫无疑问将是Java EE 5的主要卖点。在某些领域中,它们给Java社区带来了竞争优势,并使Java 在其他领域与竞争对手不分伯仲(因为,不可否认,某些领域尚不存在基于标准的方法)。
过去数年来,Spring Framework一直是EJB在企业领域的主要竞争对手。EJB 3.0规范解决了很多促进Spring兴起的问题。随着它的出现,EJB3.0毫无疑问比Spring提供了更好的开发体验--最引人注目的优势是它不需要配置文件。
JPA提供一种标准的OR映射解决方案,该解决方案完全集成到EJB3.0兼容的容器中。JPA的前辈将会继续稳定发展,但是业务应用程序中的 raw 使用将可能会减少。实现 JPA 兼容的实体管理器似乎很可能是此类技术的发展方向。
Java EE系列规范的较大问题与JPA没有任何关系。Java EE 系列规范的问题涉及到 Web和EJB容器之间的集成。Spring在此领域仍然具有主要竞争优势。JBoss的Seam项目尝试使用自定义的方法来解决这一问题。Caucho Resin应用服务器试图扩展容器边界并支持在Web容器中使用@EJB注释。我们希望Java EE 5.1将解决层集成的问题,为我们提供一个全面而标准的依赖性注入方法。
在不久的将来,Oracle可能会将JPA作为一个单独的JSR对待,同时JPA还可能作为Java SE的一部分。不过这些都不太重要,重要的是,我们已经可以在脱离容器的情况下、在Java SE应用中使用JPA了。
JPA已经作为一项对象持久化的标准,不但可以获得Java EE应用服务器的支持,还可以直接在Java SE中使用。开发者将无需在现有多种ORM框架中艰难地选择,按照Sun的预想,现有ORM框架头顶的光环将渐渐暗淡,不再具有以往的吸引力。
jpa和hibernate
JPA本身是一种规范,它的本质是一种ORM规范(不是ORM框架,因为JPA并未提供ORM实现,只是制定了规范)因为JPA是一种规范,所以,只是提供了一些相关的接口,但是接口并不能直接使用,JPA底层需要某种JPA实现,JPA现在就是Hibernate功能的一个子集
JPA和Hibernate之间的关系,可以简单的理解为JPA是标准接口,Hibernate是实现,并不是对标关系,借用下图可以看清楚他们之间的关系,Hibernate属于遵循JPA规范的一种实现,但是JPA是Hibernate遵循的规范之一,Hibernate还有其他实现的规范
3.如何使用jpa->快速入门
1)创建maven项目导入依赖
<dependencies>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.6</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.1.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.21.Final</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.9.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
2)编写xml配置applicationContext.xml文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <!--spring 和 spring data jpa的配置--> <!-- 1.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="com.gg.domain"/> <!--配置jpa实现厂家--> <property name="persistenceProvider"> <bean class="org.hibernate.jpa.HibernatePersistenceProvider"/> </property> <!--3.配置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> <!--4.整合spring data jpa--> <!--[dao]扫描包--> <jpa:repositories base-package="com.gg.dao" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory"> </jpa:repositories> <!--5.配置事务管理器--> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory"/> </bean> <!--6.声明式事务--> <!--7.配置spring注解扫描包--> <context:component-scan base-package="com.gg"/> </beans>
3)编写实体类与实体类映射关系
@Entity @Table(name = "cst_customer") public class Customer { 略
4)编写jpa的dao层接口
- 需要继承两个接口(JpaRepository,JpaSpecificationExecutor)
- 需要提供响应的泛型
/** * 符合SpringDataJpa的dao层接口规范 * JpaRepository <操作的实体类类型,实体类中主键属性的类型> * 封装了基本的CRUD操作 * JpaSpecificationExecutor<操作的实体类类型> * 封装了复杂查询(分页等..) */ public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { //这里什么都不用写,就可以完成基本的CRUD操作 }
5)测试
/** * 根据id查询 */ @Test public void test1(){ Customer customer = customerDao.findById(3L).get(); System.out.println(customer); }
6)增删查改
@RunWith(SpringJUnit4ClassRunner.class) //声明spring提供的单元测试环境 @ContextConfiguration(locations = "classpath:applicationContext.xml") //指定spring容器的配置信息 public class CustomerDaoTest { @Autowired private CustomerDao customerDao; /** * 根据id查询 */ @Test public void test1(){ Customer customer = customerDao.findById(3L).get(); System.out.println(customer); } /** * save方法:存在记录就更新 */ @Test public void test2(){ Customer customer = new Customer(); customer.setCustId(1L); customer.setCustName("700"); customer.setCustAddress("000"); customer.setCustSource("000"); Customer save = customerDao.save(customer); System.out.println(save); } /** * save方法:不存在记录就添加 */ @Test public void test3(){ Customer customer = new Customer(); customer.setCustId(4L); customer.setCustName("700"); customer.setCustAddress("000"); customer.setCustSource("000"); Customer save = customerDao.save(customer); System.out.println(save); } /** * 删除 */ @Test public void test4(){ customerDao.deleteById(5L); }
spring-data Jpa执行过程
接口
真正发挥作用:
接口的实现类
在程序执行的过程中,自动的帮助我们动态生成了接口的实现类的接口
如果动态的生成实现类对象
动态代理(生成基于接口的实现类的对象)
流程
customerDao.findById(3L).get() 1.通过JDKDynamicAopProxy创建动态代理对象 2.动态代理对象:simpleJpaRepository 实现了JpaRepository接口,实现了JpaSpecificationExecutor接口 3.findOne():通过entityManager完成查询操作 public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> { public interface JpaRepositoryImplementation<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> { 声明对此包下的dao接口进行动态代理增强! <jpa:repositories base-package="com.gg.dao" transaction-manager-ref="transactionManager" entity-manager-factory-ref="entityManagerFactory"> </jpa:repositories>
spring-data jpa 高级查询
接口原方式
1.查询总数量
/** * 测试统计查询:查询客户的总数量 */ @Test public void test6(){ long count = customerDao.count(); //查询全部的客户数量 System.out.println(count); }
2.判断id为4的客户是否存在
/** * 判断id为4的客户是否存在 * 1.可以查询一下id为4的客户 * 如果值为空,代表不存在,如果不为空,代表存在 * 2.判断数据库中id为4的客户的数量 * 如果数量为0,代表不存在,如果大于0,代表存在 */ @Test public void test7(){ boolean exists = customerDao.existsById(4L); System.out.println(exists); }
3.根据id从数据库查询
/** * 根据id从数据库查询 */ @Test @Transactional //保证getOne正常运行 public void test8(){ Customer customer = customerDao.getOne(3L); System.out.println(customer); }
jpql查询方式
1.使用jpql查询用户
public interface CustomerDao extends JpaRepository<Customer,Long>, JpaSpecificationExecutor<Customer> { //这里什么都不用写,就可以完成基本的CRUD操作 /** * 案例:根据客户名称查询客户 * 使用jpql的形式查询 * jpql:from Customer where custName = ? * * * 配置jpql语句,使用的@Query注解 */ @Query(value = "from Customer where custName = ?1") public Customer findJpql(String custName); @Test public void testFindJPQL(){ Customer customer = customerDao.findJpql("可可"); System.out.println(customer); }
2.根据客户名称和客户id查询客户
/** * 案例:根据客户名称和客户id查询客户 * jpql:from Customer where custName = ? and custId = ? * * 对于多个占位符参数 * 赋值的时候,默认的情况下,占位符的位置需要和方法参数中的位置保持一致 */ @Query(value = "from Customer where custName = ?1 and custId = ?2") public Customer findCustNameAndId(String custName,Long Id); @Test public void testFindJPQL1(){ Customer customer = customerDao.findCustNameAndId("700",1L); System.out.println(customer); } * 可以指定占位符参数的位置 */ @Query(value = "from Customer where custId = ?2 and custName = ?1") public Customer findCustNameAndId(String custName,Long Id);
3.!!! 使用jpql完成更新操作 !!!
/** * 使用jpql完成更新操作 * 案例:根据id更新,客户的名称 * 更新4号客户的名称 * sql:update cst_customer set cust_name = ? where cust_id = ? * jpql: update Customer set custName = ? where custId = ? * * @Query:代表的是进行查询 * * 声明此方法是用来进行更新操作 * @Modifying * * 当前执行的是一个更新操作 */ @Query(value = "update Customer set custName = ?2 where custId = ?1") @Modifying void updateCustomer(Long custId,String CustName); /** * 测试jpql的更新操作 * * springDataJpa中使用jpql完成 更新/删除操作 * * 需要手动添加事务的支持 * * 默认会执行结束之后,回滚事务 * @Rollback设置是否自动回滚 * false true */ @Test @Transactional //添加事务支持 @Rollback(value = false) 默认会自动回滚,需要取消回滚 public void testFindJPQL2(){ customerDao.updateCustomer(1L,"007"); }
4.SQL查询
/** * sql语句的查询 * 1.特有的查询:需要在dao接口上配置方法 * 2.在新添加的方法上,使用注解的形式配置sql查询语句 * 3.注解:@Query * value:jpql语句 | sql语句 * nativeQuery : false(使用jpql查询) | true(使用本地查询:sql查询) * 是否使用本地查询 */ // 使用sql的形式查询 // 查询全部的客户 //sql: select * from cst_customer @Query(value = "select * from cst_customer",nativeQuery = true) List<Object []> findSql(); @Test public void test1(){ /* Hibernate: select * from cst_customer [1, 000, null, null, 007, null, 000] [2, 11, null, null, gg, null, 213] [3, 001, 乱码, null, 可可, null, 213] [4, 001, 乱码, null, 可惜, null, 213] */ List<Object[]> objects = customerDao.findSql(); for (Object[] object : objects) { System.out.println(Arrays.toString(object)); } }
5.模糊查询
/** * sql模糊查询 */ @Query(value = "select * from cst_customer where cust_name like ?1",nativeQuery = true) List<Customer> findLikeCustomer(String custName); @Test public void test2(){ List<Customer> customerList = customerDao.findLikeCustomer("可%"); customerList.forEach(System.out::println); }
测试结果:
Hibernate: select * from cst_customer where cust_name like ? Customer{custId=3, custName='可可', custSource='213', custIndustry='乱码', custLevel='null', custAddress='001', custPhone='null'} Customer{custId=4, custName='可惜', custSource='213', custIndustry='乱码', custLevel='null', custAddress='001', custPhone='null'}
方法名称规则查询
-
是对jpql查询更加深层的一层封装,我们只需要按照SpringData提供的方法名称规则定义方法,不需要再去配置jpql语句,完成查询
_1.精准查询
/** * 方法名的约定 * findBy:查询 * 对象中的属性名(首字母大写),查询的条件 * findByCustName -> 根据客户名称查询 * 在springdataJpa的运行阶段 * 会根据方法名称进行解析 findBy from xxx(实体类) * 属性名称 where custName = ? */ Customer findByCustName(String custName); @Test public void test3(){ Customer customer = customerDao.findByCustName("可可"); System.out.println(customer); }
_2.单条件查询
/** * 方法名的约定 * findBy:查询 * 对象中的属性名(首字母大写),查询的条件 * CustName * * 默认情况 : 使用等于的方式查询 * 特殊的查询方式 * * findByCustName -> 根据客户名称查询 * 在springdataJpa的运行阶段 * 会根据方法名称进行解析 findBy from xxx(实体类) * 属性名称 where custName = ? * * * findBy + 属性名称 (根据属性名称进行完成匹配的查询) * findBy + 属性名称 + "查询方式(Like | isnull)" * findByCustNameLike */ List<Customer> findByCustNameLike(String name); @Test public void test4(){ List<Customer> list = customerDao.findByCustNameLike("可%"); list.forEach(System.out::println); }
_3.多条件查询
* 3.多条件查询 * findBy + 属性名 + "查询方式" + "多条件的连接符(AND | OR)" + 属性名 + "查询方式" /** * 使用客户名称模糊匹配 */ Customer findByCustNameLikeAndCustSource(String name,String source); @Test public void test5(){ Customer customer = customerDao.findByCustNameLikeAndCustSource("%可", "213"); System.out.println("customer = " + customer); }
Specifications动态查询
Specification:查询条件
自定义我们自己的Specification实现类
实现
//root:查询的根对象(查询的任何属性都可以从根对象中获取)
//CriteriaQuery:顶层查询对象,自定义查询方式(了解:一般不用)
//CriteriaBuilder:查询的构造器,封装了很多的查询条件
Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,CriteriaBuilder cb) //封装查询条件
1)自定义查询,单条件精准查询
/** * 根据条件:查询单个对象 */ @Test public void test(){ /** * 自定义查询条件 * 1.实现Specification接口(提供泛型,查询的对象类型) * 2.实现toPredicate方法(构造查询条件) * 3.需要借助方法参数中的两个参数 * root:获取需要查询的对象属性 * CriteriaBuilder:构造查询条件的,内部封装了很多的查询条件(模糊匹配,精准匹配) * 案例:根据客户名称查询,查询客户名为xxx的客户 * 查询条件 * 1.查询方式 * cb对象 * 2.比较的属性名称 * root对象 * 3. */ Specification<Customer> spec = new Specification<Customer>() { /** * 匿名内部类 * @param root * @param query * @param criteriaBuilder * @return */ @Override public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { // 1.获取比较的属性 Path<Object> custName = root.get("custName"); // 2.构造查询 select * from cst_customer where cust_name = 'xxx' // criteriaBuilder.equal(需要比较的属性,比较的取值) Predicate predicate = criteriaBuilder.equal(custName, "可惜");// 进行精准匹配(比较的属性名,比较的属性的取值) return predicate; } }; Optional<Customer> customer = customerDao.findOne(spec); System.out.println(customer); }
2)多条件查询
/** * 多条件查询 * 案例:根据客户名称和客户xxx查询 */ @Test public void test1(){ /** * root:获取属性 * 客户名 * 其他 * cb:构造查询 * 1.构造客户名的精准匹配查询 * 2.构造其他属性的精准匹配查询 * 3.将以上两个查询联系起来 */ Specification<Customer> spec = new Specification<Customer>() { @Override public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { Path<Object> custName = root.get("custName"); Path<Object> custSource = root.get("custSource"); // 查询条件1 Predicate predicate1 = criteriaBuilder.equal(custName, "007"); // 查询条件2 Predicate predicate2 = criteriaBuilder.equal(custSource, "000"); // 将多个查询条件组合(满足条件1和条件2,与关系 / 满足条件1或满足条件2,或关系) Predicate resPredicate = criteriaBuilder.and(predicate1, predicate2); return resPredicate; } }; Optional<Customer> customer = customerDao.findOne(spec); System.out.println(customer); }
3)模糊查询,定义查询字段的类型
/** * 案例:完成根据客户名称的模糊匹配,返回客户列表 * * equal : 直接得到path(属性),然后进行比较即可 * gt lt le like : 得到path对象,根据path指定比较的参数类型,再去进行比较 * 指定参数类型:path.as(类型的字节码对象) */ @Test public void test3(){ Specification<Customer> spec = new Specification<Customer>() { @Override public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { Path<Object> custName = root.get("custName"); Predicate predicate = criteriaBuilder.like(custName.as(String.class),"可%"); return predicate; } }; List<Customer> list = customerDao.findAll(spec); list.forEach(System.out::println); }
4)添加排序
/** * 添加排序规则 */ @Test public void test4(){ Specification<Customer> spec = new Specification<Customer>() { @Override public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) { Path<Object> custName = root.get("custName"); Predicate predicate = criteriaBuilder.like(custName.as(String.class),"可%"); return predicate; } }; // 添加排序 // 创建排序对象 // 第一个参数:排序的顺序(倒序,正序) // 第二个参数:排序的属性名称 Sort sort = Sort.by(Sort.Direction.DESC, "custId"); List<Customer> list = customerDao.findAll(spec,sort); list.forEach(System.out::println); }
5) 分页
/** * 分页查询 * findAll(Specification,Pageable):带有条件的分页 * findAll(Pageable):没有条件的分页 * * 返回:Page(springDataJpa为我们封装好的pageBean对象,数据列表,总条数) */ @Test public void test5(){ /** * PageRequest对象是Pageable接口的实现类 * * 创建PageRequest的过程中,需要调用它的构造方法传入两个参数 * 第一个参数:当前查询的页数 * 第二个参数:每页查询的数量 */ Pageable pageable = PageRequest.of(1, 3); Specification spec = new Specification() { @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder criteriaBuilder) { return null; } }; Page<Customer> customerPage = customerDao.findAll(spec,pageable); System.out.println("customerPage.getTotalElements() = " + customerPage.getTotalElements()); System.out.println("customerPage.getTotalPages() = " + customerPage.getTotalPages()); // System.out.println("customerPage.getContent() = " + customerPage.getContent()); System.out.println("customerPage.getNumber() = " + customerPage.getNumber()); System.out.println("customerPage.getNumberOfElements() = " + customerPage.getNumberOfElements()); System.out.println("customerPage.getPageable() = " + customerPage.getPageable()); // customerPage.forEach(System.out::println); System.out.println("customerPage.nextPageable() = " + customerPage.nextPageable()); }
多表之间的关系和操作多表的操作步骤
完成多表操作
-
一对多查询
添加表对应实体类
@Entity @Table(name = "cst_linkman") public class LinkMan { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "lkm_id") private Long lkmId; @Column(name = "lkm_name") private String lkmName; @Column(name = "lkm_gender") private String lkmGender; @Column(name = "lkm_phone") private String lkmPhone; @Column(name = "lkm_mobile") private String lkmMobile; @Column(name = "lkm_email") private String lkmEmail; @Column(name = "lkm_position") private String lkmPosition; @Column(name = "lkm_memo") private String lkmMemo; /*配置联系人到客户的多对一关系 使用注解的形式配置多对一关系 1.配置表关系 2.配置外键(中间表) */ @ManyToOne(targetEntity = Customer.class) @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") private Customer customer;
添加表关系
/** * 使用注解的形式配置多表关系 * 1.声明关系 * @OneToMany(targetEntity = LinkMan.class) * 2.配置外键(中间表) * @JoinColumn:配置外键 * name:外键字段名称 * referencedColumnName:参照的主表的主键字段名称 * * * * 在客户实体类上(一方)添加了外键的配置,所以对于客户而言,也具备了维护外键 作用 */ // 配置客户和联系人之间的关系(一对多关系) @OneToMany(targetEntity = LinkMan.class) @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") private Set<LinkMan> linkMans = new HashSet<>();
测试
@Autowired private LinkManDao linkManDao; @Autowired private CustomerDao customerDao; @Test @Transactional @Rollback(value = false) public void test(){ Customer customer = new Customer(); customer.setCustName("西西"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("米米"); linkMan.setCustomer(customer); customerDao.save(customer); linkManDao.save(linkMan); } @Test @Transactional @Rollback(value = false) public void test1(){ Customer customer = new Customer(); customer.setCustName("西西1"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("米米1"); // linkMan.setCustomer(customer); List<LinkMan> list = new ArrayList<LinkMan>(); list.add(linkMan); customer.setLinkMans(list); linkMan.setCustomer(customer); customerDao.save(customer); linkManDao.save(linkMan); }
放弃外键保护
// @OneToMany(targetEntity = LinkMan.class) // @JoinColumn(name = "lkm_cust_id",referencedColumnName = "cust_id") /** * 放弃外键维护权 * mapperBy:对方配置关系的属性名称 */ @OneToMany(mappedBy = "customer") private List<LinkMan> linkMans;
一对多查询(获取类联系中的所属集合元素) 使用延迟加载
@Test @Transactional @Rollback(value = false) public void test4(){ Customer customer = customerDao.getOne(96L); List<LinkMan> linkMans = customer.getLinkMans(); for (LinkMan linkMan : linkMans) { System.out.println("linkMan = " + linkMan); } }
延迟加载:FetchType
* fetch:配置关联对象的加载方式 * FetchType.EAGER:立即加载 * FetchType.LAZY:延迟加载 */ @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL,fetch = FetchType.EAGER)
多对一查询(获取联系集合属于哪一个类) 使用立即加载
@Test @Transactional @Rollback(value = false) public void test5(){ LinkMan linkMan = linkManDao.getOne(5L); Customer customer = linkMan.getCustomer(); System.out.println("customer = " + customer); }
级联操作配置
级联添加
@OneToMany(mappedBy = "customer",cascade = CascadeType.ALL) private List<LinkMan> linkMans; /** * 级联添加:保存一个客户的同时,保存客户的所有联系人 * 需要在操作主题的实体类上,配置cascade属性 */ @Test @Transactional @Rollback(value = false) public void test2(){ Customer customer = new Customer(); customer.setCustName("111"); LinkMan linkMan = new LinkMan(); linkMan.setLkmName("2221"); linkMan.setCustomer(customer); List<LinkMan> list = new ArrayList<>(); list.add(linkMan); customer.setLinkMans(list); customerDao.save(customer); }
级联删除
/** * 级联删除 * 删除1号客户的同时,删除1号客户的所有联系人 */ @Test @Transactional @Rollback(value = false) public void test3(){ customerDao.deleteById(94L); }
级联的其他参数
* cascade:配置级联 * CascadeType.ALL : 所有 * MERGE :更新 * PERSIST :保存 * REMOVE :删除 */ @OneToMany(mappedBy = "customer",cascade = CascadeType.ALL) private List<LinkMan> linkMans;
-
多对多查询
/** * 配置多对多 */ // @ManyToMany(targetEntity = User.class) // 配置多表关系 // @JoinTable(name = "sys_user_role", // 中间表 // // 当前对象在中间表中的外键 // joinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")}, // // inverseJoinColumns,对方对象在中间表的外键 // inverseJoinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")} // ) @ManyToMany(mappedBy = "roles",cascade = CascadeType.ALL) private Set<User> users = new HashSet<>(); /** * 配置用户到角色的多对多关系 * 配置多对多映射关系 * 1.声明表关系的配置 * @ManyToMany(targetEntity = Role.class) 配置多对多 * targetEntity:代表对方的实体类字节码 * 2.配置中间表(包含两个外键) * @JoinTable:中间表的名称 * joinColumns:配置当前对象在中间表的外键 * @JoinColumn数组 * name:外键名 * referencedColumnName:参照的主表的主键名 * inverseJoinColumns:配置对方对象在中间表的外键 * */ @ManyToMany(targetEntity = Role.class) @JoinTable(name = "sys_user_role", // 当前对象在中间表中的外键 joinColumns = {@JoinColumn(name = "sys_user_id",referencedColumnName = "user_id")}, // inverseJoinColumns,对方对象在中间表的外键 inverseJoinColumns = {@JoinColumn(name = "sys_role_id",referencedColumnName = "role_id")} ) private Set<Role> roles = new HashSet<>();
测试
@Test @Transactional @Rollback(value = false) public void test1(){ User user = new User(); user.setUserName("小李"); Role role = new Role(); role.setRoleName("前端工程师"); // role.getUsers().add(user); user.getRoles().add(role); userDao.save(user); roleDao.save(role); } @Test @Transactional @Rollback(value = false) public void test2(){ // Optional<User> user = userDao.findById(1L); userDao.deleteById(1L); }
时间花在哪里,成就就在哪里

浙公网安备 33010602011771号