SpringBoot05: JPA

1、概述

1.1 定义

JPA Spring Data:致力于减少数据访问层(DAO)的开发量。开发者唯一要做的,就只是声明持久层的接口,其他都交给Spring DataJPA来帮你完成!

1.2 使用步骤
  1. 声明持久层的接口,该接口继承Repository

    Repository是一个标记型接口,它不包含任何方法,如必要,Spring Data可实现Repository其他子接口,其中定义了一些常用的增删改查,以及分页相关的方法

  2. 在接口中声明需要的方法

    Spring Data将根据给定的策略来为其生成实现代码

  3. 在Spring配置文件中配置Spring Data,让Spring为声明的接口创建代理对象

    配置了<jpa:repositories>后,Spring初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承Repository或其子接口的接口创建代理对象,并将代理对象注册为Spring Bean,业务层便可以通过Spring自动封装的特性来直接使用该对象

2、环境搭建

  • 配置文件
<?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:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	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/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!-- 1. 配置自动扫描的包 -->
	<context:component-scan base-package="com.moc"/>

	<!-- 2. 配置数据源 -->
	<context:property-placeholder location="classpath:db.properties"/>
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="user" value="${jdbc.user}"/>
		<property name="password" value="${jdbc.password}"/>
		<property name="driverClass" value="${jdbc.driverClass}"/>
		<property name="jdbcUrl" value="${jdbc.jdbcUrl}"/>
		<!-- 配置其他属性 -->
	</bean>


	<!-- 3. 配置 JPA 的 EntityManagerFactory -->
	<!-- 配置EntityManagerFactory相关属性设置 -->
	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<!-- 配置数据源 -->
		<property name="dataSource" ref="dataSource"/>
		<!-- 配置扫描entity的路径 -->
		<property name="packagesToScan" value="com.moc.entity"/>
		<!-- 配置JPA的产品提供的适配器 -->
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
		</property>
		<!-- 配置JPA的基本属性 -->
		<property name="jpaProperties">
			<props>
				<!-- 二级缓存相关 -->
				<!--  
				<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</prop>
				<prop key="net.sf.ehcache.configurationResourceName">ehcache-hibernate.xml</prop>
				-->
				<!-- 生成的数据表的列的映射策略 -->
				<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop> <!--建表策略 -->
				<!-- hibernate 基本属性 -->
				<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
				<prop key="hibernate.show_sql">true</prop>
				<prop key="hibernate.format_sql">true</prop>
				<prop key="hibernate.hbm2ddl.auto">update</prop>
			</props>
		</property>
	</bean>

	<!-- 4. 配置事务管理器 -->
	<bean id="tm" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory"/>
	</bean>

	<!-- 5. 配置支持注解的事务 -->
	<tx:annotation-driven transaction-manager="tm"/>


	<!-- 6. 配置 SpringData -->
	<!-- 加入  jpa 的命名空间 -->
	<!-- base-package: 扫描 Repository Bean 所在的 package -->
	<jpa:repositories base-package="com.moc.dao"
		entity-manager-factory-ref="entityManagerFactory"
		transaction-manager-ref="tm"/>
</beans>

3、Repository

3.1 Repository

​ 是一个标记接口,继承图如下(PersonXxx为自定义的)

在这里插入图片描述

3.2 CrudRepository
  • 方法
    在这里插入图片描述

  • 实例

    @Test
    public void test2() {
        List<Person> list = new ArrayList<>();
        list.add(new Person("zh1", "zh1@163.com", 22, new Date()));
        list.add(new Person("zh2", "zh3@163.com", 23, new Date()));
        list.add(new Person("zh3", "zh4@163.com", 24, new Date()));
    	// Iterable -- 批量写入
        personRepository.save(list);
    }
    
3.3 PagingAndSortingRepository

​ 重要方法:Page<T> findAll(Pageable var1);

@Test
public void test3() {
    Sort.Order order = new Sort.Order(Sort.Direction.ASC, "id");
    List<Sort.Order> orders = new ArrayList<>();
    orders.add(order);

    Sort sort = new Sort(orders);
    Pageable pageable = new PageRequest(0, 10, sort);
    Page<Person> pages = personRepository.findAll(pageable);

    System.out.println("pages.getTotalElements()" + pages.getTotalElements()); // 总条数
    System.out.println("pages.getTotalPages()" + pages.getTotalPages());  // 总页数
    System.out.println("pages.getSize()" + pages.getSize());  // 每页的条数
    System.out.println("pages.getNumberOfElements()" + pages.getNumberOfElements());  // 得到的条数
    System.out.println("pages.getContent()" + pages.getContent());  // 获取的内容
}
3.4 JpaRepository
  • 方法
    在这里插入图片描述
@Test
public void test4() {
    Person person = new Person();
    person.setAge(11);
    person.setBirth(new Date());
    person.setEmail("xxx@1ss.com");
    person.setName("z33");
    person.setId(1);
    // 有主键更新,无主键插入
    personRepository.saveAndFlush(person);
}
3.5 JpaSpecificationExecutor

​ 不属于Repository体系,实现一组 JPA Criteria 查询相关的方法

  • 方法
    在这里插入图片描述
  • 带条件得分页查询
public void test5() {
    Sort.Order order = new Sort.Order(Sort.Direction.ASC, "id");
    List<Sort.Order> orders = new ArrayList<>();
    orders.add(order);

    Sort sort = new Sort(orders);
    Pageable pageable = new PageRequest(0, 10, sort);


    Specification specification = new Specification<Person>(){
        /**
             * 带条件查询的分页
             *
             * @param root            导航root根对象,获得对象的属性(属性的属性的)path路径依赖
             * @param criteriaQuery   几乎不用
             * @param criteriaBuilder CriteriaBuilder的查询工厂类,类似where后面的各种条件组合
             * @return Predicate        Criteria的一个查询条件
             */
        @Override
        public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            Predicate p = criteriaBuilder.gt(root.get("id"), 5);  // gt 大于,ge 大于等于
            Predicate p2 = criteriaBuilder.equal(root.get("name"), "gg");
            return criteriaBuilder.and(p, p2);
        }
    };
    Page<Person> pages = personRepository.findAll(specification, pageable);

    System.out.println("pages.getTotalElements()" + pages.getTotalElements());
    System.out.println("pages.getTotalPages()" + pages.getTotalPages());
    System.out.println("pages.getSize()" + pages.getSize());
    System.out.println("pages.getNumberOfElements()" + pages.getNumberOfElements());
    System.out.println("pages.getContent()" + pages.getContent());
}

4、查询关键字

4.1 简单条件查询
  • 简单条件查询:查询某一个实体类或者集合

  • 按照 Spring Data 的规范,查询方法以 find | read | get 开头,

    涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性以首字母大写

例如:定义一个 Entity 实体类

class Userprivate String firstName; 
	private String lastName;

使用And条件连接时,应这样写:
findByLastNameAndFirstName(String lastName, String firstName);
条件的属性名称与个数要与参数的位置与个数一一对应

4.2 支持的关键字

​ 直接在接口中定义查询方法,如果是符合规范的,可以不用写实现,目前支持的关键字写法如下:
在这里插入图片描述在这里插入图片描述

4.3 查询方法解析流程

可以明确在属性之间加上 “_” 以显式表达意图,表示在分隔(在类里面)

@Table(name = "tbl_person")
@Entity
public class Person {
    @Id
    @GeneratedValue
    private Integer id;

    // 列名为address_id,同时外键关联
    @JoinColumn(name = "address_id")  
    @ManyToOne
    private Address address;

    private String addressCity;  // 区分上面
    
    public Person(){}
}

@Table(name = "tbl_address")
@Entity
public class Address {
    @Id
    @GeneratedValue
    private Integer id;
    private String city;

    public Address() { }
}

@Test
public void test7() {
    // 查询类里的属性 AddressCity
    List<Person> list1 = personRepository.getByAddressCityLike("%bj%");
    // 查询类里的属性 Address 的级联的 City
    List<Person> list2 = personRepository.getByAddress_CityLike("%bj%");
    System.out.println(list1.size());
    System.out.println(list2.size());
}

5、注解

5.1 @Query

​ 这种查询可以声明在 Repository 方法中,摆脱像命名查询那样的约束,将查询直接在相应的接口方法中声明,结构更为清晰

  • 索引参数如下所示,索引值从1开始,查询中 ”? X” 个数需要与方法定义的参数个数相一致,并且顺序也要一致
  • 命名参数(推荐使用这种方式):可以定义好参数名,赋值时采用**@Param(“参数名”)**,而不用管顺序,SQL中使用 :参数名
  • 如果是 @Query 中有 LIKE 关键字,后面的参数需要前面或者后面加 %,这样在传递参数值的时候就可以不加 %
// SQL中使用Entity类
@Query(value = "select p from Person p where p.name=? and p.email=?")
List<Person> getByNameAndEmail(String city, String name);

// 使用原生的SQL
@Query(value = "select email from tbl_person where name = :city and email = :name", nativeQuery = true)
    List<String> getByNameAndEmail2(@Param("city") String city, @Param("name") String name);

// 测试
@Test
public void test8() {
    List<Person> list = personRepository.getByNameAndEmail("rr", "rr@qq.com");
    System.out.println(list.size());

    List<String> list2 = personRepository.getByNameAndEmail2("rr", "rr@qq.com");
    System.out.println(list.size());
}

// like
@Query("select o from UserModel o where o.name like ?1%")
public List<UserModel> findByUuidOrAge(String name);
@Query("select o from UserModel o where o.name like %?1")
public List<UserModel> findByUuidOrAge(String name);
@Query("select o from UserModel o where o.name like %?1%")
public List<UserModel> findByUuidOrAge(String name);
5.2 @Modifying
  • @Query 与 @Modifying 这两个 annotation一起声明,可定义个性化更新操作,例如只涉及某些字段更新时最为常用
  • 注意:
  1. 方法的返回值应该是 int,表示更新语句所影响的行数
  2. 在调用的地方必须加事务,没有事务不能正常执行
// dao层
@Modifying
@Query(value = "update Person p set p.email=? where p.id=?")
int updateEmail(String email, Integer id);

// service层
@Transactional
public int updateEmail(String email, Integer id) {
    return personRepository.updateEmail(email, id);
}

// 测试
@Test
public void test9() {
    // 需要事务
    // int result = personRepository.updateEmail("abc@163.com", 1);
    int result = personService.updateEmail("abc@163.com", 1);
    System.out.println(result);
}

6、自定义Repository

​ 为某一个 Repository 上添加自定义方法

实现步骤
  • 新建xxxDao(PersonDao)

    public interface PersonDao {
       void dealMethod();
    }
    
  • 新建xxxRepositorylmpl(PersonRepositoryImpl)类并实现PersonDao接口

    关联entitymanager,标注@Repository

// Dao层
public interface PersonDao {
   void dealMethod();
}

// 实现
@Repository
public class PersonRepositoryImpl implements PersonDao {
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void dealMethod() {
        System.out.println("*********" + entityManager.getClass().getName());
    }
}

// 关联
public interface PersonRepository extends JpaRepository<Person, Integer>,
	   JpaSpecificationExecutor, PersonDao { }

// 测试
@Test
public void test10() {
    personRepository.dealMethod();
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值