3.ORM实践

news2024/7/6 18:04:57

文章目录

  • 3.1 介绍Spring Data JPA
    • JPA(Java Persistence API)标准
    • Hibernate
    • Spring Data
    • Spring Data JPA
      • 引入依赖
  • 3.2 定义JPA的实体对象
    • 常用JPA注解
      • 实体
      • 主键
      • 映射
      • 关系
    • 常用lombok注解
  • 3.3 SpringBucks线上咖啡馆实战项目
    • (1)项目目标
    • (2)项目中的对象实体
    • (3)导入所需的依赖
    • (4)application.properties
    • (5)实体对象初定义
      • Coffee
      • CoffeeOrder
      • 执行的SQL
    • (6)将实体共有属性抽出组成BaseEntity
    • (7)通过Spring Data JPA操作数据库
      • 保存实体
      • 查询实体
        • 根据方法名定义查询
        • 涉及到分页查询
    • (8)Spring Data JPA中Repository怎么从接口变成Bean?为什么方法可以不具体实现?
      • Repository Bean的创建
      • 接口中的方法是如何被解释的
  • 3.4 通过MyBatis操作数据库
    • MyBatis
    • 引入依赖
    • 简单配置
    • Mapper扫描、定义、使用
    • Mapper接口示例
  • 3.5 Mybatis实例
    • (1)引入依赖
    • (2)schema.sql建表
    • (3)配置文件
    • (4)实体类
    • (5)Mapper接口
    • (6)自定义TypeHandler
    • (7)使用Mapper保存和查询数据
  • 3.6 让MyBatis更好用的工具
    • MyBatis Generator介绍
      • 使用MyBatis Generator
        • 配置generatorConfig.xml 结构
        • (1)命令行
        • (2)Maven Pugin(mybatis-generator-maven-plugin)
        • (3)Java程序
      • 生成的文件
      • 使用生成的文件
      • 实例
        • (1)引入依赖
        • (2)创建表
        • (3)generatorConfig.xml
        • (4)调用
        • (5)将自动生成的和手写的分开放
    • MyBatis PageHelper
      • 引入依赖
      • application.properties
      • 常用的分页方式

3.1 介绍Spring Data JPA

JPA(Java Persistence API)标准

  • 2006年,JPA 1.0作为JSP 220的一部分正式发布
  • JPA为对象关系映射提供了一种基于POJO的持久化模型
    • 简化持久化代码开发工作
    • 屏蔽不同持久化API的差异

Hibernate

  • 一款开源的ORM(Object Relational Mapping)框架
  • 屏蔽了底层数据库的各种细节
  • 2006年,Hiberbate 3.2 实现 JPA 规范

Spring Data

Spring Data是Spring 的一个子项目。用于简化数据库访问,支持NoSQL和关系数据库存储。其主要目标是使数据库的访问变得方便快捷。

Spring Data 项目所支持NoSQL存储:

  • Spring Data MongoDB(文档数据库)
  • Spring Data Neo4j (图形数据库)
  • Spring Data Redis(键/值存储)
  • Spring Data Hbase(列族数据库)

Spring Data 项目所支持的关系数据存储技术:

  • Spring Data JDBC
  • Spring Data JPA

Spring Data JPA

Spring Data JPA是Spring Data大家庭的一部分,它使得那些以JPA接口为规范的应用更加方便, 致力于减少数据访问层(DAO)的开发量。

Spring Data JPA 底层默认的使用的是 Hibernate 来做的 JPA 实现。

引入依赖

Spring:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-releasetrain</artifactId>
    <version>Lovelace-SR4</version>
    <scope>import</scope>
    <type>pom</type>
</dependency>

SpringBoot:

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

3.2 定义JPA的实体对象

常用JPA注解

实体

  • @Entity(name):注明这个类是一个实体
  • @Table(name) :实体和对应表关联起来
  • @MappedSuperclass :在多个实体的父类上标注

主键

https://www.cnblogs.com/lich/archive/2011/11/29/2268253.html

  • @Id
    • @GeneratedValue(strategy)
      • strategy:AUTO:自动选择合适的策略,IDENTITY:mysql数据库ID自增长的方式来生成主键值。默认是GenerationType.AUTO,就是说可以简单写成这样@GeneratedValue。

映射

  • @Column(name, nullable, length, insertable, updatable)
  • @JoinTable(name) 、@JoinColumn(name):表的关联

关系

  • @OneToOne 、@OneToMany 、@ManyToOne 、@ManyToMany
  • @OrderBy

常用lombok注解

  • @Data
    • @Getter
    • @Setter
    • @ToString
    • @RequiredArgsConstructor
  • @NoArgsConstructor:如果存在没有被初始化的final属性,使用@NoArgsConstructor注解会报错
@NoArgsConstructor
pulic class Entity {
	private final int i;  // 未被初始化
}
  • @RequiredArgsConstructor:生成一个由所有final与@NotNull属性组成的构造方法。
@Data // 包含@RequiredArgsConstructor
pulic class Entity {
	private final int i; 
	private final String name;
	private int age;
	
	// lombok生成的构造方法
	public Entity(int i, String name) {
		this.i = i;
	}
}
  • @AllArgsConstructor
  • @Builder:生成build方法
@Builder
public class Product {
    @Id
    private long id;

    public static void main(String[] args) {
        ProductBuilder builder = Product.builder();
        Product build = builder.id(111).build();
    }
}
  • @Slf4j、@CommonsLog、@Log4j

3.3 SpringBucks线上咖啡馆实战项目

(1)项目目标

通过一个完整的例子演示Spring全家桶各主要成员的用法。

  • 咖啡的菜单放到缓存中(redis)
  • RabbitMQ
    在这里插入图片描述

(2)项目中的对象实体

(1)实体:顾客、服务员、咖啡师、订单、咖啡
(2)实体之间的关系:
在这里插入图片描述
(3)订单生成及其状态机流转
在这里插入图片描述

(3)导入所需的依赖

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	
	<dependency>
		<groupId>org.joda</groupId>
		<artifactId>joda-money</artifactId>   // 不用浮点数,用Money类定义金额,也便于处理货币单位,货币转换,汇率之类的问题
		<version>1.0.1</version>
	</dependency>
	
	<dependency>
		<groupId>org.jadira.usertype</groupId>
		<artifactId>usertype.core</artifactId>  // joda-money映射需要
		<version>6.0.1.GA</version>
	</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>
</dependencies>

(4)application.properties

# 启动时检查表是否存在,存在则删除,然后创建表结构,结束之后删除
spring.jpa.hibernate.ddl-auto=create-drop
# 打印sql并格式化输出
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true

# 默认的h2数据库
# spring.datasource.url=jdbc:h2:mem:testdb
# spring.datasource.username=sa
# spring.datasource.password=

# 数据库连接池
# spring.datasource.hikari.maximum-pool-size=5
# spring.datasource.hikari.minimum-idle=5
# spring.datasource.hikari.idleTimeout=600000
# spring.datasource.hikari.connectionTimeout=30000
# spring.datasource.hikari.maxLifetime=1800000

# 启用h2控制台
spring.h2.console.enabled=true
# h2控制台访问地址,http://localhost:8080/h2
spring.h2.console.path=/h2

(5)实体对象初定义

Coffee

@Entity
@Table(name = "T_MENU")
@Builder
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Coffee implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String name;

    @Column
    @Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyAmount",
            parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CNY")})
    /* PersistentMoneyAmount,数据库类型为decimal(19,2)*/
    /* PersistentMoneyMinorAmount,数据库类型为bigint,20.0存为2000*/
    private Money price;

    @Column(updatable = false)
    @CreationTimestamp
    private Date createTime;

    @Column
    @UpdateTimestamp
    private Date updateTime;
}

CoffeeOrder

@Entity
@Table(name = "T_ORDER")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class CoffeeOrder implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String customer;

    @ManyToMany
    @JoinTable(name = "T_ORDER_COFFEE")
    private List<Coffee> items;

    @Column(nullable = false)
    private Integer state;

    @Column(updatable = false)
    @CreationTimestamp
    private Date createTime;

    @Column
    @UpdateTimestamp
    private Date updateTime;
}

执行的SQL

create table t_menu (
   id bigintnot null,
    create_time timestamp,
    name varchar(255),
    price decimal(19,2),
    update_time timestamp,
    primary key (id)
)
create table t_order (
   	id bigintnot null,
	create_time timestamp,
    customer varchar(255),
    state integer not null,
    update_time timestamp,
    primary key (id)
)
create table t_order_coffee (
    coffee_order_id bigint not null,
	items_id bigint not null
)
alter table t_order_coffee add constraint FKj2swxd3y69u2tfvalju7sr07q 
foreign key (items_id) references t_menu
alter table t_order_coffee add constraint FK33ucji9dx64fyog6g17blpx9v 
foreign key (coffee_order_id) references t_order

(6)将实体共有属性抽出组成BaseEntity

@MappedSuperclass   // 实体类的父类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(updatable = false)
    @CreationTimestamp
    private Date createTime;

    @UpdateTimestamp
    private Date updateTime;
}
@Entity
@Table(name = "T_MENU")
@Data
@ToString(callSuper = true)  // 父类的属性也会打印
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Coffee extends BaseEntity implements Serializable {
    private String name;

    @Type(type = "org.jadira.usertype.moneyandcurrency.joda.PersistentMoneyMinorAmount",
            parameters = {@org.hibernate.annotations.Parameter(name = "currencyCode", value = "CNY")})
    private Money price;
}
@Entity
@Table(name = "T_ORDER")
@Data
@Builder
@ToString(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor
public class CofferOrder extends BaseEntity implements Serializable {

    private String customer;

    @ManyToMany
    @JoinTable(name = "T_ORDER_COFFEE")
    @OrderBy("id")
    private List<Coffee> items;

    @Enumerated
    @Column(nullable = false)
    private OrderState state;  // 会映射为数据库的integer类型
}
public enum OrderState {
    INIT, PAID, BREWING, BREWED, TAKEN, CANCELLED
}

(7)通过Spring Data JPA操作数据库

保存实体

  • 启动类上加@EnableJpaRepositories ,自动发现CrudRepository等接口拓展
  • 自定义接口实现Repository<T, ID>接口,泛型中指定实体对象和主键类型
    • CrudRepository<T, ID>,PagingAndSortingRepository<T, ID>,JpaRepository<T, ID>
public interface CoffeeOrderRepository extends CrudRepository<CoffeeOrder, Long> {
}
public interface CoffeeRepository extends CrudRepository<Coffee, Long> {
}
@SpringBootApplication
@Slf4j
@EnableJpaRepositories    // 自动发现CrudRepository等接口拓展
@RestController
public class SpringBucksApplication {
	@Autowired
	CoffeeRepository coffeeRepository;

	@Autowired
	CoffeeOrderRepository orderRepository;

	public static void main(String[] args) {
		SpringApplication.run(SpringBucksApplication.class, args);
	}

	@RequestMapping("/initOrders")
	private void initOrders() {
		// 意式浓缩咖啡
		// CNY:Chinese yuan
		Coffee espresso = Coffee.builder().name("espresso") 
				.price(Money.of(CurrencyUnit.of("CNY"), 20.0))
				.build();
		coffeeRepository.save(espresso);
		// 在做完save操作之后会把生成的值赋到对象里
		log.info("Coffee: {}", espresso);
		
		// 拿铁本指牛奶,抹茶拿铁中没有咖啡
		// 意式拿铁:Espresso + 牛奶
		// 美式拿铁:Espresso + 牛奶 + 奶沫
		// 玛奇朵:Espresso + 奶沫
		Coffee latte = Coffee.builder().name("latte") 
				.price(Money.of(CurrencyUnit.of("CNY"), 30.0))
				.build();
		// 在做完save操作之后会把生成的值赋到对象里
		coffeeRepository.save(latte);
		log.info("Coffee: {}", latte);

		CoffeeOrder order = CoffeeOrder.builder()
				.customer("Li Lei")
				.items(Collections.singletonList(espresso))
				.state(OrderState.INIT)
				.build();
		// 在做完save操作之后会把生成的值赋到对象里
		orderRepository.save(order);
		log.info("Order: {}", order);

		order = CoffeeOrder.builder()
				.customer("Li Lei")
				.items(Arrays.asList(espresso, latte))
				.state(OrderState.INIT)
				.build();
		// 在做完save操作之后会把生成的值赋到对象里
		orderRepository.save(order);
		log.info("Order: {}", order);
	}
}

查询实体

根据方法名定义查询

  • find…By…,read…By…,query…By,get…By…
  • count…By…
  • …OrderBy…[Asc / Desc]
  • And ,Or , IgnoreCase
  • Top,First , Distinct

涉及到分页查询

  • PagingAndSortingRepository<T, ID>
  • Pageable ,Sort
  • Slice , Page
@NoRepositoryBean  // 告诉Spring不需要为BaseRepository创建一个Bean
public interface BaseRepository<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);
}
public interface CoffeeRepository extends BaseRepository<Coffee, Long> {
}
@SpringBootApplication
@Slf4j
@EnableJpaRepositories // 自动发现CrudRepository等接口拓展
@RestController
public class SpringBucksApplication {
	@Autowired
	CoffeeRepository coffeeRepository;

	@Autowired
	CoffeeOrderRepository orderRepository;

	@Autowired
	DataSource dataSource;

	public static void main(String[] args) {
		SpringApplication.run(SpringBucksApplication.class, args);
	}

	@RequestMapping("/test")
	@Transactional
	public String test() throws Exception {
		initOrders();
		findOrders();
		Connection();
		return "访问成功";
	}

	public void Connection() throws Exception{
		log.info("数据源: {}", dataSource.toString());
		Connection connection = dataSource.getConnection();
		log.info("数据源: {}", connection.toString());
	}

	public void initOrders() {
		Coffee espresso = Coffee.builder().name("espresso")
				.price(Money.of(CurrencyUnit.of("CNY"), 20.0))
				.build();
		coffeeRepository.save(espresso);
		log.info("Coffee: {}", espresso);

		Coffee latte = Coffee.builder().name("latte")
				.price(Money.of(CurrencyUnit.of("CNY"), 30.0))
				.build();
		coffeeRepository.save(latte);
		log.info("Coffee: {}", latte);

		Coffee macchiato = Coffee.builder().name("Macchiato")
				.price(Money.of(CurrencyUnit.of("CNY"), 35.0))
				.build();
		coffeeRepository.save(macchiato);
		log.info("Coffee: {}", macchiato);

		Coffee americano = Coffee.builder().name("Americano")
				.price(Money.of(CurrencyUnit.of("CNY"), 20.0))
				.build();
		coffeeRepository.save(americano);
		log.info("Coffee: {}", americano);

		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);
	}

	public void findOrders() throws InterruptedException {
		// (1)菜单:所有咖啡种类,按id做降序排列
		coffeeRepository
				.findAll(Sort.by(Sort.Direction.DESC, "id"))
				.forEach(c -> log.info("=====================Menu {}", c));

		// (2)最新出品:所有咖啡种类,更新时间降序,id升序,前3条
		List<Coffee> coffeeList = coffeeRepository.findTop3ByOrderByUpdateTimeDescIdAsc();
		log.info("=====================Top3 Coffee: {}", getName(coffeeList));

		// (3)根据用户名查询用户的所有订单
		List<CoffeeOrder> list = orderRepository.findByCustomerOrderById("Li Lei");
		log.info("=====================Li Lei的订单: {}", list);
		list.forEach(e -> log.info("=====================items" + e.getItems()));
	}

	public String getName(List<Coffee> list) {
		return list.stream().map(o -> o.getName().toString())
				.collect(Collectors.joining(","));
	}
}

(8)Spring Data JPA中Repository怎么从接口变成Bean?为什么方法可以不具体实现?

Repository Bean的创建

在这里插入图片描述

接口中的方法是如何被解释的

在这里插入图片描述

3.4 通过MyBatis操作数据库

MyBatis

  • 一款优秀的持久层框架
  • 支持定制化SQL、存储过程和高级映射
  • JPA中SQL都是框架生成的,MyBatis中SQL都是手写的(也可以用工具生成SQL)。
  • 如果SQL操作简单使用JPA,如果DBA对SQL有较高的把控要求、加了很多join、大厂用MyBatis。

引入依赖

  • Spring
<dependency>
  	<groupId>org.mybatis</groupId>
	<artifactId>mybatis</artifactId>
	<version>3.5.1</version>
</dependency>
<dependency>
	<groupId>org.mybatis</groupId>
	<artifactId>mybatis-spring</artifactId>
	<version>1.3.1</version>
</dependency>
  • Spring-Boot
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.1.0</version>
</dependency>

简单配置

  • mybatis.mapper-locations = classpath*:mapper/**/*.xml
  • mybatis.type-aliases-package = com.example.entity
  • mybatis.type-handlers-package = TypeHandler的包前缀:不用再指定TypeHandler
  • mybatis.configuration.map-underscore-to-camel-case = true :将带有下划线的表字段映射为驼峰格式的实体类属性,省去了在mapper.xml文件中编写表字段列表与表实体类属性的映射关系,即resultMap。

Mapper扫描、定义、使用

  • @MapperScan(“com.mapper”):加在启动类上配置Mapper接口扫描位置。也可以写个配置类,加在上面。
@Configuration
@MapperScan("com.mapper")
public class MapperConfig {

}
  • @Mapper:加在定义的Mapper接口上面
  • 映射的定义:
    • @MapperScan配置下的@Mapper接口
    • mybatis.mapper-locations目录下的xml文件

Mapper接口示例

  • @Options的作用是在执行完sql之后把生成的id回填到Coffee对象中
  • insert、update、delete方法返回的是影响的条数
  • @Results指定结果集的映射
@Mapper
public interface CoffeeMapper {
    @Insert("insert into t_coffee (name, price, create_time, update_time)"
            + "values (#{name}, #{price}, now(), now())")
    @Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
    int save(Coffee coffee);

    @Select("select * from t_coffee where id = #{id}") 
    @Results({
            // 默认会根据名字映射
            @Result(id = true, column = "id", property = "id"),  // 主键
            @Result(column = "create_time", property = "createTime"),
            // map-underscore-to-camel-case = true 可以实现一样的效果
            // @Result(column = "update_time", property = "updateTime"),
    })
    Coffee findById(@Param("id") Long id);
}

3.5 Mybatis实例

(1)引入依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- mybatis -->
<dependency>
	<groupId>org.mybatis.spring.boot</groupId>
	<artifactId>mybatis-spring-boot-starter</artifactId>
	<version>1.3.2</version>
</dependency>
<!-- joda-money -->
<dependency>
	<groupId>org.joda</groupId>
	<artifactId>joda-money</artifactId>
	<version>LATEST</version>
</dependency>
<!-- h2 -->
<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>

(2)schema.sql建表

create table t_coffee (
    id bigint not null auto_increment,
    name varchar(255),
    price bigint not null,
    create_time timestamp,
    update_time timestamp,
    primary key (id)
);

(3)配置文件

# 不用指定TypeHandler
mybatis.type-handlers-package=geektime.spring.data.mybatisdemo.handler
mybatis.configuration.map-underscore-to-camel-case=true

(4)实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Coffee {
    private Long id;
    private String name;
    private Money price;
    private Date createTime;
    private Date updateTime;
}

(5)Mapper接口

@Mapper
public interface CoffeeMapper {
    @Insert("insert into t_coffee (name, price, create_time, update_time)"
            + "values (#{name}, #{price}, now(), now())")
    // 因为配置了mybatis.type-handlers-package,所以不用指定typeHandler
    // #{price, typeHandler=geektime.spring.data.mybatisdemo.handler.MoneyTypeHandler}
    @Options(useGeneratedKeys = true, keyColumn="id", keyProperty="id")
    int save(Coffee coffee);
	
    @Select("select * from t_coffee where id = #{id}")
    @Results({
            // 默认会根据名字映射
            // map-underscore-to-camel-case = true 可以实现一样的效果
            // 因为配置了mybatis.type-handlers-package,所以不用指定typeHandler
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "create_time", property = "createTime"),
            @Result(column = "update_time", property = "updateTime")
            // 因为配置了mybatis.type-handlers-package,所以不用指定typeHandler
            // @Result(column = "price", property = "price", typeHandler = geektime.spring.data.mybatisdemo.handler.MoneyTypeHandler.class),
    })
    Coffee findById(@Param("id") Long id);
}

(6)自定义TypeHandler

/**
 * 在 Money 与 Long 之间转换的 TypeHandler,处理 CNY 人民币
 */
public class MoneyTypeHandler extends BaseTypeHandler<Money> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Money parameter, JdbcType jdbcType) throws SQLException {
    	// 获得一个代表分的金额
        ps.setLong(i, parameter.getAmountMinorLong());
    }

    @Override
    public Money getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return parseMoney(rs.getLong(columnName));
    }

    @Override
    public Money getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return parseMoney(rs.getLong(columnIndex));
    }

    @Override
    public Money getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return parseMoney(cs.getLong(columnIndex));
    }

    private Money parseMoney(Long value) {
    	// 转成人民币类型
    	return Money.ofMinor(CurrencyUnit.of("CNY"), value);
        // return Money.of(CurrencyUnit.of("CNY"), value / 100.0);
    }
}

(7)使用Mapper保存和查询数据

@SpringBootApplication
@Slf4j
@MapperScan("geektime.spring.data.mybatisdemo.mapper")
public class MybatisDemoApplication implements ApplicationRunner {
	@Autowired
	private CoffeeMapper coffeeMapper;

	public static void main(String[] args) {
		SpringApplication.run(MybatisDemoApplication.class, args);
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		Coffee c = Coffee.builder().name("espresso")
				.price(Money.of(CurrencyUnit.of("CNY"), 20.0)).build();
		int count = coffeeMapper.save(c);
		log.info("Save {} Coffee: {}", count, c);

		c = Coffee.builder().name("latte")
				.price(Money.of(CurrencyUnit.of("CNY"), 25.0)).build();
		count = coffeeMapper.save(c);
		log.info("Save {} Coffee: {}", count, c);

		c = coffeeMapper.findById(c.getId());
		log.info("Find Coffee: {}", c);
	}
}

3.6 让MyBatis更好用的工具

MyBatis Generator介绍

  • MyBatis官方提供的一个生成器
  • 根据数据库表自动生成 POJO、Mapper接口、SQL Map XML

使用MyBatis Generator

配置generatorConfig.xml 结构

  • 有顺序
  • generatorConfiguration
    • context
      • plugin:内置插件,生成POJO时自带Builder、toString方法、Serializable、分页
      • jdbcConnection:JDBC连接的信息
      • javaModelGenerator:Model生成配置
      • sqlMapGenerator:Mapper生成配置
      • javaClientGenerator:选择通过注解(简单的)或者XML(有Example参与的)配置
      • table:给哪些表生成映射
<generatorConfiguration>
    <context id="H2Tables" targetRuntime="MyBatis3">
        <plugin type="org.mybatis.generator.plugins.FluentBuilderMethodsPlugin" />
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
        <plugin type="org.mybatis.generator.plugins.RowBoundsPlugin" />

        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/cpmp?characterEncoding=utf8&amp;autoReconnect=true&amp;serverTimezone=PRC"
                        userId="root"
                        password="123456">
        </jdbcConnection>

        <javaModelGenerator targetPackage="geektime.spring.data.mybatis.model"
                            targetProject="./src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <sqlMapGenerator targetPackage="geektime.spring.data.mybatis.mapper"
                         targetProject="./src/main/resources/mapper">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <javaClientGenerator type="MIXEDMAPPER"  # MIXEDMAPPER, ANNOTATEDMAPPER, XMLMAPPER
                             targetPackage="geektime.spring.data.mybatis.mapper"
                             targetProject="./src/main/java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <table tableName="t_coffee" domainObjectName="Coffee" >
            <columnOverride column="price" javaType="org.joda.money.Money" jdbcType="BIGINT"
                            typeHandler="geektime.spring.data.mybatis.handler.MoneyTypeHandler"/>
        </table>
        
        <table tableName="t_order" domainObjectName="Order" >
        </table>
        
        <table tableName="t_coffee_order" domainObjectName="CoffeeOrder" >
        </table>
    </context>
</generatorConfiguration>

(1)命令行

官网(https://mvnrepository.com/)下载 mybatis-generator-core-1.3.7.jar,mysql-connector-java-8.0.19.jar。

java -cp mybatis-generator-core-1.3.7.jar;mysql-connector-java-8.0.19.jar org.mybatis.generator.api.ShellRunner -configfile generatorConfig.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="H2Tables" targetRuntime="MyBatis3">
        <plugin type="org.mybatis.generator.plugins.FluentBuilderMethodsPlugin" />
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
        <plugin type="org.mybatis.generator.plugins.RowBoundsPlugin" />

		<!-- driverClass用com.mysql.cj.jdbc.Driver,不能用com.mysql.jdbc.Driver -->
		<!-- connectionURL要加上时区serverTimezone=GMT,参数之间用&amp;连接,不能用& -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/learnjdbc?serverTimezone=GMT&amp;useUnicode=true&amp;characterEncoding=UTF-8"
                        userId="root"
                        password="123456">
        </jdbcConnection>

        <javaModelGenerator targetPackage="model"
                            targetProject="./">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <sqlMapGenerator targetPackage="mapper1"
                         targetProject="./">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <javaClientGenerator type="MIXEDMAPPER"
                             targetPackage="mapper2"
                             targetProject="./">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <table tableName="t_coffee" domainObjectName="Coffee" >
            <columnOverride column="price" javaType="org.joda.money.Money" jdbcType="BIGINT"
                            typeHandler="geektime.spring.data.mybatis.handler.MoneyTypeHandler"/>
        </table>
        <table tableName="t_order" domainObjectName="Order" >
        </table>
        <table tableName="t_coffee_order" domainObjectName="CoffeeOrder" >
        </table>
    </context>
</generatorConfiguration>

(2)Maven Pugin(mybatis-generator-maven-plugin)

<plugin>
	<groupId>org.mybatis.generator</groupId>
	<artifactId>mybatis-generator-maven-plugin</artifactId>
	<version>1.3.7</version>
	<configuration>
		<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
		<verbose>true</verbose>
		<overwrite>true</overwrite>
	</configuration>
	<!-- 注意:此处需要把依赖引进来 -->
	<dependencies>
		<dependency>
			<groupId>org.mybatis.generator</groupId>
			<artifactId>mybatis-generator-core</artifactId>
			<version>1.3.7</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.19</version>
		</dependency>
	</dependencies>
</plugin>

在idea右侧maven标签中的Plugins中运行mybatis-generator即可生成相对文件

(3)Java程序

private void generateArtifacts() throws Exception {
	List<String> warnings = new ArrayList<>();
	ConfigurationParser cp = new ConfigurationParser(warnings);
	Configuration config = cp.parseConfiguration(
			this.getClass().getResourceAsStream("/generatorConfig.xml"));
	DefaultShellCallback callback = new DefaultShellCallback(true);
	MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
	myBatisGenerator.generate(null);
}

生成的文件

  • 实体类 + Example实体类
    在这里插入图片描述
  • Mapper接口
    在这里插入图片描述
  • Mapper的xml文件
    在这里插入图片描述

使用生成的文件

  • 简单操作:使用Mapper + 实体类
  • 复杂查询:使用Mapper + Example实体类
Coffee espresso = new Coffee()
		.withName("espresso")
		.withPrice(Money.of(CurrencyUnit.of("CNY"), 20.0))
		.withCreateTime(new Date())
		.withUpdateTime(new Date());
coffeeMapper.insert(espresso);

Coffee latte = new Coffee()
		.withName("latte")
		.withPrice(Money.of(CurrencyUnit.of("CNY"), 30.0))
		.withCreateTime(new Date())
		.withUpdateTime(new Date());
coffeeMapper.insert(latte);

Coffee s = coffeeMapper.selectByPrimaryKey(1L);
log.info("Coffee {}", s);

CoffeeExample example = new CoffeeExample();
example.createCriteria().andNameEqualTo("latte");
List<Coffee> list = coffeeMapper.selectByExample(example);
list.forEach(e -> log.info("selectByExample: {}", e));

实例

(1)引入依赖

<dependencies>
	<!-- mybatis-spring-boot-starter -->
	<dependency>
		<groupId>org.mybatis.spring.boot</groupId>
		<artifactId>mybatis-spring-boot-starter</artifactId>
		<version>2.0.0</version>
	</dependency>
	<!-- mybatis-generator-core -->
	<dependency>
		<groupId>org.mybatis.generator</groupId>
		<artifactId>mybatis-generator-core</artifactId>
		<version>1.3.7</version>
	</dependency>
	<dependency>
		<groupId>org.joda</groupId>
		<artifactId>joda-money</artifactId>
		<version>1.0.1</version>
	</dependency>
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>8.0.19</version>
	</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>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
</dependencies>

(2)创建表

create table t_coffee (
    id bigint not null auto_increment,
    name varchar(255),
    price bigint not null,
    create_time timestamp,
    update_time timestamp,
    primary key (id)
);


create table t_order (
    id bigint not null auto_increment,
    create_time timestamp,
    customer varchar(255),
    state integer not null,
    update_time timestamp,
    primary key (id)
);

create table t_coffee_order (
    order_id bigint not null,
    coffee_id bigint not null
);

(3)generatorConfig.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <context id="H2Tables" targetRuntime="MyBatis3">
        <plugin type="org.mybatis.generator.plugins.FluentBuilderMethodsPlugin" />
        <plugin type="org.mybatis.generator.plugins.ToStringPlugin" />
        <plugin type="org.mybatis.generator.plugins.SerializablePlugin" />
        <plugin type="org.mybatis.generator.plugins.RowBoundsPlugin" />

        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://127.0.0.1:3306/learnjdbc?serverTimezone=GMT&amp;useUnicode=true&amp;characterEncoding=UTF-8"
                        userId="root"
                        password="123456">
        </jdbcConnection>

        <javaModelGenerator targetPackage="geektime.spring.data.mybatis.model"
                            targetProject="./src/main/java">
            <property name="enableSubPackages" value="true" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>

        <sqlMapGenerator targetPackage="geektime.spring.data.mybatis.mapper"
                         targetProject="./src/main/resources/mapper">
            <property name="enableSubPackages" value="true" />
        </sqlMapGenerator>

        <javaClientGenerator type="MIXEDMAPPER"
                             targetPackage="geektime.spring.data.mybatis.mapper"
                             targetProject="./src/main/java">
            <property name="enableSubPackages" value="true" />
        </javaClientGenerator>

        <table tableName="t_coffee" domainObjectName="Coffee" >
            <columnOverride column="price" javaType="org.joda.money.Money" jdbcType="BIGINT"
                            typeHandler="geektime.spring.data.mybatis.handler.MoneyTypeHandler"/>
        </table>
        
        <table tableName="t_order" domainObjectName="Order" >
        </table>
        
        <table tableName="t_coffee_order" domainObjectName="CoffeeOrder" >
        </table>
    </context>
</generatorConfiguration>

(4)调用

@SpringBootApplication
@Slf4j
@MapperScan("geektime.spring.data.mybatis.mapper")
public class MybatisGeneratorDemoApplication implements ApplicationRunner {
	@Autowired
	private CoffeeMapper coffeeMapper;

	public static void main(String[] args) {
		SpringApplication.run(MybatisGeneratorDemoApplication.class, args);
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		generateArtifacts();
		playWithArtifacts();
	}

	private void generateArtifacts() throws Exception {
		List<String> warnings = new ArrayList<>();
		ConfigurationParser cp = new ConfigurationParser(warnings);
		Configuration config = cp.parseConfiguration(
				this.getClass().getResourceAsStream("/generatorConfig.xml"));
		DefaultShellCallback callback = new DefaultShellCallback(true);
		MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
		myBatisGenerator.generate(null);
	}

	private void playWithArtifacts() {
		Coffee espresso = new Coffee()
				.withName("espresso")
				.withPrice(Money.of(CurrencyUnit.of("CNY"), 20.0))
				.withCreateTime(new Date())
				.withUpdateTime(new Date());
		coffeeMapper.insert(espresso);

		Coffee latte = new Coffee()
				.withName("latte")
				.withPrice(Money.of(CurrencyUnit.of("CNY"), 30.0))
				.withCreateTime(new Date())
				.withUpdateTime(new Date());
		coffeeMapper.insert(latte);

		Order hanfei_order1 = new Order()
				.withCustomer("HanFei")
				.withState(1)
				.withCreateTime(new Date())
				.withUpdateTime(new Date());
		orderMapper.insert(hanfei_order1);

		Order zhujiahua_order1 = new Order()
				.withCustomer("ZhuJiaHua")
				.withState(1)
				.withCreateTime(new Date())
				.withUpdateTime(new Date());
		orderMapper.insert(zhujiahua_order1);

		CoffeeExample example1 = new CoffeeExample();
		example1.createCriteria().andNameEqualTo("espresso");
		example1.setOrderByClause("id desc");
		List<Coffee> coffees = coffeeMapper.selectByExample(example1);
		System.out.println(coffees);
		coffees.forEach(re -> log.info("============Coffee: {}", re));
	}
}

(5)将自动生成的和手写的分开放

@Mapper
public interface MyCofferMapper {

    @Select("select * from t_coffee where id = #{id}")
    @Results({
            // 默认会根据名字映射
            // map-underscore-to-camel-case = true 可以实现一样的效果
            @Result(id = true, column = "id", property = "id"),
            @Result(column = "create_time", property = "createTime"),
            @Result(column = "update_time", property = "updateTime"),
            @Result(column = "price", property = "price", typeHandler = geektime.spring.data.mybatis.handler.MoneyTypeHandler.class),
    })
    Coffee findById(@Param("id") Long id);
}
Coffee myCofferMapperById = myCofferMapper.findById(1L);
log.info("==========myCofferMapperById: {}" , myCofferMapperById);

MyBatis PageHelper

  • 支持多种数据库
  • 支持多种分页方式
  • SpringBoot支持
    • pagehelper-spring-boot-starter

引入依赖

<dependency>
	<groupId>com.github.pagehelper</groupId>
	<artifactId>pagehelper-spring-boot-starter</artifactId>
	<version>1.2.10</version>
</dependency>

application.properties

# 使用RowBounds里面的offset作为页码使用
pagehelper.offset-as-page-num=true
# 页码小于零时
# pagehelper.reasonable=true
# PageSize为零时查找所有记录
pagehelper.page-size-zero=true
pagehelper.support-methods-arguments=true

常用的分页方式

@Mapper
public interface CoffeeMapper {
    @Select("select * from t_coffee order by id")
    // RowBounds方式的调用
    List<Coffee> findAllWithRowBounds(RowBounds rowBounds);

    @Select("select * from t_coffee order by id")
    // 参数方法调用
    List<Coffee> findAllWithParam(@Param("pageNum") int pageNum,
                                  @Param("pageSize") int pageSize);
}
@SpringBootApplication
@Slf4j
@MapperScan("geektime.spring.data.mybatisdemo.mapper")
public class MybatisDemoApplication implements ApplicationRunner {
	@Autowired
	private CoffeeMapper coffeeMapper;

	public static void main(String[] args) {
		SpringApplication.run(MybatisDemoApplication.class, args);
	}

	@Override
	public void run(ApplicationArguments args) throws Exception {
		coffeeMapper.findAllWithRowBounds(new RowBounds(1, 3))
				.forEach(c -> log.info("Page(1) Coffee {}", c));
		coffeeMapper.findAllWithRowBounds(new RowBounds(2, 3))
				.forEach(c -> log.info("Page(2) Coffee {}", c));

		log.info("==================================================================");

		coffeeMapper.findAllWithRowBounds(new RowBounds(1, 0))
				.forEach(c -> log.info("Page(1) Coffee {}", c));

		log.info("==================================================================");

		coffeeMapper.findAllWithParam(1, 3)
				.forEach(c -> log.info("Page(1) Coffee {}", c));

		log.info("==================================================================");

		List<Coffee> list = coffeeMapper.findAllWithParam(2, 3);
		PageInfo page = new PageInfo(list);
		log.info("PageInfo: {}", page);
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/66888.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何利用Java爬取网站数据?

1.Jsoup介绍 - 官网文档&#xff1a;https://jsoup.org - Jsoup 是一款Java 的HTML解析器&#xff0c;可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API&#xff0c;可通过DOM&#xff0c;CSS以及类似于jQuery的操作方法来取出和操作数据。 2. Jsoup快速入门…

TCP--三次握手和四次挥手

原文网址&#xff1a;TCP--三次握手和四次挥手_IT利刃出鞘的博客-CSDN博客 简介 本文介绍TCP的三次握手和四次挥手。即&#xff1a;TCP建立连接和断开连接的过程。 三次握手 流程图 主机 A为客户端&#xff0c;主机B为服务端。 第一次握手 A 发送同步报文段&#xff08;SYN…

小程序初始创建

1. 注册小程序账号 官网&#xff1a; https://mp.weixin.qq.com/wxopen/waregister?actionstep1 2. 下载小程序 官网&#xff1a; https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 百度网盘&#xff08;非最新版&#xff09; https://pan.baidu…

部署SNMP使网管与设备通信,配置关于TCP测试NQA的配置案例

一、部署SNMP 组网需求&#xff1a;通过部署RouterA由NMS网管设备管理用于监控网络是否畅通和业务是否正常。为了保证NMS和RouterA之间有一个数据传输安全、接入方式灵活、链路传输可靠的网络&#xff0c;并且可以实时监控设备的运行情况&#xff0c;网络中的RouterA通过网管实…

Databend 开源周报 #70

Databend 是一款强大的云数仓。专为弹性和高效设计&#xff0c;自由且开源。 即刻体验云服务&#xff1a;https://app.databend.com。 What’s New 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 Features & Improvements Format 更好地检查格…

PCB 二:AD 原理图绘制以及PCB绘制

PCB 二&#xff1a;AD 原理图绘制以及PCB绘制前言(一)资料总结(二)PCB前言 本文简单收集了AD软件在绘制PCB电路板的一些资料&#xff0c;还有遇到的一些问题&#xff0c;并记录一些常用的操作。 (一)资料总结 1【AD】Altium Designer 原理图的绘制 2【AD】Altium Designer P…

飞利浦CT的AI重建技术

原文&#xff1a;AI for significantly lower dose and improved image quality 飞利浦医疗CT的深度学习重建技术。 人工智能可显着降低剂量并提高图像质量概述背景飞利浦CT智能工作流Precise Image 如何训练神经网络深入了解深度学习训练神经网络验证神经网络推断法可以实现快…

Android 11及以上授予文件管理权限

背景 安卓11改变了此前安卓系统对于文件管理的规则,在安卓11上,文件读写变成了特殊权限。应用默认只能读写自己的目录/android/data/包名 gradle配置 Android11系统对应用写入权限做了严格的限制。本文介绍如何获取文件读写权限。 项目中 build.gradle 的targetSdkVersion …

YOLOv5 模型结构及代码详细讲解(一)

王旭*&#xff0c;沈啸彬 *, 张钊* (淮北师范大学计算机科学与技术学院&#xff0c;淮北师范大学经济与管理学院&#xff0c;安徽 淮北) *These authors contributed to the work equllly and should be regarded as co-first authors. &#x1f31e;欢迎来到深度学习的世界 …

window本地编译Spring源码并运行

1. Gradle 下载地址 https://services.gradle.org/distributions/ 2. Jdk下载地址&#xff1a; https://www.oracle.com/java/technologies/downloads/#java11-windows 3. Spring源码下载地址 https://github.com/spring-projects/spring-framework/tags 4. 注意事项 以…

微服务守护神-Sentinel-流控规则

引言 书接上篇 微服务守护神-Sentinel-概念 &#xff0c;上面介绍了Sentinel相关概念&#xff0c;本篇就来看下Sentinel的流控规则。 流控规则 流量控制&#xff0c;其原理是监控应用流量的QPS(每秒查询率) 或并发线程数等指标&#xff0c;当达到指定的阈值时 对流量进行控…

OSG开发-使用VisualStudio2019创建CMake项目方式开发HelloOSG

本文保证你使用VS2019&#xff0c;可以把这个OSG程序开发出来&#xff0c;看到那个蓝色的带有纹理的地球。 大概步骤如下&#xff1a; 下载已经编译好的OSG的库和头文件新建一个CMake项目编辑make.txt编写main.cpp运行。下载已经编译好的OSG的库和头文件 由于我们需要用OSG的…

blender 常用修改器

文章目录前置.阵列.倒角.布尔.精简.拆边.镜像.螺旋.实体化表面细分.三角化.体积到网格.焊接修改器.蒙皮.线框.铸型.曲线修改器.置换修改器.晶格修改器.缩裹修改器.简易修改.表面形变修改 .多级精度修改器.前置. 注意&#xff0c;修改器未应用前&#xff0c;只能操作原物体 阵…

自动驾驶:2022 apollo day 观后感(三)

自动驾驶&#xff1a;2022 apollo day 观后感&#xff08;三&#xff09;TOPIC THREE&#xff1a; 文心大模型在自动驾驶感知中的落地应用&#xff08;王井东&#xff09;多传感器融合autolabeling的发展&#xff1a;大模型数据闭环大模型&#xff0c;已经成为自动驾驶能力提升…

差动驱动机器人轨迹-CoCube

轨迹博客&#xff1a; 玫瑰线轨迹如何规划&#xff1f;&#xff08;desmosROS2turtlesim……&#xff09; ROS1云课→23turtlesim绘制小结&#xff08;数学和编程&#xff09; 如上所涉及的机器人假定模型都是差动驱动机器人。 许多移动机器人使用一种称为差动驱动的驱动机构…

vue项目图片裁剪上传——vue-cropper的使用,裁剪后上传头像

vue项目图片裁剪上传——vue-cropper的使用&#xff0c;裁剪后上传头像 npm地址&#xff1a;https://www.npmjs.com/package/vue-cropper github地址&#xff1a;https://github.com/xyxiao001/vue-cropper 在线demo&#xff1a;http://github.xyxiao.cn/vue-cropper/exampl…

学习分享:如何进行全局变量的学习

​对于很多朋友&#xff0c;尤其是刚接触全局变量的朋友而言&#xff0c;全局变量的学习对他们来说不是一件容易的事情。关于这方面的学习&#xff0c;很多朋友不太理解它的用法及分析方法&#xff0c;所以会比较乱&#xff0c;难以掌握。 什么是axure全局变量&#xff1f;全局…

【STM32】详解PWM的概念和原理

PWM的概念和原理一、PWM是什么&#xff1f;二、如何实现&#xff1f;三、STM32中的PWM四、使用库函数配置PWM将LED0设置为呼吸灯一、PWM是什么&#xff1f; PWM&#xff08;Pulse width modulation&#xff09;脉冲宽度调制。PWM是通过编程控制输出方波的频率和占空比&#xf…

oracle自启动的p***并行进程过多导致的process进程超限问题

某项目现场反馈无任何业务连接&#xff0c;查询v$process仍有500多个进程&#xff1b; 查询会话连接&#xff0c;也只有十几个会话&#xff1b; select b.MACHINE, b.PROGRAM,b.USERNAME, count(*) from v$process a, v$session b where a.ADDR b.PADDR and b.USERNAME is…

RCNN学习笔记-ResNeXt

论文地址&#xff1a;https://arxiv.org/pdf/1611.05431.pdf Abstract 我们提出了一种简单、高度模块化的图像分类网络体系结构。我们的网络是通过重复一个构建块来构建的&#xff0c;该构建块聚合了一组具有相同拓扑的变换。我们的简单设计产生了一个只有几个超参数可设置的…