访问数据库的方式一般来说有两种,一种以Java Entity为中心,将实体和实体关系对应到数据库的表和表关系,例如Hibernate框架(Spring Data JPA由此实现);另一种以原生SQL为中心,更加灵活便捷,例如Mybatis。
本篇要讲数据源配置,接着重点介绍下Spring Data JPA技术,最后讲下Spring Boot集成Mybatis。
一、配置Spring Data JPA
pom文件引入Spring Data JPA的依赖,选用mysql驱动包和druid作为数据源
<!-- jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Druid数据源,是第三方数据源。SpringBoot2默认HikariCP数据源,区别1版本用的Tomcat -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
接着配置文件进行数据源的配置,这里官方推荐使用yml后缀文件替代properties后缀文件。示例配置如下:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: root
password: toor
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
jpa:
show-sql: true
open-in-view: true
这里注意,spring.jpa.open-in-view设置为true是为了解决懒加载时加载完以后Session关闭导致的No Session异常。
同时还要加入一个过滤器,设置为Bean,代码如下
@Bean
public OpenEntityManagerInViewFilter openEntityManagerInViewFilter() {
return new OpenEntityManagerInViewFilter();
}
此外,也可以通过Java代码来配置数据源。示例代码如下:
@Configuration
public class DataSourceConfigure {
@Autowired
private Environment env;
private static final String JDBC_URL;
private static final String DRIVER_CLASS;
private static final String USERNAME;
private static final String PASSWORD;
static {
JDBC_URL = "spring.datasource.url";
DRIVER_CLASS = "com.mysql.jdbc.Driver";
USERNAME = "spring.datasource.username";
PASSWORD = "spring.datasource.password";
}
@Bean(name = "datasource")
public DataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(env.getProperty(JDBC_URL));
dataSource.setDriverClassName(env.getProperty(DRIVER_CLASS));
dataSource.setUsername(env.getProperty(USERNAME));
dataSource.setPassword(env.getProperty(PASSWORD));
return dataSource;
}
}
二、准备数据库与实体类
数据库
## 部门表
CREATE TABLE `department` (
`department_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE utf8_bin DEFAULT NULL,
PRIMARY KEY (`department_id`)
);
## 用户表
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(45) COLLATE utf8_bin DEFAULT NULL COMMENT '名称',
`department_id` int(11) DEFAULT NULL,
`gmt_create` date DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`user_id`),
KEY `fk_department_id` (`department_id`),
CONSTRAINT `fk_department_id` FOREIGN KEY (`department_id`) REFERENCES `department` (`department_id`) ON DELETE SET NULL ON UPDATE NO ACTION
);
实体类
// 部门实体
@Entity
public class Department implements Serializable {
private static final long serialVersionUID = -8263045269071570448L;
@Id
@Column(name = "department_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
/**
* 一对多,一方默认是LAZY
* mappedBy这里以声明Many端的对象(User实体)的department属性提供了对应的映射关系
* yml文件需要保持session的开启状态,spring.jpa.open-in-view=true
*/
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
@JsonIgnore
private Set<User> users = new HashSet<>();
// 省略getter/setter
}
// 用户实体
@Entity
public class User implements Serializable {
private static final long serialVersionUID = 5608542878548547190L;
@Id
@Column(name = "user_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column
private String name;
@Column(name = "gmt_create")
private Date gmtCreate;
/** 用户与部门 多对一,多方默认是EAGER */
@ManyToOne(fetch = FetchType.EAGER, optional = false)
@JoinColumn(name = "department_id")
private Department department;
// getter/setter
}
JPA注解简介:
JPA注解 | 说明 |
@Entity | 标记为实体类 |
@Table | 对应表名,不写默认为类名,首字母小写 |
@Id | 声明一个属性映射到主键的字段 |
@GeneratedValue | 设定主键生成策略,自增可用AUTO或IDENTITY |
@Column | 表明属性对应到数据库的一个字段。 |
@ManyToOne | 多对一,默认fetch = FetchType.EAGER |
@JoinColumn | 与@ManyToOne搭配,指明外键字段 |
@OneToMany | 一对多,mappedBy这里以声明Many端的对象(User实体)的属性(department)提供了对应的映射关系 |
三、Repository
Repository是Spring Data的核心概念。提供有如下接口:
- CrudRepository:提供基本的增删该查,批量操作接口。
- PagingAndSortingRepository:集成CrudRepository,提供附加的分页查询功能。
- JpaRepository:专用于JPA,是重点。
public interface UserRepository extends JpaRepository<User, Integer>{
// 不需要我们编写实现
}
1.JpaRepository
这些接口不需要我们编写实现,Spring Boot底层采用Hibernate实现。现在主要来看JpaRepository的部分用法。
Jpa提供了很多方法,例如:findAll()
:返回一个集合,当元素为空时,返回空集合。示例代码如下:
// 查询所有用户
List<User> userList = userRepository.findAll();
如果想做分页查询可通过 findAll(Pageable pageable)
,利用Pageable进行分页查询。构造Pageable可使用其实现类PageRequest,Sort用来指定排序方式。示例代码如下:
// 分页查询第一页用户,限制每页10条记录,按创建时间降序排序
int page = 0, size = 10;
PageRequest pageRequest = PageRequest.of(page, size, (Sort.Order.desc("gmtCreate")));
Page<User> userPage = userRepository.findAll(pageRequest);
如果即想做分页查询又需要条件查询,可以再实现接口 JpaSpecificationExecutor<T>
,使用 Specification
设置条件。示例代码如下:
// 分页查询第一页用户,限制每页10条记录,按创建时间降序排序,同时要求用户的id大于2
PageRequest pageRequest = PageRequest.of(0, 10, (Sort.Order.desc("gmtCreate")));
Specification<User> specification = (root, query, builder) -> {
/*
root:实体对象引用,这里是user
query:规则查询对象
builder:规则构建对象
*/
List<Predicate> predicates = new ArrayList<>();
predicates.add(builder.greaterThan(root.get("id").as(Integer.class), 2));
return builder.and(predicates.toArray(new Predicate[predicates.size()]));
};
userPage = userRepository.findAll(specification, pageRequest);
2.基于方法名字查询
Spring Data 通过查询的方法名和参数名来自动构造一个JPA OQL查询。也就是说,我们可直接在自己的Repository中按照Spring Data 的规则定义方法,有Spring Data帮我们实现。
示例代码如下:
/**
* 根据用户名模糊查询
* @param name 需要包含%或者?
* @return userList
*/
List<User> findByNameLike(String name);
下面附上Spring Data支持的关键字:(官方地址)
3.@Query查询
使用 @Query
注解标记在Repository方法上可以使用JPQL进行查询,示例代码如下:
@Query(value = "select u from User u where u.id = ?1")
User getOneByJpql(Integer id);
也可以使用原生SQL查询,不过要把设置 nativeQuery = true
,示例代码如下:
@Query(value = "select * from user where user_id =:id", nativeQuery = true)
User getOneBySql(@Param("id") Integer id);
注意:Idea中使用JPA原生sql查询时需要配置好Database的数据源,指定数据库方言,指定Schema。
否则可能遇到异常报错如:
SQL dialect is not configured
This inspection performs unresolved SQL references check
现在重新来看上面分页查询加条件查询,利用@Query是如何轻松的解决的。示例代码如下:
@Query(value ="select u from User u where u.id > :id")
Page<User> queryUsers(@Param("id") Integer id, Pageable pageable);
// 方法中调用查询
Page<User> userPage =
userRepository.queryUsers(2, PageRequest.of(0, 10, (Sort.Order.desc("gmtCreate"))));
@Query允许SQL更新、删除语句,但必须搭配 @Modifying
使用。示例代码如下:
@Modifying
@Query(value = "update User u set = ?1 where u.id = ?2")
int update(String name, Integer id);
4.EntityManager
这个还不熟悉,留着以后补充。
四、集成Mybatis
pom文件引入mybatis支持:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.2</version>
</dependency>
配置文件指定mapper文件所在处,例如我的:
mybatis:
mapper-locations: classpath:mapper/*Mapper.xml
启动类指定mapper java文件所在包,例如我的:
@MapperScan(basePackages = {"pers.hdh.d_database.mapper"})
@SpringBootApplication
public class DDatabaseApplication {
//...
}
然后在指定包下创建Mapper Java文件,mapper文件夹下创建xml文件即可。