第三部分:O/R Mapping实践

15认识Spring Data JPA
对象与关系的范式不匹配、
------ObjectRDBMS
粒度
继承没有
唯一性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 及其相关⼯具的基本⽤法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值