15认识Spring Data JPA
对象与关系的范式不匹配、
------ | Object | RDBMS |
---|---|---|
粒度 | 类 | 表 |
继承 | 有 | 没有 |
唯一性 | a==b&a.equals(b) | 主键 |
关联 | 引用 | 外键 |
数据访问 | 逐级访问 | SQL数量要少 |
Hibernate
- 一款开源的对象关系映射(Object/Relational Mapping) 框架
- 将开发者从95%的常见数据持久化工作中解放出来
- 屏蔽了地城数据库的各种细节
Hibernate 发展历程
- 2001年,Gavin King发布第一个版本
- 2003年,Hibernate开发团队加入JBoss
- 2006年,Hibernate3.2成为JPA实现
Java Persistence
JPA为对象关系映射提供了一种基于POJO的持久化模型
- 简化数据持久化代码的开发工作
- 为Java社区屏蔽不同持久化API的差异
2006年,JPA1.0作为JSP220的一部分正式发布
Spring Data
在保留底层存储特性的同时,提供相对一致的、基于Spring的编程模型
主要模块
- Spring Data Commons
- Spring Data JDBC
- Spring Data JPA
- Spring Data Redis
- …
<dependencyManagment>
<dependencies>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-releasetrain</artifactId>
<version>Lovelace-SR4</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependencies>
</dependencyManagment>
<dependecy>
<groupId>org.springframwork.boot</groupId>
<artigfactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
16定义JPA实体对象
常用JPA注解
实体
- @Entity、@MappedSuperclass
- @Table(name)
主键
- @Id
- @GeneratedValue(strategy,generator)
- @SequenceGenerator(name,sequenceName)
常用JPA注解
@Entity(name = "Product")
public static class Product{
@Id
@GeneratedValue(
stratregy = GenerationType.SEQUENCE,
generator = "sequence-generator"
)
@SequenceGenerator(
name = "sequence-generator"
sequenceName = "product_sequence"
)
private Long id;
@Column(name = "product_name")
private String name;
}
常用JPA注解
映射
- @Column(name,nullable,length,insertable,updatable)
- @JoinTable(name)、@JoinColum(name)
关系
- @OneToOne、@OneToMany、@ManyToOne、@ManyToMany
- @OrderBy
Project Lombok
Project Lombok 能够自动嵌入IDE和构建工具,提升开发效率
常用功能
- @Getter / @Setter
- @ToString
- @NoArgsConstructor / @RequiredArgsConstructor / @AllArgsConstructor
- @Data
- @Builder
- @Slf4j / @CommonsLog / @Log4j2
17线上咖啡馆实战项目SpringBucks
项目目标
通过一个完整的例子演示Spring全家桶各主要成员的用法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zg6r07v1-1630909610459)(https://note.youdao.com/yws/api/personal/file/D1ABA98AB3DF45D8AD676013F1752054?method=download&shareKey=214199f13f9567517ed7b36a9c3b79f5)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HVgn8Xup-1630909610461)(https://note.youdao.com/yws/api/personal/file/D2DD2E80D1A34D2DA41290636DAFF811?method=download&shareKey=cb478a2e3755c433763c6249f3b99b41)]
项目中的对象实例
实体
- 咖啡、订单、顾客、服务员、咖啡师
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nawQoKRE-1630909610462)(https://note.youdao.com/yws/api/personal/file/F8DB963B421748779E335B89955CB022?method=download&shareKey=ed66e11466df47482912163254c8a5f5)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RdjIwOJO-1630909610464)(https://note.youdao.com/yws/api/personal/file/28456483E9A1424A818913D05FFAAFC6?method=download&shareKey=e1d15d06f424df32f97cc4524a561b2c)]
项目配置application.properties
## 创建表结构,结束后删除
spring.jpa.hibernate.ddl-auto=create-drop
## 打印灭一条sql
spring.jpa.properties.hibernate.show_sql=true
## 对SQL进行格式化输出
spring.jpa.properties.hibernate.format_sql=true
实体定义
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--通过joda-money 对象来映射金额-->
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>1.0.1</version>
</dependency>
<!--urertype 帮助实现映射-->
<dependency>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.core</artifactId>
<version>6.0.1.GA</version>
</dependency>
</dependencies>
基础类
// @MappedSuperclass 此注解为super类,表名为基础类
@MappedSuperclass
// @Data 此注解为类创建get set方法,toString方法
@Data
// @NoArgsConstructor 生成空的构造方法
@NoArgsConstructor
// @AllArgsConstructor 生成全属性的构造方法
@AllArgsConstructor
public class BaseEntity {
@Id
@GeneratedValue
private Long id;
// 这里 @Column(updatable = false) 表示此列不被更新
@Column(updatable = false)
// @CreationTimestamp 自动生成时间戳
@CreationTimestamp
private Date createTime;
// @UpdateTimestamp 自动更新时间戳
@UpdateTimestamp
private Date cpdateTime;
}
咖啡类
@Entity
@Table(name = "T_MENU")
@Data
// @ToString(callSuper = true)这里指明toString方法打印父类方法
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Coffee extends BaseEntity implements Serializable {
private String name;
@Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyAmount",
parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CNY")})
private Money price;
}
制作开发命令类
@Entity
@Data
@Table(name = "T_ORDER")
@Builder
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class CoffeeOrder extends BaseEntity implements Serializable {
private String customer;
// @ManyToMany表示多对多的关系
@ManyToMany
//
@JoinTable(name = "T_ORDER_COFFEE")
private List<Coffee> items;
private OrderState state;
}
咖啡状态枚举类
public enum OrderState {
INIT, PAID, BREWING, BREWED, TAKEN, CANCELLED
}
通过Spring Data JPA 操作数据库
Repository
@EnableJpaRepositories
Respository<T,ID>
- CrudRepository<T,ID>
- PagingAndSortingRepository<T,ID>
- JpaRepository<T,ID>
定义查询
根据方法名定义查询
- find…by…/ read…By… / query…By… / get…By…
- count…By…
- …OrderBy…[Asc / Desc]
- And / Or / IgoreCase
- Top / First / Disinct
分页查询
分页查询
分页查询
- PagingAndSoringRepository<T,ID>
- Pageable / Sort
- Slice / Page
保存实体
Coffee latte = Coffee.builder().name("latte")
.price(Money.of(CurrencyUnit.of("CNY"), 30.0))
.build();
coffeeRepository.save(latte);
log.info("Coffee: {}", latte);
Coffee espresso = Coffee.builder().name("espresso")
.price(Money.of(CurrencyUnit.of("CNY"), 20.0))
.build();
coffeeRepository.save(espresso);
log.info("Coffee: {}", espresso);
保存实体
CoffeeOrder order = CoffeeOrder.builder()
.customer("Li Lei")
.items(Collections.singletonList(espresso))
.state(OrderState.INIT)
.build();
orderRepository.save(order);
log.info("Order: {}", order);
order = CoffeeOrder.builder()
.customer("Li Lei")
.items(Arrays.asList(espresso, latte))
.state(OrderState.INIT)
.build();
orderRepository.save(order);
log.info("Order: {}", order);
查询实体
@NoRepositoryBean
public interface BaseRespostory<T,Long> extends PagingAndSortingRepository<T,Long> {
List<T> findTop3ByOrderByUpdateTimeDescIdAsc();
}
public interface CoffeeOrderRepository extends BaseRepository<CoffeeOrder, Long> {
List<CoffeeOrder> findByCustomerOrderById(String customer);
List<CoffeeOrder> findByItems_Name(String name);
}
查询实体
coffeeRepository
.findAll(Sort.by(Sort.Direction.DESC, "id"))
.forEach(c -> log.info("Loading {}", c));
List<CoffeeOrder> list = orderRepository.findTop3ByOrderByUpdateTimeDescIdAsc(); log.info("findTop3ByOrderByUpdateTimeDescIdAsc: {}", getJoinedOrderId(list));
list = orderRepository.findByCustomerOrderById("Li Lei");
log.info("findByCustomerOrderById: {}", getJoinedOrderId(list));
// 不开启事务会因为没Session而报LazyInitializationException
list.forEach(o -> {
log.info("Order {}", o.getId());
o.getItems().forEach(i -> log.info(" Item {}", i));
});
list = orderRepository.findByItems_Name("latte");
log.info("findByItems_Name: {}", getJoinedOrderId(list));
19 Repository 是怎么从接⼝变成 Bean 的
Repository Bean 是如何创建的
JpaReositoriesRegistrar
- 激活了@EnableJpaRepositories
- 返回JpaRepostoryConfigExtension
RepositoryBeanDefinitionRegistrarSupport.registerBeanDefinitions
- 注册Reository Bean(类型是JpaRepositoryFactoryBean)
RepositoryConfigurationExtensionSupport.getRepositoryConfiguarations
- 取得Repostory配置
JpaRepositoryFactory.getTargetRepository
- 创建了Repository
接口中的方法是如何被解释的
RepositoryFactorySupport.getRepository 添加了Advice
- DefaultMethodInvokingMethodInterceptor
- QueryExcutorMethodInterceptor
AbstractJpaQuery.execute 执行具体的查询
语法解释在Part中
20通过MyBatis操作数据库
认识MyBatis
MyBatis(https://github.com/mybatis/mybatis-3)
- 一款优秀的持久层框架
- 支持定制化SQL、存储过程和高级映射
在Spring中使用MyBaits
- MyBatis Spring Adapter (https://github.com/mybatis/spring)
- MyBatis Spring-Boot-Starter(https://github.com/mybatis/spring-boot-starter)
简单配置
- mybatis.mapper-locations = classpath*.mapper/**/*.xml
- mybatis.type-aliases-package = 类型别名的包名
- mybaits.type-handlers-package = TypeHandler扫描包名
- mybaits-configuration.map-underscore-to-camel-case =ture
Mapper的定义与扫描
- @MapperScan 配置扫描位置
- @Mapper 定义接口
- 映射的定义 —— —— XML与注解
Mapper的定义与扫描
public interface CoffeeMapper(
@Insrt("insert into t_coffee(name,price,create_time,update_time)" + "value (#{name},#{price},now(),noew())")
@Options(usrGeneratedKeys = true)
int save(Coffee coffee);
@Selecct("select * from t_coffee where id = #{id}")
@Results({
@Result(id = ture,column = "id",property = "id"),
@Result(column = "create_time",property = "createTime"),
// map-underscoure-to-camel-case = true 可以实现一样的效果
// @Result(colum = "update_time",property = "updateTime")
})
Coffee findById(@Param("id") Long id);
)
21让MyBatis 更好⽤的那些⼯具MyBatis Generator
认识MyBatis Generator
MyBatis Generator(http://www.mybatis.org/generator/)
- MyBatis 代码生成器
- 根据数据库表生成相关代码
- POJO
- Mapper接口
- SQL Map XML
运行MyBatis Generator
命令行
- java -jar mybatis-generator-core-x.x.x.jar -configfile generatorConfig.xml
Maven Plugin(mybatis-generator-maven-plugin)
- mvn mybatis-generator:generate
- ${basedir}/src/main/resources/generatorConfig.xml
Eclipse Plugin
Java 程序
Ant Task
配置Mybatis Generator
generatorConfiguartion
context
- jdbcConnection
- javaModelGenerator
- sqlMapGenerator
- javaClientGenerator(Annoratedmapper/xmlapper/mixedmapper)
- table
生成是可以使用的插件
内置插件都在org.mybatis.generator.plugins 包中
- FluentBuilderMethodsPlugin
- ToStringPlugin
- SerializablePlugin
- RowBounds
- …
使⽤⽣成的对象
- 简单操作,直接使⽤⽣成的 xxxMapper 的⽅法
- 复杂查询,使⽤⽣成的 xxxExample 对象
使⽤⽣成的对象
Coffee latte = new Coffee()
.withName("latte")
.withPrice(Money.of(CurrencyUnit.of("CNY"),30.0)
.withCreateTime(new Date())
.withUpdateTime(new Date());
coffeeMapper.insert(latter);
Coffee s = coffeeMapper.selectByPrimaryKey(1L);
log.info("Coffee{}",s);
CoffeeExample example = new CoffeeExample();
example.createCriteria().andName.EqualTo("latte");
List<Coffee> list = coffeeMapper.selectByExample(example);
list.forEach(e -> log.info("selectByExample:{}",e));
22让 MyBatis 更好⽤的那些⼯具Mybatis PageHelper
认识Mybaits PageHelper
Mybatis PageHepler(https://pagehelper.github.io)
- 支持多数据库
- 支持多种分页方式
- SpringBoot 支持(https://github.com/pagehelper/pagehepler=spring-boot)
- pageehelper-spring-boot-starter
23 SpringBucks 进度⼩结
本章小结
- 简单了解了 Java Persistence API 的背景
- 学习了 JPA 的常⽤注解
- 学习了 Lombok 的⽤法
- 学习了 Spring Data JPA 的基本⽤法
- 学习了 MyBatis 及其相关⼯具的基本⽤法