第8章 Spring Boot的数据访问(2)

8.2.2 Spring Boot的支持

1.JDBC的自动配置
spring-boot-starter-data-jpa依赖于spring.boot.starter-jdbc,而Spring Boot对JDBC做了一些自动配置,源码放置在org.springframework.boot.autoconfigure.jdbc下.我们通过”spring.datasource”为前缀的属性自动配置dataSource;
Spring Boot自动开启了注解事务的支持(@EnableTransactionManagement);还配置了一个jdbcTemplate.
Spring Boot还提供了一个初始化数据的功能:放在在类路径下的schema.sql文件会自动用来初始化表结构;放置在类路径下的data.sql文件会自动用来填充表数据.

2.对JPA的自动配置
Spring Boot对JPA的自动配置放置在org.springframework.boot.autoconfigure.com.jpa下.
从HibernateJpaAutoConfiguration可以看出,Spring Boot默认JPA的实现这是Hibernate;
HibernateJpaAutoCongfiguration依赖于DataSourceAutoConfiguration.
配置JPA可以使用spring,jpa为前缀的属性在application.properties中配置
从JpaBaseCongfiguration源码中可以看出,Spring Boot为我们配置了transactionManager、jpaVendorAdapter、entityManagerFactory等Bean.JpaBaseConfiguration还有一个getPackagesToScan方法,可以自动扫描注解有@Entity的实体类.

在Web项目中我们经常会遇到在控制器或者页面访问数据的时候会出现会话连接关闭的错误,这时候我们会配置一个Open EntityManager(Session) In View这个过滤器.令人惊喜的是,Spring Boot为我们自动配置了OpenEntityManagetInViewInterceptor这个Bean ,并注册到Spring MVC的拦截器中.

3.对Spring Data JPA的自动配置
而Spring Boot对Spring Data JPA的自动配置放置在org.springframework.boot.autoconfigure.data.jpa下
从JpaRepositoriesAutoConfiguration和JpaRepositoriesAutoConfigureRegistrar源码可以看出JpaRepositoriesAutoConfiguration是依赖HibernateJpaAutoConfiguration配置的,且Spring Boot自动开启了对Spring Data JPA的支持,即我们无需在配置类中显示声明@EnableJpaRepositories.

4.Spring Boot下的Spring Data JPA
通过上面的分析可知,我们在Spring Boot下使用Spring Data JPA,在项目Maven依赖里添加spring-boot-stater-data-jpa,然后只需定义DataSource、实体类和数据访问层,并在需要使用数据访问的地方注入数据访问层的Bean即可,无须任何额外配置.

3.新建Spring Boot项目
依赖选择JPA(spring-boot-starter-data-jpa)和Web(spring-boot-starter-web)

4.配置基本属性

spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc\:oracle\:thin\@localhost\:1521\:xe
spring.datasource.username=boot
spring.datasource.password=boot
#1
spring.jpa.hibernate.ddl-auto=update
#2
spring.jpa.show.sql=true
#3
spring.jackson.serialization.indent_output=true

代码解释
上面代码第一段是用来配置数据源,第二段是用来配置jpa,更多配置内容请查阅api
①:hibernate提供了根据实体类自动维护数据库表结构的功能,可以通过spring.jpa.hibernate.ddl.auto来配置,有夏磊可选项:

  • create:启动时删除上一次生成的表,并根据实体生成表,表中数据会被清空
  • crate-drop:启动时会根据实体生成表,sessionFactory关闭时表会被删除.
  • update:启动时会根据实体类生成表,当实体属性变动的时候,表结构也会更新,在初期开发阶段使用此选型.
  • validate:启动时验证实体类和数据表是否一致,在我们数据结构稳定时采用此选项.
  • none:不采取任何措施.
    ②:spring.jpa.show-sql:用来设置hibernate操作的时候在控制台显示其真实的sql语句.
    ③:让控制器输出的json字符串格式更美观.

5.定义映射实体类
Hibernate支持自动将实体类映射为数据表格:
@Entity注解指明这是一个和数据库表映射的实体类
@Id注解指明这个属性映射为数据库的主键
@GeneratedValue注解默认使用主键生成方式为自增,hibernate会为我们自动生成一个名为HIBERNATE_SEQUENCE序列.
在磁力中使用的注解也许和你平时经常使用的注解实体类不大一样,比如没有使用@Table(实体类映射表名)、@Column(属性映射字段名)注解.这是因为我们采用正向工程通过实体类生成表结构,而不是通过逆向工程从表结构生成数据库.

在这里你可能注意到,我们没有通过@Column注解来注解普通属性,@Column是用来映射属性名和字段名,不注解的时候hibernate会自动根据属性名生成数据表的字段名.如属性名name映射成字段NAME;多字母属性如testMame会自动映射为TEST_NAME.表名的映射规则也如此

6.定义数据访问接口

7.自定义Repository实现
上面的实战演示 已经包含了Spring Boot和Spring Data JPA组合的绝大多数功能.下面我们将结合Specification和自定义Repository实现来定制一个自动模糊查询.即对于任意的实体对象进行查询,对象里有几个值我们就查几个值,当值为字符型时我我们就自动like查询,其余的类型使用自动等于查询,没有值我们就查询全部.

(1)定义Specification:

public class SpecificationUtils {
    public static<T>Specification<T> byAuto(final EntityManager entityManager,final T example){//1
        final Class<T> type =(Class<T>) example.getClass();//2
        return new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) {
                List<Predicate> predicates =  new ArrayList<>();//3
                EntityType<T> entity = entityManager.getMetamodel().entity(type);//4
                for(Attribute<T,?> attr: entity.getDeclaredAttributes()){//5
                    Object attrValue = getValue(example,attr);//6
                    if(attrValue !=null){
                        if(attr.getJavaType()==String.class){//7
                            if(attrValue!=null && attrValue!=""){//8
                                predicates.add(cb.like(root.get(attribute(entity,attr.getName(),String.class)),pattern((String)attrValue)));//9
                            }else{
                                predicates.add(cb.equal(root.get(attribute(entity,attr.getName(),attrValue.getClass())),attrValue));//10
                            }
                        }
                    }
                }
                return predicates.isEmpty()?cb.conjunction():cb.and(predicates.toArray(new Predicate[predicates.size()]));//11
            }

            //12
            private<T> Object getValue(T example,Attribute<T,?> attr){
                return ReflectionUtils.getField((Field) attr.getJavaMember(),example);
            }

            //13
            private <E,T> SingularAttribute<T,E> attribute(EntityType<T> entity, String fieldName, Class<E> fieldClass){
                return entity.getDeclaredSingularAttribute(fieldName,fieldClass);
            }
        };
    }

    static private String pattern(String str){

        return "%"+str+"%";
    }
}

代码解释:
①:定义一个返回值为Specification的方法byAuto,这里使用的是泛型T,所以这个Specification是可以用于任意的实体类的.它接受的参数是entityManager和当前的包含值作为查询条件的实体类对象.
②:获得当前实体类对象类的类型
③:新建Predicate列表存储构造的查询条件
④:获得实体类的EntityType,我们可以从EntityType获得实体类的属性
⑤:对实体类的所有属性做循环
⑥获得实体类对象某一个属性的值
⑦:当前属性值为字符类型的时候.
⑧:若当前字符不为空的情况下
⑨:构造当前属性like(前后%)属性值查询条件,并添加到条件列表中.
10:其余情况下,构造属性和属性值equal查询条件,并添加到条件列表中.
11:将条件列表转换成Predicate
12:通过反射获得实体类对象对应属性的属性值.
13:获得实体类的当前属性的SingularAttribute,SingularAttribute包含的是实体类的某个单独属性
14:构造like的查询模式,即前后加%

(2):定义接口:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

import java.io.Serializable;

public interface CustomRepository <T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T> {
    Page<T> findByAuto(T example, Pageable pageable);
}

代码解释
此例中的接口继承了JpaRepository,让我们具备了JpaRepository所提供的方法;继承了JpaSpecificationExecutor,让我们具备使用Specification的能力

(3)定义实现:

这里写代码片
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值