spring data Hello word

为了方便大家学习springData,所以花了些时间整理了一下,希望能够帮到大家。当然网上各种关于springData的博文都有,建议大家多多搜索,采纳更多的方式和意见,以达到更好的学习效果。
学习了spring jpa 的朋友,在学springData时就非常容易理解了!
主要从以下来探讨:
1.springData概述
2.使用SpringData JPA进行持久层开发 hello word
—Repository接口及其子接口的介绍
—自定义repository,进行方法扩展
—调用方法时方法名的书写格式
—使用关键字创建方法名
—使用@Query注解可以自定义hql查询
—设置@nativeQuery=true,原生sql查询
—普通分页,带条件查询的分页
—批量新增,批量删除

一、springData概述:
Spring Data :Spring 的一个子项目。用于简化数据库访问,支持NoSQL和关系数据存储。其主要目标是使数据库的访问变得方便快捷。SpringData项目所支持NoSQL存储:
MongoDB(文档数据库)
Neo4j(图形数据库)
Redis(键/值存储)
Hbase(列族数据库)
SpringData项目所支持的关系数据存储技术:
JDBC
JPA
JPA Spring Data:致力于减少数据访问层(DAO)的开发量.开发者唯一要做的,就只是声明持久层的接口,其他都交给SpringData JPA 来帮你完成!
框架怎么可能代替开发者实现业务逻辑呢?比如:当有一个UserDao.findUserById() 这样一个方法声明,大致应该能判断出这是根据给定条件的ID查询出满足条件的User 对象。SpringData JPA 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。

二、SpringData JPA HelloWorld

1.使用 Spring Data JPA 进行持久层开发需要的四个步骤:
(1).配置 Spring 整合 JPA
(2).在 Spring 配置文件中配置 Spring Data,让 Spring 为声明的接口创建代理对象。配置了 后,Spring 初始化容器时将会扫描 base-package 指定的包目录及其子目录,为继承 Repository 或其子接口的接口创建代理对象,并将代理对象注册为 Spring Bean,业务层便可以通过 Spring 自动封装的特性来直接使用该对象。
(3)声明持久层的接口,该接口继承 Repository,Repository 是一个标记型接口,它不包含任何方法,如必要,Spring Data 可实现 Repository 其他子接口,其中定义了一些常用的增删改查,以及分页相关的方法。
(4)在接口中声明需要的方法。Spring Data 将根据给定的策略(具体策略稍后讲解)来为其生成实现代码。
(5)同时下载 Spring Data Commons 和 Spring Data JPA 两个发布包:
Commons 是 Spring Data 的基础包,并把相关的依赖 JAR 文件加入到 CLASSPATH 中
在 Spring 的配置文件中配置 Spring Data

创建application.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: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/context http://www.springframework.org/schema/context/spring-context-4.3.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.3.xsd">

<!-- 配置自动扫描的包 -->       
<context:component-scan base-package="com.ctg.springdata.entity"></context:component-scan>
<!--1.配置数据源  -->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="user" value="${jdbc.user}"></property>
    <property name="password" value="${jdbc.password}"></property>
    <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
    <property name="driverClass" value="${jdbc.driverClass}"></property>
    <!-- 配置其他属性 -->
</bean>

<!-- 2.配置JPA的EntityManagerFactory -->
<bean id="entityManangerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"></property>
    <!-- jpa适配器 -->
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"></bean>
    </property>
    <!-- 配置扫描加entity注解的包 -->
    <property name="packagesToScan" value="com.ctg.springdata.entity"></property>
    <!-- hibernate配置文件 -->
    <property name="jpaProperties">
        <props>
            <!-- 生成数据表的列映射策略 -->
            <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
            <!-- hibernate 基本属性 -->
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            <prop key="hibernate.format_sql">true</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
        </props>
    </property>
</bean>

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

<!-- 4.配置支持注解的事务 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
<!-- 5. 配置SpringData-->
<!-- 加入JPA的命名空间 -->
<!-- base-package:用于扫描Repository Bean 所在的package -->
<jpa:repositories base-package="com.ctg.springdata.entity" entity-manager-factory-ref="entityManangerFactory"></jpa:repositories>
</beans>

创建两个类:Person 和 Address 他们之间存在多对一关联关系
Person:

package com.ctg.springdata.entity;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name="JPA_PERSONS")
public class Person {
    @Id
    @GeneratedValue
    private Integer id;
    private String lastName;
    private String email;
    private Date birth;
    @ManyToOne
    @JoinColumn(name="ADDRESS_ID")
    private Address address;

    @Column(name="ADD_ID")
    private Integer addressId;
    public Integer getAddressId() {
        return addressId;
    }
    public void setAddressId(Integer addressId) {
        this.addressId = addressId;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Date getBirth() {
        return birth;
    }
    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "Person [id=" + id + ", lastName=" + lastName + ", email=" + email + ", birth=" + birth + ", address="
                + address + ", addressId=" + addressId + "]";
    }       
}

Address:

package com.ctg.springdata.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

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

    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getProvince() {
        return province;
    }
    public void setProvince(String province) {
        this.province = province;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }   
}

由于此处只是建立一个demo,所以中间业务层在发生事务时创建,建立持久层personRepository之前,需要了解他所要继承的类及其子类之间的关系:
(1).Repository: 仅仅是一个标识,表明任何继承它的均为仓库接口类;
(2).CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法 ;
(3).PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法 ;
(4).JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法 ;
(5).自定义的 XxxxRepository 需要继承 JpaRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
(6).JpaSpecificationExecutor: 不属于Repository体系,实现一组 JPA Criteria 查询相关的方法 ;
自定义一个PersonDao,进行方法扩展:

package com.ctg.springdata.entity;

public interface PersonDao {
    void test();
}

创建personDao的实现类:

package com.ctg.springdata.entity;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

public class PersonRepositoryImpl implements PersonDao {
    @PersistenceContext
    private EntityManager entityManager;

    @Override
    public void test() {
        Person person = entityManager.find(Person.class, 11);
        System.out.println("---->"+person);
    }

}

所以,我们在建立PesonRepository时只需要继承JpaRepository,JpaSpecificationExecutor,就可以调用其父类中所有的方法了,同时需要方法扩展时可以继承自己创建的PersonDao:

package com.ctg.springdata.entity;

import java.util.Date;
import java.util.List;

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 org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.RepositoryDefinition;
import org.springframework.data.repository.query.Param;
/**
 * 1.Repository 是一个空接口,即是一个标记接口
 * 2.若我们定义的接口继承了Repository,则该接口会被IOC容器识别为一个Repository Bean。纳入到IOC容器中,进而可以在该接口
 * 中定义满足一定规范的方法。
 * 3.实际上,也可以通过@RepositoryDenition 注解来替代继承Repository 接口
 * 
 * 4.在Repository 子接口中声明方法:
 * ①不是随便声明的,而需要符合一定的规范
 * ②查询方法以find|read|get开头
 * ③设计查询条件时,条件的属性用条件关键字连接。要注意的是:条件属性以首字母大写;
 * ④支持属性级联查询,若当前类有符合条件的属性,则优先使用,而不使用级联属性。
 * 若要使用级联属性,则属性之间使用‘_’连接
 * @author asus
 *
 */
//@RepositoryDefinition(domainClass=Person.class,idClass=Integer.class)
public interface PersonRepository extends JpaRepository<Person, Integer>,JpaSpecificationExecutor<Person>,PersonDao {

    //1.根据lastName来获取对应的Person:分别使用find|read|get开头
    Person getByLastName(String lastName);

    Person readByLastName(String lastName);

    Person findByLastName(String lastName);

    //2.涉及查询条件时,条件的属性用条件关键字连接,如下:StartingWith:以什么开始;And:并且;LessThan:小于
    //WHERE lastName LIKE ?% AND id<?
    List<Person> getByLastNameStartingWithAndIdLessThan(String lastName,Integer id);

    //3.EndingWidth:以什么结束
    //WHERE lastName LIKE %? AND id<?
    List<Person> getByLastNameEndingWithAndIdLessThan(String lastName,Integer id);

    //4.In:在..范围内
    //WHERE email IN (?,?,?) or birth<?
    List<Person> getByEmailInOrBirthLessThan(List<String> emails,Date birth);

    //5.属性级联查询:GreaterThan:大于
    //WHRER a.id > ? 
    List<Person> getByAddressIdGreaterThan(Integer id);
    //级联属性使用‘_下划线连接’
    List<Person> getByAddress_IdGreaterThan(Integer id);

    //总结1:以上使用关键字连接进行查询,缺点:①:方法名复杂,较长;②:更复杂的条件查询无法使用:比如带有子查询就无法进行;

    //JP_QUERY的使用===============================================

    //6.查询id值最大的那个Person:使用@Query注解可以自定义jpql 语句以实现更灵活的查询
    @Query("SELECT p FROM Person p WHERE p.id=(SELECT max(p2.id) FROM Person p2 )")
    Person getMaxIdPerson();

    //7.带参查询
    //使用占位符
    @Query("SELECT p FROM Person p where p.lastName=?1 and p.email=?2")
    Person getBylastNameAndEmail1(String lastName,String email);

    //使用命名参数的方式
    @Query("SELECT p FROM Person p where p.lastName=:lastName and p.email=:email")
    Person getBylastNameAndEmail2(@Param("email")String email,@Param("lastName")String lastName);

    //8.like 模糊查询
    //使用占位符:是否在sql中添加入%如过没有添加,在调用方法入参时需要加入
    @Query("SELECT p FROM Person p where p.lastName like ?1 or p.email like ?2")
    List<Person> testdQueryAnotationLikeParam1(String lastName,String email);

    //也可以在占位符上添加%
    @Query("SELECT p FROM Person p where p.lastName like %?1% or p.email like %?2%")
    List<Person> testdQueryAnotationLikeParam2(String lastName,String email);

    //使用命名参数:
    @Query("SELECT p FROM Person p where p.lastName like %:lastName% or p.email like %:email%")
    List<Person> testdQueryAnotationLikeParam3(@Param("lastName")String lastName,@Param("email")String email);

    //本地sql查询==============================================

    //9.设置nativeQuery=true,表明是原生sql查询
    @Query(value="select count(id) from jpa_persons",nativeQuery=true)
    long getTotalCount();

    //可以通过自定义JPQL完成UPDATE和DELETE 操作,注意:JPQL不支持使用insert
    //在@Query注解中编写JPQL语句,但必须使用@Modifying进行修饰,已通知SpringData,这是一个UPDATE或Delete操作,需要使用事务定义service层
    //10.@Modifying注解:进行修改,删除====================================================
    @Query("update Person p set p.email=:email where p.id = :id")
    @Modifying
    void updatePersonEmail(@Param("id")Integer id,@Param("email")String email); 
}

创建一个测试类,测试类中分别对当PersonRepository继承Repository 及其子类时调用的方法进行测试:

package com.ctg.springdata.test;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.sql.DataSource;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.jpa.domain.Specification;

import com.ctg.springdata.entity.Person;
import com.ctg.springdata.entity.PersonRepository;
import com.ctg.springdata.entity.PersonService;
/**
 * 单元测试类
 * @author asus
 *
 */
public class SpringDataTest {
    private ApplicationContext ctx = null;
    private PersonRepository personRepository = null;
    private PersonService personService=null;
    {

        ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        personRepository=ctx.getBean(PersonRepository.class);
        personService = ctx.getBean(PersonService.class);
    }

    //==========================personRepository继承自定义的PersonDao测试=============
    @Test
    public void testPersonDao(){
        personRepository.test();
    }
    //==========================personRepository继承JpaSpecificationExecutor 测试=============
    /**
     * 目标:实现带查询条件的分页,id>5
     * 调用JpaSpecificationExecutor 的page<T> findAll(Specification<T> spec,Pageable pageable)
     * Specification:封装了JPA Criteria 查询条件
     * Pageable:封装了请求分页的信息:例如pageNo ,pageSize,Sort
     */
    @Test
    public void testJpaSpecificationExecutor(){
        int pageNo = 3;
        int pageSize = 5;

        PageRequest pageable = new PageRequest(pageNo-1, pageSize);
        //通常使用Specification的匿名内部类:
        Specification<Person> specification = new Specification<Person>(){
            /**
             * root:代表查询的实体类
             * query:可以从中得到Root对象,即告知JPA Criteria 查询要查询哪一个实体类。还可以来添加查询条件,还可以结合EntityManager
             * 对象得到最终查询的TypeQuery 对象;
             * cb:CriteriaBuilder 对象。用于创建Criteria相关对象的工厂,当然可以从中获取到Predicate对象
             * return:Predicate类型,代表一个查询条件
             */
            @Override
            public Predicate toPredicate(Root<Person> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path path = root.get("id");
                Predicate predicate = cb.gt(path, 5);
                return predicate;
            }
        };
        Page<Person> page = personRepository.findAll(specification, pageable);
        System.out.println("总记录数:"+page.getTotalElements());
        System.out.println("当前第几页:"+(page.getNumber()+1));
        System.out.println("总页数:"+page.getTotalPages());
        System.out.println("当前页面的list:"+page.getContent());
        System.out.println("当前页面的记录数:"+page.getNumberOfElements());
    }

    //==========================personRepository2继承JpaRepository 测试=============
    @Test
    public void testJpaRepository(){
        Person person = new Person();
        person.setBirth(new Date());
        person.setEmail("sdjfl@ctg.com");
        person.setLastName("lskdj");
        person.setId(28);
        //saveAndFlush 相当于 hibernate中的merge()
        Person person2 = personRepository.saveAndFlush(person);
        System.out.println(person==person2);
    }


    //==========================personRepository2继承PagingAndSortingRepository 测试=============
    /**
     * 测试1 :分页
     */
    @Test
    public void testPagingAndSortingRepository1(){
        int pageNo = 3;
        int pageSize = 5;
        //Pageable 接口通常使用的其PageRequest 实现类,其中封装了需要分页的信息;
        Pageable pageable = new PageRequest(pageNo-1, pageSize);
        Page<Person> page = personRepository.findAll(pageable);

        System.out.println("总记录数:"+page.getTotalElements());
        System.out.println("当前第几页:"+(page.getNumber()+1));
        System.out.println("总页数:"+page.getTotalPages());
        System.out.println("当前页面的list:"+page.getContent());
        System.out.println("当前页面的记录数:"+page.getNumberOfElements());
    }

    /**
     * 测试2:分页,排序
     */
    @Test
    public void testPagingAndSortingRepository2(){
        int pageNo = 3;
        int pageSize = 5;
        //排序相关的,Sort 封装了排序的信息
        //Order 是具体针对于某一个属性进行升序还是降序
        Order order1 = new Order(Direction.DESC, "id");
        Order order2 = new Order(Direction.ASC,"email");
        Sort sort = new Sort(order1,order2);
        PageRequest pageable = new PageRequest(pageNo, pageSize, sort);
        Page<Person> page = personRepository.findAll(pageable);

        System.out.println("总记录数:"+page.getTotalElements());
        System.out.println("当前第几页:"+(page.getNumber()+1));
        System.out.println("总页数:"+page.getTotalPages());
        System.out.println("当前页面的list:"+page.getContent());
        System.out.println("当前页面的记录数:"+page.getNumberOfElements());
    }
    //==========================personRepository2继承CrudRepository 测试=============
    /**
     * 测试1:批量新增
     */
    @Test
    public void testCrudRepository(){
        List<Person> persons = new ArrayList<>();
        for(int i='a';i<='z';i++){
            Person person = new Person();
            person.setAddressId(i+1);
            person.setBirth(new Date());
            person.setEmail((char)i+""+(char)i+"@ctg.com");
            person.setLastName((char)i+""+(char)i);
            persons.add(person);
        }
        personService.savePersons(persons);
    }



    //==========================personRepository继承Repository 测试=============
    /**
     * 测试1
     */
    @Test
    public void testHelloWorldSpringData(){

        System.out.println("是一个代理对象:"+personRepository.getClass().getName());
        Person person = personRepository.getByLastName("aa");
        System.out.println(person);
    }

    /**
     * 测试2
     */
    @Test
    public void testKeyWords2(){
        List<Person> persons = personRepository.getByLastNameEndingWithAndIdLessThan("a", 4);
        System.out.println("查询总数:"+persons.size());
    }

    /**
     * 测试3
     */
    @Test
    public void testKeyWords1(){
        List<Person> persons = personRepository.getByLastNameStartingWithAndIdLessThan("a", 4);
        System.out.println("查询总数:"+persons.size());
    }

    /**
     * 测试4
     */
    @Test
    public void testKeyWords3(){
        List<Person> persons = personRepository.getByEmailInOrBirthLessThan(Arrays.asList("aa@ctg.com","bb@ctg.com","cc@ctg.com"), new Date());
        System.out.println(persons.size());     
    }

    /**
     * 测试5
     */
    @Test
    public void testKeyWords4(){
        List<Person> persons = personRepository.getByAddressIdGreaterThan(1);
        System.out.println(persons.size());
    }

    /**
     * 测试6
     */
    @Test
    public void testKeyWords5(){
        Person person = personRepository.getMaxIdPerson();
        System.out.println(person);
    }

    /**
     * 测试7
     */
    public void testKeyWords6(){
        Person person = personRepository.getBylastNameAndEmail1("aa", "aa@ctg.com");
        System.out.println(person);
    }

    /**
     * 测试8
     */
    public void testdQueryAnotationLikeParam(){
        List<Person> person = personRepository.testdQueryAnotationLikeParam1("%a%", "%a%");
        System.out.println(person);
    }

    /**
     * 测试9
     */
    @Test
    public void getTotalCount(){
        long count = personRepository.getTotalCount();
        System.out.println(count);
    }

    /**
     * 测试10
     */
    @Test
    public void updatePersonEmail(){
        personService.updatePersonEmail("123@ctg.com", 1);
    }
    //=======================测试jpa配置=======================================
    @Test
    public void testJpa(){

    }

    //========================测试数据连接========================================
    /**
     * 测试数据库连接
     * @throws SQLException
     */
    @Test
    public void testDataSource() throws SQLException {
        DataSource dataSource = ctx.getBean(DataSource.class);
        System.out.println( dataSource.getConnection());
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值