内容
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 User{
private 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;