springboot学习5——Datasource

一、配置数据源

1.引入了JPA的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Spring Boot使用Hibernate实现JPA,所以我们就可以在不使用任何配置数据库的情况下运行Spring Boot工程了.

2.配置MySQL的依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

3.配置数据源

spring.datasource.url=jdbc:mysql://localhost:3306/chapter5
spring.datasource.username=root
spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#最大等待连接中的数量, 设 0 为没有限制
spring.datasource.tomcat.max-idle=10
#最大连接活动数
spring.datasource.tomcat.max-active=50
#最大等待毫秒数, 单位为 ms, 超过时间会出错误信息
spring.datasource.tomcat.max-wait=10000
#数据库连接池初始化连接数
spring.datasource.tomcat.initial-size=5

这样我们就完成了Spring Boot的数据源配置,注意Spring Boot会尽可能地去判断数据源是什么类型的,然后根据其默认的情况去匹配驱动类。在它不能匹配的情况下,我们可以明确地配置它,这样就不会使用默认的驱动类了。
接着我们可以根据需要配置数据源的属性,因为上面使用的是Tomcat自带的数据库连接池,所以可以看到很多配置的代码中带有tomcat字样。

上面只是匹配Spring Boot绑定的Tomcat的数据源,有时候我们希望使用的是第三方数据源,例如,要使用DBCP数据源,只需要加入DBCP的数据源的Maven依赖。
在Maven中加入DBCP数据源依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
</dependency>

配置DBCP2数据源

spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot_chapter5
spring.datasource.username=root
spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#指定数据库连接池的类型
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
#最大等待连接中的数量,设 0 为没有限制
spring.datasource.dbcp2.max-idle=10
#最大连接活动数
spring.datasource.dbcp2.max-total=50
#最大等待毫秒数, 单位为 ms, 超过时间会出错误信息
spring.datasource.dbcp2.max-wait-millis=10000
#数据库连接池初始化连接数
spring.datasource.dbcp2.initial-size=5

在上述代码中,我们首先通过spring.datasource.type属性指定了数据库连接池的类型,然后再使用spring.datasource.dbcp2.*去配置数据库连接池的属性,这样Spring Boot就会根据这些属性去配置对应的数据库连接池,
从而知道使用的是DBCP数据源。为了验证这个结果,我们新建一个Bean,如代码清单5-6所示。

监测数据库连接池类型

@Component
// 实现Spring Bean生命周期接口ApplicationContextAware 
public class DataSourceShow implements ApplicationContextAware {

    ApplicationContext applicationContext = null;

     // Spring容器会自动调用这个方法,注入Spring IoC容器
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
        DataSource dataSource = applicationContext.getBean(DataSource.class);
        System.out.println("--------------------------------");
        System.out.println(dataSource.getClass().getName());
        System.out.println("--------------------------------");
    }
}

上述代码中实现了接口ApplicationContextAware 的方法setApplicationContext(),依照Spring Bean 生命周期的规则,在其初始化的时候该方法就会被调用,从而获取Spring IoC容器的上下文(applicationContext),
这时通过getBean方法就可以获取数据库连接池,然后打印出数据连接池的全限定名,这样就可以知道它使用的是哪种数据库连接池了。启动Spring Boot程序,就可以发现类似下面的日志出现:

......
--------------------------------
org.apache.commons.dbcp2.BasicDataSource
--------------------------------
......

显然这里是使用DBCP2的数据库连接池提供服务的。我们也可以通过类似的方法配置第三方数据源。

二、JdbcTemplate

1.JdbcTemplate的映射关系是需要开发者自己实现RowMapper的接口的,完成数据库数据到POJO(Plain Ordinary Java Object)对象的映射。对于Java 8及以上版本,可以使用Lambda表达式来比较优雅地实现RowMapper接口,如果JDK的版本比较低,只能声明类或者使用匿名类了。

2.对于增删查改,主要是传递参数,然后执行SQL后返回影响数据库记录数。执行多条SQL时,JdbcTemplate每调用一次便会生成一个数据库连接,很浪费资源,可以使用StatementCallback或者ConnectionCallback接口实现一个连接里面执行多条SQL。

三、JPA(Hibernate)

JPA(Java Persistence API,Java持久化API),定义了对象关系映射(ORM)以及实体对象持久化的标准接口。
JPA作为POJO持久化的标准规范,可以脱离容器独立运行、开发和测试,更加方便。然而这套方案并未被企业广泛使用,相对地JPA更多地是依靠Hibernate的支持才得以使用。

在Spring Boot中JPA是依靠Hibernate得以实现的,Hibernate在3.2版本中已经对JPA实现了完全的支持,这里我们就以Hibernate方案来讨论JPA的应用。

JPA所维护的核心是实体(Entity Bean),它是通过一个持久化上下文(Persistence Context)来使用的。持久化上下文包含以下3个部分:

•对象关系映射(Object Relational Mapping,简称ORM,或O/RM,或O/R映射)描述,JPA支持注解或XML两种形式的描述,在Spring Boot中主要通过注解实现;
•实体操作API,通过这节规范可以实现对实体对象的CRUD操作,来完成对象的持久化和查询;
•查询语言,约定了面向对象的查询语言JPQL(Java Persistence Query Language),通过这层关系可以实现比较灵活的查询。

开发JPA
在Maven中引入了spring-boot-starter-data-jpa,这样便能够使用JPA编程了。

1.建表SQL

create table t_user(
    id int(12) not null auto_increment,
    user_name varchar(60) not null,
    /**性别列,1-男,2-女**/
    sex int(3) not null default 1 check (sex in(1,2)),
    note varchar(256) null,
    primary key(id)
);

2.定义用户POJO

// 标明是一个实体类
@Entity(name="user")
// 定义映射的表
@Table(name = "t_user")
public class User {
    // 标明主键
    @Id
    // 主键策略,递增
    @GeneratedValue (strategy = GenerationType.IDENTITY)
    private Long id = null;

    // 定义属性和表的映射关系
    @Column(name = "user_name")
    private String userName = null;

    private String note = null;

    // 定义转换器
    @Convert(converter = SexConverter.class)
    private SexEnum sex = null;

    /**** setter and getter ****/
}

@Entity标明这是一个实体类
@Table配置的属性name指出它映射数据库的表,这样实体就映射到了对应的表上,
@Id标注那个属性为表的主键,注解@GeneratedValue则是可以配置采用何种策略生成主键,这里采用GenerationType.IDENTITY,这是一种依赖于数据库递增的策略;
@Column标注除id外的属性,因为属性名称(userName)和数据库列名(user_name)不一致,而其他的属性名称和数据库列名保持一致,这样就能与数据库的表的字段一一对应起来了。

为此Spring提供了JpaRepository接口提供基本CRUD操作,它本身也继承了其他的接口。

Spring 的JPA接口设计
JPA最顶级的接口是Repository,它没有定义任何方法,
它的子接口CrudRepository,定义了最基本的增删改的操作,功能性还不足够强大,
PagingAndSortingRepository继承了它并且提供了分页和排序的功能,
最后JpaRepository扩展了PagingAndSortingRepository,而且扩展了QueryByExampleExecutor接口,

一般而言,我们只需要定义JPA接口扩展JpaRepository便可以获得JPA提供的方法了,例如,针对用户类(User)的JPA接口定义,如代码清单5-15所示。

3.定义JPA接口

public interface JpaUserRepository 
    extends JpaRepository<User, Long> {
}

继承JpaRepository,获得JPA提供的方法。

4.控制器测试

@Controller
@RequestMapping("/jpa")
public class JpaController {
    // 注入JPA接口,这里不需要使用实现类
    @Autowired
    private JpaUserRepository jpaUserRepository = null;

    @RequestMapping("/getUser")
    @ResponseBody
    public User getUser(Long id) {
        // 使用JPA接口查询对象
        User user = jpaUserRepository.findById(id).get();
        return user;
    }
}

Spring提供了两个注解用来扫描对应的JPA接口和实体类,它们是@EnableJpaRepositories和@EntityScan。顾名思义,
@EnableJpaRepositories:启用JPA和制定扫描的包,生成对应的Bean,装配在IoC容器中
@EntityScan:通过扫描装载JPA的实体类

5.Spring Boot启动文件:将接口扫描到Spring IoC容器中

// 定义Spring Boot扫描包路径
@SpringBootApplication(scanBasePackages = {"com.springboot.chapter5"})
// 定义JPA接口扫描包路径
@EnableJpaRepositories(basePackages = "com.springboot.chapter5.dao")
// 定义实体Bean扫描包路径
@EntityScan(basePackages = "com.springboot.chapter5.pojo")
public class Chapter5Application {
    public static void main(String[] args) throws Exception {
        SpringApplication.run(Chapter5Application.class, args);
    }
}

实际上,即使没有使用注解@EnableJpaRepositories和@EntityScan,只要依赖了spring-boot-starter-data-jpa,Spring Boot 2.x也会对项目进行扫描,这样JPA的实体和接口都会被扫描,只是使用它们可以更进一步配置JPA的相关信息而已。

6.对JPA进行一定的配置:

配置JPA属性
#使用MySQL数据库方言
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
#打印数据库SQL
spring.jpa.show-sql=true
#选择Hibernate数据定义语言(DDL)策略为update
spring.jpa.hibernate.ddl-auto=update

但是有时我们可能需要更加灵活的查询,这时可以使用JPA查询语言(JPQL),它与Hibernate提供的HQL是十分接近的。这里使用注解@Query标识语句就可以了。

定义查询语句:
@Query("from user where user_name like concat(’%’, ?1, ‘%’) "
+ “and note like concat(’’, ?2, ‘%’)”)
public List findUsers(String userName, String note);

其中from user中的user是定义的实体类名称(@Entity注解的name属性),所以才能这样定义一条JPQL,提供给上层调用。

按照一定规则命名的方法也可以在不写任何代码的情况下完成逻辑:
JPA的命名查询

/**
 * 按用户名称模糊查询
 * @param userName 用户名
 * @return 用户列表
 */
List<User> findByUserNameLike(String userName);

/**
 * 根据主键查询
 * @param id -- 主键
 * @return 用户
 */
User getUserById(Long id);

/**
 * 按照用户名称或者备注进行模糊查询
 * @param userName 用户名
 * @param note 备注
 * @return 用户列表
 */
List<User> findByUserNameLikeOrNoteLike(String userName, String note);

可以看到,这里的命名是以动词(get/find)开始的,而以by代表按照什么内容进行查询。
例如,getUserById方法就是通过主键(id)对用户进行查询的,这样JPA就会根据方法命名生成SQL来查询数据库了;
findByUserNameLike方法的命名则多了一个like,它代表着采用模糊查询,也就是使用like关键字进行查询;
findByUserNameLikeOrNoteLike这样的命名,则涉及两个条件,一个是用户名(userName),另一个是备注(note),它们都采用了like,因此会执行模糊查询,而它们之间采用的连接词为Or(或者),所以SQL的生成也采用了Or。

其实JPA所实现的功能通过mybatis跟mybatis-plus也都可以实现

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章