SpringData JPA框架

内容

1.概述
2.开发步骤
3.常用接口
4.方法定义规范
5.常用注解
6.接口方法解析
7.自定义Repository方法
8.SpringData和SpringBoot框架搭建
9.在SpringBoot中使用单元测试
10.附:hibernate常用注解及HQL语句

一SpringData概述

1.SpringData介绍

1.Spring Data : 
Spring 的一个子项目。用于简化数据库访问,支持NoSQL 和 关系数据存储。其主要目标是使数据库的访问变得方便快捷
2.SpringData 项目所支持 NoSQL 存储:
(1)MongoDB (文档数据库)
(2)Neo4j(图形数据库)
(3)Redis(键/值存储)
(4)Hbase(列族数据库)
3.SpringData 项目所支持的关系数据存储技术:
(1)JDBC
(2)JPA

2.SpringData JPA介绍

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

3.问题思考

框架怎么可能代替开发者实现业务逻辑呢?
比如:当有一个 UserDao.findUserById()这样一个方法声明,大致应该能判断出这是根据给定条件的 ID 查询出满足条件的 User  对象。Spring Data JPA 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。

二.开发步骤

1.配置 Spring 整合 JPA
2.在Spring 配置文件中配置 Spring Data
3.声明持久层的接口,该接口继承  Repository
4.在接口中声明需要的方法

1.配置Spring 整合JPA

同时下载 Spring Data Commons 和 Spring Data JPA 两个发布包:
Commons 是 Spring Data 的基础包并把相关的依赖 JAR 文件加入到 CLASSPATH 中

2.在Spring 配置文件中配置Spring Data

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

3.声明持久层的接口,该接口继承 Repository

Repository是一个标记型接口,它不包含任何方法,如必要,Spring Data 可实现 Repository 其他子接口,其中定义了一些常用的增删改查,以及分页相关的方法。
public interface UserRepository extends Repository<User,Integer>{
    User selectById(Integer id);
}

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

Spring Data 将根据给定的策略(具体策略稍后讲解)来为其生成实现代码。

5.代码示例

pom.xml配置

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
	   <!--Spring相关包-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-orm</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.2.RELEASE</version>
        </dependency>

        <!--Spring JPA-->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-commons</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-jpa</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <!--JPA:Java Pesistence API-->
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>javax.persistence-api</artifactId>
            <version>2.2</version>
        </dependency>
        <!--JPA实现-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.4.0.Final</version>
        </dependency>

        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>
        <!--数据源-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.2</version>
        </dependency>
    </dependencies>

spring.xml关于JPA配置

<?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:jpa="http://www.springframework.org/schema/data/jpa"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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.xsd
       http://www.springframework.org/schema/data/jpa
       http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

    <context:component-scan base-package="service"></context:component-scan>

    <!--生成Dao接口代理对像-->
    <jpa:repositories base-package="dao"
                      entity-manager-factory-ref="entityManagerFactory "
                      transaction-manager-ref="txManager"></jpa:repositories>

    <!--dataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql:///javaee"></property>
        <property name="username" value="root"></property>
        <property name="password" value="tiger"></property>
    </bean>
    <!--
    EntityManagerFactory (对应 Hibernate中的SessionFactory)。
    EntityManager (对应Hibernate中的Session)。-->
    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <!--扫描model中hibernate注解-->
        <property name="packagesToScan" value="model"></property>
        <!--hibernate实现-->
        <property name="jpaVendorAdapter">
            <bean  class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="generateDdl" value="true"></property>
            </bean>
        </property>
        
        <property name="jpaProperties">
            <props>
                <!--把数据库表的列映射成对像属性,如USER_ID映射成属性userId-->
                <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.format_sql">true</prop>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQL57Dialect</prop>
            </props>
        </property>
    </bean>

	<!--事务切面-->
    <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory"></property>
    </bean>
	<!--事务驱动-->
    <tx:annotation-driven transaction-manager="txManager"></tx:annotation-driven>

</beans>

dao代码

//注意:自定义接口继承的接口父类下面会做详细解释
public interface IUserDao extends Repository<User, Id> {
	
    @Query("select u from User u where u.id =:id")
    User selectById(@Param("id") Integer id);
}

service代码

@Service
@Transactional
public class UserService {
    @Autowired
    private IUserDao dao;
    public User findById(Integer id){

        return dao.selectById(id);
    }
}

测试类


@RunWith(value = SpringJUnit4ClassRunner.class)
@ContextConfiguration(value="classpath:spring.xml")
public class UserTest {
    @Autowired
    UserService service;
    @Test
    public void testFindById(){
        User user = service.findById(1);
        System.out.println(user);
    }

}

三.常用接口

1.Repository 接口

1.Repository 接口是 Spring Data 的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法 
public interface Repository<T, ID extends Serializable> {

} 
2.Spring Data可以让我们只定义接口,只要遵循 Spring Data的规范,就无需写实现类。  
3.与继承 Repository 等价的一种方式,就是在持久层接口上使用 @RepositoryDefinition 注解,并为其指定 domainClass 和 idClass 属性。如下两种方式是完全等价的,如下:

@RepositoryDefinition(domainClass =User.class ,idClass = Integer.class)
public interface IUserDao  {
    
}

2.Repository的子接口

基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能。它们的继承关系如下: 
1.Repository: 仅仅是一个标识,表明任何继承它的均为Repository接口类
2.CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法 
3.PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法 
4.JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法 (一般推荐此方式)
5.自定义的 XxxxRepository 需要继承 JpaRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
6.JpaSpecificationExecutor: 不属于Repository体系,实现一组 JPA Criteria 查询相关的方法 

四.方法定义规范

1.简单条件查询

1.简单条件查询: 查询某一个实体类或者集合 
2.按照 Spring Data 的规范,查询方法以 find | read | get 开头,涉及条件查询时,条件的属性用条件关键字连接,要注意的是:条件属性以首字母大写。 
例如:定义一个 Entity 实体类 

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

注意:在方法名中约定了相关查询条件,所以方法名中的And,Or....代表条件的组合方式,见下图:

2.支持的关键字

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

[外链图片转存失败(img-LYcigzAt-1567750914612)(1.png)]

[外链图片转存失败(img-fCScOS2v-1567750914613)(2.png)]

3.查询方法流程分析

//查询文档,按照用户或部门,或用户uuid查询
假如创建如下的查询:
findDocByUserDepUuid(),框架在解析该方法时,首先剔除 findBy,然后对剩下的属性进行解析,查询实体为Doc
1.先判断 userDepUuid (根据 POJO 规范,首字母变为小写)是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,继续第二步;
2.从右往左截取第一个大写字母开头的字符串(此处为Uuid),然后检查剩下的字符串是否为查询实体的一个属性,如果是,则表示根据该属性进行查询;如果没有该属性,则重复第二步,继续从右往左截取;最后假设 user 为查询实体的一个属性;doc.user.dep.uuid
3.接着处理剩下部分(DepUuid),先判断 user 所对应的类型是否有depUuid属性,如果有,则表示该方法最终是根据 “ Doc.user.depUuid” 的取值进行查询;否则继续按照步骤 2 的规则从右往左截取,最终表示根据 “Doc.user.dep.uuid” 的值进行查询。
4.可能会存在一种特殊情况,比如 Doc包含一个 user 的属性,也有一个 userDep 属性,此时会存在混淆。可以明确在属性之间加上 "_" 以显式表达意图,比如 "findByUser_DepUuid()" 或者 "findByUserDep_uuid()"

规则总结(表示按对像的属性查询):
1.doc.userDepUuid 非按该属性查询,则执行2
2.doc.user 非按该属性查询,则执行3
3.doc.user.depUuid 非按该属性查询,则执行4
4.doc.user.dep  非按该属性查询,则执行5
5.doc.user.dep.uuid 
特殊的参数: 还可以直接在方法的参数上加入分页或排序的参数,比如:
Page<UserModel> findByName(String name, Pageable pageable);
List<UserModel> findByName(String name, Sort sort);

Pageable用法

注意:其他分页查询条件由方法名规定

//用法一
  int pageNum = 0;//当前页
  int pageSize = 2;//每页条数
  Pageable pageable = PageRequest.of(pageNum,pageSize, Sort.Direction.DESC,"排序属性条件");
//用法二
 int pageNum = 0;//当前显示页(从0开始)
 int pageSize = 2;//每页显示条数
 Sort sort = Sort.unsorted();//查询结果默认排序
 Pageable pageable = PageRequest.of(pageNum,pageSize,sort);

Sort用法

//如按User中id属性降序排列
Sort sort = Sort.by("id").descending();

五.注解的使用

1.使用@Query自定义查询

这种查询可以声明在 Repository 方法中,摆脱像命名查询那样的约束,将查询直接在相应的接口方法中声明,结构更为清晰,这是 Spring data 的特有实现。
//HQL
@Query("select u from User u where u.id = ?1 and username=?2")
User selectUserById(Integer id,String username);

索引参数与命名参数

1.索引参数如下所示,索引值从1开始,查询中 ”?X” 个数需要与方法定义的参数个数相一致,并且顺序也要一致 
2.命名参数(推荐使用这种方式):可以定义好参数名,赋值时采用@Param("参数名"),而不用管顺序。
@Query("select c from User c where c.firstName = :firstName or c.lastName=:lastName")
Customer selectByFNameOrLName(@Param("firstName")String Fname,@Param("lastName")String LName);

如果是 @Query 中有 LIKE 关键字

如果是 @Query 中有 LIKE 关键字,后面的参数需要前面或者后面加 %,这样在传递参数值的时候就可以不加 %1.@Query("select u from User u where u.name like ?1%")//以?开始
  public List<User> findByIdOrAge(String name);
2.@Query("select u from User u where u.name like %?1")//以?结束
  public List<User> findByIdOrAge(String name);
3.@Query("select u from User u where u.name like %?1%") 
  public List<User> findByIdOrAge(String name);//出现?即可

用@Query来指定本地查询(返回Sql编程)

还可以使用@Query来指定本地查询,只要设置nativeQuery为true,比如:
1.@Query(value="select * from tb_user where name like %?1" ,nativeQuery=true)
    public List<User> findByIdOrAge(String name);

2.@Modifying 注解和事务

@Query 与 @Modifying 执行更新操作

@Query@Modifying 这两个 annotation一起声明,可定义个性化更新操作,例如只涉及某些字段更新时最为常用,示例如下:
@Modifying
@Query("update User u set u.name=?1")
int update(String name)
注意:
方法的返回值应该是 int,表示更新语句所影响的行数
在调用的地方必须加事务,没有事务不能正常执行

事务

1.Spring Data 提供了默认的事务处理方式,即所有的查询均声明为只读事务。
2.对于自定义的方法,如需改变 Spring Data 提供的事务默认方式,可以在方法上注解 @Transactional 声明 
3.进行多个 Repository 操作时,也应该使它们在同一个事务中处理,按照分层架构的思想,这部分属于业务逻辑层,因此,需要在 Service 层实现对多个 Repository 的调用,并在相应的方法上声明事务。 

六.接口方法解析

1.CrudRepository 接口

CrudRepository 接口提供了最基本的对实体类的添删改查操作:
1.T save(T entity);//保存单个实体 
2.Iterable<T> save(Iterable<? extends T> entities);//保存集合        
3.T findOne(ID id);//根据id查找实体         
4.boolean exists(ID id);//根据id判断实体是否存在         
5.Iterable<T> findAll();//查询所有实体,不用或慎用!         
6.long count();//查询实体数量         
7.void delete(ID id);//根据Id删除实体         
8.void delete(T entity);//删除一个实体 
9.void delete(Iterable<? extends T> entities);//删除一个实体的集合         
10.void deleteAll();//删除所有实体,不用或慎用! 

2.PagingAndSortingRepository接口

该接口提供了分页与排序功能 
1.Iterable<T> findAll(Sort sort); //排序 
2.Page<T> findAll(Pageable pageable); //分页查询(含排序功能) 

3.JpaRepository接口

该接口提供了JPA的相关功能 
1.List<T> findAll(); //查找所有实体 
2.List<T> findAll(Sort sort); //排序、查找所有实体 
3.List<T> save(Iterable<? extends T> entities);//保存集合 
4.void flush();//执行缓存与数据库同步 
5.T saveAndFlush(T entity);//强制执行持久化 
6.void deleteInBatch(Iterable<T> entities);//删除一个实体集合 

4.JpaSpecificationExecutor接口(略)

1.不属于Repository体系,实现一组 JPA Criteria 查询相关的方法,如下图
2.Specification:封装  JPA Criteria 查询条件。通常使用匿名内部类的方式来创建该接口的对象

[外链图片转存失败(img-95fwtKG8-1567750914613)(3.png)]

七.自定义 Repository 方法

1.自定义方法接口方式

1.为某一个 Repository 上添加自定义方法
2.为所有的 Repository 都添加自实现的方法

2.为某一个 Repository上添加自定义方法步骤

步骤:
1.定义一个父接口: 并声明相应自定义接口方法
2.提供该父接口的实现类: 类名需在要声明的 接口名 后添加 Impl, 并实现方法
3.声明 Repository 子接口, 并继承第1步声明的父接口,一般也应继承JPARepository
4.使用 Repository 子接口进行CRUD操作.当调用自定接口方法时,会主动调用相应实现类的方法
注意:
默认情况下, Spring Data 会在 base-package 中查找 "接口名Impl" 作为实现类. 也可以通过 repository-impl-postfix 声明后缀. 

[外链图片转存失败(img-SptOEazu-1567750914614)(4.png)]

上述示例:
实际上在使用 PersonRepository 的 test 方法时,会调用 PersonRepositoryImpl 中 test 方法的实现

代码示例

//父接口
public interface PersonDao{
    void method();
}
//父接口实现类:注意实现类命名必须是接口名+Impl后缀
public class PersonDaoImpl implements PersonDao{
    @PersistenceContext
    private EntityManager entityManager;//提供了CRUD相应方法
    
    public void method(){
        System.out.println("method()");
        System.out.println(entityManager);
    }
}

//子接口
public interface PersonRepository extends PersonDao,JpaRepository<Person,Integer> {
    
}

3.为所有的 Repository都添加自实现的方法

步骤:
1.声明一个接口, 在该接口中声明需要自定义的方法, 且该接口需要继承 Spring Data 的 Repository空接口.
2.提供 1) 所声明的接口的实现类. 且继承 SimpleJpaRepository, 并提供方法的实现
3.手动创建JpaRepositoryFactory接口的实现类.
4.定义 JpaRepositoryFactoryBean接口的实现类, 使其生成 1) 定义的接口实现类的对象
5.修改 <jpa:repositories /> 节点的 factory-class 属性指向 3) 的全类名



注意: 全局的扩展实现类不要用 Imp 作为后缀名, 或为全局扩展接口添加 @NoRepositoryBean 注解告知  Spring Data: Spring Data 不把其作为 Repository

过程:
1.springJPA加载自定义RepositoryFactoryBean
2.RepositoryFactoryBean负责创建自定义RepositoryFactory对像
3.RepositoryFactory自定义的通用接口实现类对像

指定使用自定义RepositoryFactoryBean

<jpa:repositories base-package="dao" 
                  transaction-manager-ref="txManager" 
                  entity-manager-factory-ref="entityManagerFactory"
    			factory-class="commons.CommonFactoryBean">
</jpa:repositories>

创建自定义的RepositoryFactoryBean

//注意自定义factoryBean要继承JpaRepositoryFactoryBean
public class CommonFactoryBean<T extends Repository<S, ID>, S, ID> extends JpaRepositoryFactoryBean {

    public CommonFactoryBean(Class<? extends T> repositoryInterface){
        super(repositoryInterface);
    }

    //该方法会被SpringJPA调用,把EntityManager对像注入到该方法中
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {

        //创建RepositoryFactory工厂对像
        return new CommonFactory(entityManager);
    }

}

建自定义的RepositoryFactory对像

//自定义RepositoryFacoty对像要继承JpaRepositoryFactory
public class CommonFactory extends JpaRepositoryFactory {

    private EntityManager entityManager;

    public CommonFactory(EntityManager entityManager){
        super(entityManager);
        this.entityManager = entityManager;
    }

    //自定义接口实现类对像
    @Override
    protected JpaRepositoryImplementation<?, ?> getTargetRepository(RepositoryInformation information, EntityManager entityManager) {

        Class domainClass = information.getDomainType();
        return new CommonDao(domainClass,entityManager);
    }

    //自定义的接口实现类Class对像
    @Override
    protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
        return CommonDao.class;
    }
}

自定义通用接口,该接口要实现Repository

//通用接口方法
public interface ICommonDao<T,ID> extends Repository<T,ID> {
    public T selectCommons(Class<T> clazz,Object primaryKey);
}

自定义接口实现类

public class CommonDao<T,ID> extends SimpleJpaRepository<T, ID> implements ICommonDao<T,ID>{

    //接收EntityManager对像
    private EntityManager entityManager;

    public CommonDao(Class<T> domainClass,EntityManager entityManager){
        super(domainClass,entityManager);
        this.entityManager = entityManager;
    }

    //使用EntityManager对像进行CRUD操作
    public T selectCommons(Class<T> clazz,Object primarKey) {

        System.out.println(entityManager+"......................");
        T t = entityManager.find(clazz,primarKey);

        return t;
    }
}

CRUD操作依赖的接口

public interface IUserDao extends JpaRepository<User,Integer>, ICommonDao<User,Integer>{
    ........
}

总结

一般进行JPA编程时,使用如下四种方式:
1.使用@Query等相应注解,手动提供Query实现方式
2.根据方法名约定,无需提供Query实现,这对方法的起别规则有要求
3.使用JPA默认提供的接口,无需手动写接口方法或Query实现
4.提供自定义Repository 接口方法及接口实现类
public interface IUserDao extends JpaRepository<User, Id> {

    //1.提供HQL或Sql的Query实现方式
    @Query("select u from User u where u.id =:id")
    User selectById(@Param("id") Integer id);

    //2.根据约定,无需提供Query实现
    List<User> findAllByIdBetween(Integer id1,Integer id2);
    //分页加排序
    Page<User> findAllByIdBetween(Integer id1, Integer id2, Pageable pageable);
    //排序
    List<User> findAllByIdBetween(Integer id1, Integer id2, Sort sort);

    //3.使用JPA默认提供的接口
}

八.SpringData和SpringBoot框架搭建

步骤如下:
1.创建SpringBoot项目,引入相关依赖
2.配置application.properties
3.配置springDataJPA配置类
4.创建dao层接口,继承相应接口
5.创建model,controller,service相应层
6.启动SpringBoot应用

1.创建SpringBoot项目,引入相关依赖

<?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>com.sy</groupId>
    <artifactId>springboot_demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--继承parent,声明为一个SpringBoot项目-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>
    <dependencies>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>
        <!--SpringBoot Web-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--Spring Data JPA-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>1.5.1.RELEASE</version>
        </dependency>

        <!--themeleaf模板引擎-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <version>1.4.0.RELEASE</version>
        </dependency>
		<!--C3p0数据源-->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <!--SpringBoot Maven插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>

    </build>
</project>

2.配置application.properties

#tomcat
server.port=8080
server.tomcat.uri-encoding=utf-8
#dataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/javaee
spring.datasource.username=root
spring.datasource.password=tiger
spring.datasource.type=com.mchange.v2.c3p0.ComboPooledDataSource
#SpringMVC视图层控制
spring.mvc.view.prefix=classpath:/templates/
spring.mvc.view.suffix=.html
spring.mvc.static-path-pattern=/**

#Spring Data JPA
spring.jpa.database=MYSQL
spring.jpa.show-sql=true
#spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

3.配置springDataJPA配置类

//配置类
@Configuration
//最高优先级
@Order(Ordered.HIGHEST_PRECEDENCE)
//事务驱动
@EnableTransactionManagement(proxyTargetClass = true)
//JPA接口依赖
@EnableJpaRepositories(basePackages = {"mapper"})
//持久化类扫描
@EntityScan(basePackages = {"model"})
public class JPAConfig {
    /*
     Spring异常统一处理:
     这种方式和使用HibernateTemplate有的不同是它们对异常的处理。
     HibernateTemplate会将异常统一翻译成Spring的数据访问异常体系中的某个异常,
     而我们使用Hibernate上下文的Session时,抛出的就不是Spring的异常,
     而是HibernateException,如果我们还想看到Spring的异常体系,
     就需要做点设置,当然这也很简单。在DAO实现类上加@Respository注解,
     并且注册一个PersistenceExceptionTranslationPostProcessor实例即可
     */
    @Bean
    PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){
        return new PersistenceExceptionTranslationPostProcessor();
    }
}

4.创建dao层接口,继承相应接口

@Repository
public interface UserMapper extends JpaRepository<User,Long> {

    @Query("select u from User u where u.id=:id")
    User selectById(@Param("id") Long id);
}

5.创建model,controller,service相应层

6.启动SpringBoot应用

//使用main方法启动,或使用Maven插件启动
@SpringBootApplication(scanBasePackages = {"controller","service","config"})
public class SpringBootStarter {

    public static void main(String[] args) {

        SpringApplication.run(SpringBootStarter.class,args);
    }
}

九.在SpringBoot中使用单元测试

1.引入单元测试依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <version>2.1.1.RELEASE</version>
            <scope>test</scope>
</dependency>

2.创建测试类

@RunWith(SpringRunner.class)
@SpringBootTest(classes = 启动类)
//由于是Web项目,Junit需要模拟ServletContext,因此我们需要给我们的测试类加上@WebAppConfiguration。
@WebAppConfiguration
public class Demo {
		//织入业务层bean
        @Autowired
        UserService service;

        @Test
        public void testFindAll(){
            System.out.println(service);
            System.out.println(service.findAll());
        }

        @Test
        @Ignore
        public void testFindById(){
            System.out.println("可以忽略测试的代码");
        }


}

3.Suit测试

如果测试类较多,不能一个一个去测试,需要集中一起测试,则会使用套件测试.
@RunWith(Suite.class)
@Suite.SuiteClasses({测试类1,测试类2.....})
public class DemoSuit {
    
}
//注意:只需运行该类即可,就可以运行相应的测试类

十: 附Hibernate注解及HQL

1.注解

1.@Entity
2.@Table
3.@Id
4.@GeneratedValue
5.@Column
6.@ManyToOne
	多对一关系
7.@OneToMany
	一对多关系 
8.@OneToOne
	一对一
9.@ManyToMany
	多对多
10.@PrimaryKeyJoinColumn
	主键一对一
11.@JoinTable
	中间表

3.持久化对像的关联关系

1.一对多或多对一
	班级对像和学生对像的关系(一对多),学生对像和班级对像(多对一)
2.多对多
	学生对像和老师对像
3.一对一
	学生对像和学生证对像

4.代码示例

一对多及多对一

//Clazz
@Entity
@Table(name="CLAZZ")
public class Clazz {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String cname;

    //一对多
    @OneToMany(fetch = FetchType.EAGER)
    //配置中间表信息
    @JoinTable(name = "clazz_student",//中间表表名 表1_表2
            joinColumns ={@JoinColumn(name = "CID")},//Clazz表到中间表的外键
            inverseJoinColumns = {@JoinColumn(name = "SID")})//中间表到Student表的外键
    private Set<Student> stus;

    //setter和getter省略
  
}
//Student
@Entity
@Table(name="STUDENT")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String sname;
    private String address;
    private Character sex;
    private Integer age;

    //多对一:stu-->clazz_student-->clazz
    @ManyToOne(fetch = FetchType.EAGER,targetEntity = Clazz.class)
    //配置中间表信息
    @JoinTable(name = "clazz_student",
            joinColumns ={@JoinColumn(name = "SID")},//从student表到中间表的外键
            inverseJoinColumns = {@JoinColumn(name = "CID")})//中间表到Clazz表的关联键
    private Clazz clazz;
    
     //setter和getter省略
    
}
//IStudentDao
public interface IStudentDao extends Repository<Student,Integer> {

    @Query("select s from Student s where s.id=:id")
    Student selectById(@Param("id") Integer id);
}
//IClazzDao
public interface IClazzDao extends Repository<Clazz,Integer> {
    @Query("select c from Clazz c where c.id=:id")
    Clazz selectById(@Param("id") Integer id);
}

多对多

//学生关联老师
@Entity
@Table(name="STUDENT")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String sname;
    private String address;
    private Character sex;
    private Integer age;

	//多对多
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name="STU_TEACH",joinColumns = {@JoinColumn(name="SID")},inverseJoinColumns = {@JoinColumn(name="TID")})
    private Set<Teacher> teachers;
    
     //setter和getter省略
}
//老师关联学生
@Entity
@Table(name="TEACHER")
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String tname;

    //多对多
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(name="STU_TEACH",joinColumns = {@JoinColumn(name="TID")},inverseJoinColumns = {@JoinColumn(name="SID")})
    private Set<Student> stus;
    
     //setter和getter省略
}
//dao
同一对多和多对一

一对一

//学生证关联学生
@Entity
@Table(name="CARD")
public class Card {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String cardname;

    //一对一
    @OneToOne(fetch = FetchType.EAGER)
    @PrimaryKeyJoinColumn(name="ID")
    private Student stu;
   
    //setter和getter省略
}

//学生关联学生证
@Entity
@Table(name="STUDENT")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    private String sname;
    private String address;
    private Character sex;
    private Integer age;
	//一对一
    @OneToOne(fetch = FetchType.EAGER)
    @PrimaryKeyJoinColumn(name = "ID")
    private Card card;
    
     //setter和getter省略
}

2.Hql

Hibernate Query Lanaguage Hibernate查询语言,面向对像查询语言,屏蔽了数据库底层的差异性
--HQL的语法结构
select 属性1,属性2 from 类名 别名 [where 条件] [group by 分组属性] [having 分组属性条件] 

--注意:
--1.不支持select * from 类名,但支持select count(*) from 类名
--2.不支持分页limit关键字
--3.不支持连接查询on关键字

--连接查询
select [属性] from 类名1 别名1 left join 别名1.pojo属性 
如:查询id为1的学生信息及对应的班级信息
select s from Student s left join s.clazz where s.id = 1;



发布了154 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43727372/article/details/100578583