学习Spring-Data-Jpa(六)---spring-data-commons中的repository
1、spring-data-commons项目
spring-data-commons项目是所有spring-data项目的核心,我们来看一下该项目下的repository包中的接口和注解。
2、Repository<T,ID>接口和@RepositoryDefinition注解
当中最重要的就是Repository接口了。它是做数据库操作的最底层的抽象接口、最顶级的父类,打开Repository接口看其源码,发现里面其实什么方法都没有,仅仅起到一个标识作用。捕获要管理的域类型和域类的id类型。用途是保存类型信息,并能够在类路径扫描期间发现继承该接口的接口,帮助我们创建代理类。
@Indexed
public interface Repository<T, ID> { }
@Indexed 我们发现在Repository接口上有一个@Indexed 注解,是Spring5提供的注解,用于提升应用启动性能。这个注解单独存在不起作用,要想使其生效的话,要添加spring-context-indexer依赖。在编译时会将@CompoentScan扫描指定package中要生成的bean写在METE-INF/spring.components文件中,当项目启动时,就会读取这个文件,不会再扫描指定的package了,从而提升性能。
我们只要继承Repository接口,并根据它的规则来命名接口方法,就可以进行数据库操作。(与继承Repository接口相等价的就是,在接口上添加@RepositoryDefinition注解)
示例:
/**
* 书籍持久层
* @author caofanqi
* 使用@RepositoryDefinition注解与继承Repository具有相同的效果
*/
//@RepositoryDefinition(domainClass = Book.class,idClass = Long.class)
public interface BookRepository extends Repository<Book,Long>{ /**
* 根据书名查找书籍
* @param bookName 书籍名称
* @return 该书籍名称的书列表
*/
List<Book> findBooksByBookNameContains(String bookName); }
@Transactional
@Rollback(false)
@SpringBootTest
class BookRepositoryTest { @Resource
private BookRepository bookRepository; @Test
void findBooksByBookNameContains() {
System.out.println("bookRepository : " + bookRepository.getClass().getName());
List<Book> books = bookRepository.findBooksByBookNameContains("Java");
System.out.println(books);
} }
这样就会根据我们传入的参数去like查询符合条件的书籍,生成的sql语句如下:
3、CrudRepository<T,ID>接口与注解@NoRepositoryBean
CrudRepository是Repository接口的子接口,提供了一套通用的CRUD方法。接口上的@NoRepositoryBean注解的意思是,不让Spring生成该类的代理类。
@NoRepositoryBean //不让Spring为该类生成代理类,仅仅提供一套通用的CRUD方法。
public interface CrudRepository<T, ID> extends Repository<T, ID> { /**
* 保存方法*/
<S extends T> S save(S entity); /**
* 保存*/
<S extends T> Iterable<S> saveAll(Iterable<S> entities); /**
* 根据id进行查询*/
Optional<T> findById(ID id); /**
* 判断给定id的数据是否存在*/
boolean existsById(ID id); /**
* 查询全部,数据量很大的时候,谨慎使用*/
Iterable<T> findAll(); /**
* 根据给定ids查询符合条件的数据
*/
Iterable<T> findAllById(Iterable<ID> ids); /**
* 统计条数*/
long count(); /**
* 根据id删除*/
void deleteById(ID id); /**
* 删除给定实体*/
void delete(T entity); /**
* 删除给定实体*/
void deleteAll(Iterable<? extends T> entities); /**
* 删除全部
*/
void deleteAll();
}
我们要想使用这些方法,只需自己的Repository继承该接口即可。
4、PagingAndSortingRepository<T,ID>接口
是CrudRepository的子接口,也不会生成代理,只是提供分页和排序的方法。
@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> { /**
* 根据sort去所有对象排序的集合*/
Iterable<T> findAll(Sort sort); /**
* 根据pageable进行分页,pageable中可以包含sort*/
Page<T> findAll(Pageable pageable);
}
分页相关对象:
Sort查询的排序选项,至少提供一个属性列表来进行排序,不能是null或空,默认是升序ASC;
可以通过Sort.by 来构建Sort对象:
public static Sort by(String... properties) 根据给定的属性列表进行升序排序;
public static Sort by(Direction direction, String... properties) 指定属性和排序方向,Direction.ASC(升序)、Direction.DESC(降序);
public static Sort by(Order... orders)/public static Sort by(List<Order> orders) 根据给定的一组order进行排序。
可以通过Sort.and()方法将多个sort组合再一起,通过Sort.ascending()/Sort.descending指定排序方向。
还可以通过Sort.sort(Class<T> type) 来构造类型安全的排序对象:
public static <T> TypedSort<T> sort(Class<T> type) 根据指定class类型构造该类型的typeSort排序对象;
通过TypedSort.by 方法来构建排序字段,通过ascending()/descending指定排序方向,使用and()方法进行连接。
Order,实现一个排序对,提供方向和属性,为sort提供输入。也就是说,可以针对每一个属性设置不同的升序或降序。
可以通过一下方式来构建Order对象:
public static Order by(String property) ,指定属性返回order对象,默认使用升序。
public static Order asc(String property),返回指定属性升序的order对象。
public static Order desc(String property),返回指定属性降序的order对象。
Pageable分页信息的抽象接口,实现类是PageRequest;
可以通过一下方式来构建Pageable对象:
public static PageRequest of(int page, int size),创建一个未排序的PageRequest,page从0开始;
public static PageRequest of(int page, int size, Direction direction, String... properties),创建一个根据给定方向和属性排序的分页对象。
public static PageRequest of(int page, int size, Sort sort),创建一个根据sort进行排序的分页对象。
Page,封装分页结果信息,可以通过如下方法获取分页信息。
page.getContent() ,分页查询结果列表;
page.getNumberOfElements(),当前分页结果列表中的元素个数;
page.getTotalElements(),当前条件下总条数;
page.getTotalPages(),总页数;
page.getNumber(),我们自己传的page;
page.getSize(),我们自己传入的size。
代码示例:
@Test
void testPagingAndSortingRepository(){ // Sort.Order id = Sort.Order.by("id");
// Sort.Order bookName = Sort.Order.desc("bookName");
// Sort sort = Sort.by(id,bookName); //等价于上面三句代码
// Sort sort = Sort.by("id").ascending().and(Sort.by("bookName").descending()); //使用类型安全的排序
Sort.TypedSort<Book> bookTypedSort = Sort.sort(Book.class);
Sort sort = bookTypedSort.by(Book::getId).ascending()
.and(bookTypedSort.by(Book::getBookName).descending()); Pageable pageable = PageRequest.of(2,2, sort); Page<Book> page = bookRepository.findAll(pageable); System.out.println("分页查询结果列表:" + page.getContent());
System.out.println("当前分页结果列表中的元素个数:" + page.getNumberOfElements());
System.out.println("当前条件下总条数:" + page.getTotalElements());
System.out.println("总页数:" +page.getTotalPages());
System.out.println("我们自己传的page:" +page.getNumber());
System.out.println("我们自己传入的size:" +page.getSize()); }
5、QueryByExampleExecutor<T>接口
该接口位于spring-data-commons项目的repository.query包中,允许通过实例来进行查询,可以通过该接口来进行简单的动态查询。使用的话,自己的repository继承QueryByExampleExecutor并指定域类型,就可以使用它提供的功能了。
public interface QueryByExampleExecutor<T> { /**
* 根据Example查找一个对象*/
<S extends T> Optional<S> findOne(Example<S> example); /**
* 根据Example查找一批对象*/
<S extends T> Iterable<S> findAll(Example<S> example); /**
* 根据Example查找一批对象,并排序*/
<S extends T> Iterable<S> findAll(Example<S> example, Sort sort); /**
* 根据Example查找一批对象,并分页排序*/
<S extends T> Page<S> findAll(Example<S> example, Pageable pageable); /**
* 根据Example查找,返回符合条件的对象个数*/
<S extends T> long count(Example<S> example); /**
* 判断符合给定Example的对象是否存在*/
<S extends T> boolean exists(Example<S> example);
}
ExampleMatcher源码分析:
package org.springframework.data.domain; import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults; import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function; import org.springframework.lang.Nullable;
import org.springframework.util.Assert; /**
* 用于示例查询(QBE)的属性路径匹配规范
*/
public interface ExampleMatcher { /**
* 创建一个新的匹配器,默认情况下,probe中所有的非空属性都匹配。即所有的属性条件用and连接。
*/
static ExampleMatcher matching() {
return matchingAll();
} /**
* 创建一个新的匹配器,probe中所有的非空属性匹配一个即可。即所有的属性条件用or连接。
*/
static ExampleMatcher matchingAny() {
return new TypedExampleMatcher().withMode(MatchMode.ANY);
} /**
*创建一个新的匹配器,probe中所有的非空属性都匹配。即所有的属性条件用and连接。
*/
static ExampleMatcher matchingAll() {
return new TypedExampleMatcher().withMode(MatchMode.ALL);
} /**
* 返回一个新的匹配器,忽略给定路径的匹配。属性传入这个方法,生成的条件中就不会包含该属性。一般用于基本数据类型。
*/
ExampleMatcher withIgnorePaths(String... ignoredPaths); /**
* 返回一个新的匹配器,设置默认的字符串的匹配规则。默认是StringMatcher.DEFAULT(=) 。
*
*/
ExampleMatcher withStringMatcher(StringMatcher defaultStringMatcher); /**
* 返回一个新的匹配器,忽略大小写匹配(还要看数据库是否支持大小写区分)。
*/
default ExampleMatcher withIgnoreCase() {
return withIgnoreCase(true);
} /**
* 返回一个新的匹配器,设置是否忽略大小写匹配(还要看数据库是否支持大小写区分)。
*/
ExampleMatcher withIgnoreCase(boolean defaultIgnoreCase); /**
* 返回一个新的匹配器,设置指定属性的匹配方式(使用lambda方式)。
*/
default ExampleMatcher withMatcher(String propertyPath, MatcherConfigurer<GenericPropertyMatcher> matcherConfigurer) { Assert.hasText(propertyPath, "PropertyPath must not be empty!");
Assert.notNull(matcherConfigurer, "MatcherConfigurer must not be empty!"); GenericPropertyMatcher genericPropertyMatcher = new GenericPropertyMatcher();
matcherConfigurer.configureMatcher(genericPropertyMatcher); return withMatcher(propertyPath, genericPropertyMatcher);
} /**
* 返回一个新的匹配器,设置指定属性的匹配方式(原始方式)。
*/
ExampleMatcher withMatcher(String propertyPath, GenericPropertyMatcher genericPropertyMatcher); /**
* 属性转换器
*/
ExampleMatcher withTransformer(String propertyPath, PropertyValueTransformer propertyValueTransformer); /**
* 返回一个新的匹配器,指定属性忽略大小写。
*/
ExampleMatcher withIgnoreCase(String... propertyPaths); /**
* 返回一个新的匹配器,将probe中的null属性也作为过滤条件。如:bookName is null。
*
*/
default ExampleMatcher withIncludeNullValues() {
return withNullHandler(NullHandler.INCLUDE);
} /**
* 返回一个新的匹配器,将probe中的null属性忽略,不作为过滤条件。
*/
default ExampleMatcher withIgnoreNullValues() {
return withNullHandler(NullHandler.IGNORE);
} /**
* 返回一个新的匹配器,设置null值处理器。
*/
ExampleMatcher withNullHandler(NullHandler nullHandler); /**
* 获得null处理器。
*/
NullHandler getNullHandler(); /**
* 获取默认的字符串匹配器。
*/
StringMatcher getDefaultStringMatcher(); /**
* 如果忽略字符串大小写,返回true。
*/
boolean isIgnoreCaseEnabled(); /**
* 判断是否是忽略属性。
*/
default boolean isIgnoredPath(String path) {
return getIgnoredPaths().contains(path);
} /**
* 获取忽略属性集合。
*/
Set<String> getIgnoredPaths(); /**
* 属性特定查询方式。
*/
PropertySpecifiers getPropertySpecifiers(); /**
* 是否是全匹配。
*/
default boolean isAllMatching() {
return getMatchMode().equals(MatchMode.ALL);
} /**
* 是否是任意匹配。
*/
default boolean isAnyMatching() {
return getMatchMode().equals(MatchMode.ANY);
} /**
* 获取匹配方式。
*/
MatchMode getMatchMode(); /**
* null处理器枚举。
*/
enum NullHandler { INCLUDE, IGNORE
} /**
* 回调配置匹配器。
*/
interface MatcherConfigurer<T> {
void configureMatcher(T matcher);
} /**
* 通用属性匹配。
*/
@EqualsAndHashCode
class GenericPropertyMatcher { @Nullable StringMatcher stringMatcher = null;
@Nullable Boolean ignoreCase = null;
PropertyValueTransformer valueTransformer = NoOpPropertyValueTransformer.INSTANCE; public GenericPropertyMatcher() {} /**
* 通过字符串匹配器,是否忽略大小写构建GenericPropertyMatcher。
*/
public static GenericPropertyMatcher of(StringMatcher stringMatcher, boolean ignoreCase) {
return new GenericPropertyMatcher().stringMatcher(stringMatcher).ignoreCase(ignoreCase);
} /**
*通过字符串匹配器构建GenericPropertyMatcher。
*/
public static GenericPropertyMatcher of(StringMatcher stringMatcher) {
return new GenericPropertyMatcher().stringMatcher(stringMatcher);
} /**
* 设置忽略大小写。
*/
public GenericPropertyMatcher ignoreCase() { this.ignoreCase = true;
return this;
} /**
* 设置是否忽略大小写。
*/
public GenericPropertyMatcher ignoreCase(boolean ignoreCase) { this.ignoreCase = ignoreCase;
return this;
} /**
* 设置区分大小写。
*/
public GenericPropertyMatcher caseSensitive() { this.ignoreCase = false;
return this;
} /**
* 包含给定属性值。
*/
public GenericPropertyMatcher contains() { this.stringMatcher = StringMatcher.CONTAINING;
return this;
} /**
* 以给定属性值结尾。
*/
public GenericPropertyMatcher endsWith() { this.stringMatcher = StringMatcher.ENDING;
return this;
} /**
* 以给定属性值开头。
*/
public GenericPropertyMatcher startsWith() { this.stringMatcher = StringMatcher.STARTING;
return this;
} /**
* 精确匹配。
*/
public GenericPropertyMatcher exact() { this.stringMatcher = StringMatcher.EXACT;
return this;
} /**
* 默认规则。
*/
public GenericPropertyMatcher storeDefaultMatching() { this.stringMatcher = StringMatcher.DEFAULT;
return this;
} /**
* 正则匹配。
*/
public GenericPropertyMatcher regex() { this.stringMatcher = StringMatcher.REGEX;
return this;
} /**
* 给定string匹配器。
*/
public GenericPropertyMatcher stringMatcher(StringMatcher stringMatcher) { Assert.notNull(stringMatcher, "StringMatcher must not be null!");
this.stringMatcher = stringMatcher;
return this;
} /**
* 设置属性转换器
*/
public GenericPropertyMatcher transform(PropertyValueTransformer propertyValueTransformer) { Assert.notNull(propertyValueTransformer, "PropertyValueTransformer must not be null!");
this.valueTransformer = propertyValueTransformer;
return this;
}
} /**
* 用于创建GenericPropertyMatcher。
*/
class GenericPropertyMatchers { /**
* 忽略大小写的。
*/
public static GenericPropertyMatcher ignoreCase() {
return new GenericPropertyMatcher().ignoreCase();
} /**
* 不忽略大小写的。
*/
public static GenericPropertyMatcher caseSensitive() {
return new GenericPropertyMatcher().caseSensitive();
} /**
* 包含。
*/
public static GenericPropertyMatcher contains() {
return new GenericPropertyMatcher().contains();
} /**
* 以结尾。
*/
public static GenericPropertyMatcher endsWith() {
return new GenericPropertyMatcher().endsWith(); } /**
* 以开始。
*/
public static GenericPropertyMatcher startsWith() {
return new GenericPropertyMatcher().startsWith();
} /**
* 精确匹配。
*/
public static GenericPropertyMatcher exact() {
return new GenericPropertyMatcher().exact();
} /**
* 默认方式。
*/
public static GenericPropertyMatcher storeDefaultMatching() {
return new GenericPropertyMatcher().storeDefaultMatching();
} /**
* 正则。
*/
public static GenericPropertyMatcher regex() {
return new GenericPropertyMatcher().regex();
}
} /**
* 字符串匹配模式。
*/
enum StringMatcher { /**
* 默认,效果同EXACT。
*/
DEFAULT,
/**
* 精确,相等。
*/
EXACT,
/**
* 开头匹配。
*/
STARTING,
/**
* 结尾匹配。
*/
ENDING,
/**
* 包含,模糊匹配。
*/
CONTAINING,
/**
* 正则匹配。
*/
REGEX;
} /**
* 属性转换器,一般不需要设置。
*/
interface PropertyValueTransformer extends Function<Optional<Object>, Optional<Object>> {} /**
*/
enum NoOpPropertyValueTransformer implements ExampleMatcher.PropertyValueTransformer { INSTANCE; @Override
@SuppressWarnings("null")
public Optional<Object> apply(Optional<Object> source) {
return source;
}
} /**
* 属性特定查询方式
*/
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@EqualsAndHashCode
class PropertySpecifier { String path;
@Nullable StringMatcher stringMatcher;
@Nullable Boolean ignoreCase;
PropertyValueTransformer valueTransformer; PropertySpecifier(String path) { Assert.hasText(path, "Path must not be null/empty!");
this.path = path; this.stringMatcher = null;
this.ignoreCase = null;
this.valueTransformer = NoOpPropertyValueTransformer.INSTANCE;
} public PropertySpecifier withStringMatcher(StringMatcher stringMatcher) { Assert.notNull(stringMatcher, "StringMatcher must not be null!");
return new PropertySpecifier(this.path, stringMatcher, this.ignoreCase, this.valueTransformer);
} public PropertySpecifier withIgnoreCase(boolean ignoreCase) {
return new PropertySpecifier(this.path, this.stringMatcher, ignoreCase, this.valueTransformer);
} public PropertySpecifier withValueTransformer(PropertyValueTransformer valueTransformer) { Assert.notNull(valueTransformer, "PropertyValueTransformer must not be null!");
return new PropertySpecifier(this.path, this.stringMatcher, this.ignoreCase, valueTransformer);
} public String getPath() {
return path;
} @Nullable
public StringMatcher getStringMatcher() {
return stringMatcher;
} @Nullable
public Boolean getIgnoreCase() {
return ignoreCase;
} public PropertyValueTransformer getPropertyValueTransformer() {
return valueTransformer == null ? NoOpPropertyValueTransformer.INSTANCE : valueTransformer;
} public Optional<Object> transformValue(Optional<Object> source) {
return getPropertyValueTransformer().apply(source);
}
} /**
* 特定属性查询方式集合
*/
@EqualsAndHashCode
class PropertySpecifiers { private final Map<String, PropertySpecifier> propertySpecifiers = new LinkedHashMap<>(); PropertySpecifiers() {} PropertySpecifiers(PropertySpecifiers propertySpecifiers) {
this.propertySpecifiers.putAll(propertySpecifiers.propertySpecifiers);
} public void add(PropertySpecifier specifier) { Assert.notNull(specifier, "PropertySpecifier must not be null!");
propertySpecifiers.put(specifier.getPath(), specifier);
} public boolean hasSpecifierForPath(String path) {
return propertySpecifiers.containsKey(path);
} public PropertySpecifier getForPath(String path) {
return propertySpecifiers.get(path);
} public boolean hasValues() {
return !propertySpecifiers.isEmpty();
} public Collection<PropertySpecifier> getSpecifiers() {
return propertySpecifiers.values();
}
} /**
* 匹配方式。
*/
enum MatchMode {
ALL, ANY;
}
}
示例代码:
@Test
void testQueryByExampleExecutor(){ Book book = Book.builder().bookName("java").publishDate(LocalDate.of(2019,11,11)).id(1L).build(); ExampleMatcher matcher = ExampleMatcher.matching()
.withIgnorePaths("id") //忽略id属性,不管id有没有值,都不作为查询条件。
.withIgnoreNullValues() //忽略属性为null的,不作为查询条件。
.withMatcher("bookName",m -> m.startsWith().ignoreCase()) //设置bookName属性,前包含,忽略大小写。
.withTransformer("publishDate",value -> Optional.of(LocalDate.of(2019,11,12))); //转换属性值 Example<Book> example = Example.of(book,matcher); List<Book> books = bookRepository.findAll(example); }
生成的sql语句:
Spring-Data-Jpa官网的字符串匹配举例
QueryByExampleExecutor最佳实践:
首先要判断是否需要我们自己构建匹配器,如果默认匹配器,可以完成,我们就不需要创建。
判断null值是否要作为条件,一般都是忽略的,如果null值作为条件,将不想作为条件的null属性添加到忽略列表。
基本类型是有默认值的,如果不作为条件,要加入到忽略列表。
不同的字符串属性,如果需要不同的匹配方式,进行单独设置。
不是特别复杂的动态查询,使用QBE,还是很方便的。
源码地址:https://github.com/caofanqi/study-spring-data-jpa
学习Spring-Data-Jpa(六)---spring-data-commons中的repository的更多相关文章
- 使用Spring Data JPA的Spring Boot
本文教你开始使用Spring Data JPA.来自优锐课JAVA架构专业讲师精心整理. 欢迎使用带有Spring Data JPA的Spring Boot教程!在本教程中,我们将看到Spring D ...
- Spring Data JPA简介 Spring Data JPA特点
Spring Data JPA 是Spring基于ORM框架.JPA规范的基础上封装的一套JPA 应用框架,底层使用了Hibernate 的JPA技术实现,可使开发者用极简的代码即可实现对数据的访问和 ...
- Spring Data JPA 整合Spring
1.1 Spring Data JPA 与 JPA和hibernate之间的关系 JPA是一套规范,内部是有接口和抽象类组成的.hibernate是一套成熟的ORM框架,而且Hibernate实现 ...
- 整合Spring Data JPA与Spring MVC: 分页和排序
之前我们学习了如何使用Jpa访问关系型数据库.比较完整Spring MVC和JPA教程请见Spring Data JPA实战入门,Spring MVC实战入门. 通过Jpa大大简化了我们对数据库的开发 ...
- 整合Spring Data JPA与Spring MVC: 分页和排序pageable
https://www.tianmaying.com/tutorial/spring-jpa-page-sort Spring Data Jpa对于分页以及排序的查询也有着完美的支持,接下来,我们来学 ...
- Spring Data JPA在Spring Boot中的应用
1.JPA JPA(Java Persistence API)是Sun官方提出的Java持久化规范.它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据.他的出现主要是为了简 ...
- <Spring Data JPA>二 Spring Data Jpa
1.pom依赖 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="htt ...
- spring data jpa和spring data redis同时配置时,出现Multiple Spring Data modules found, entering strict repository configuration mode错误
问题说明 data jpa和data redis同时配置时,出现Spring modules spring Spring Data Release Train <dependencyManage ...
- Spring Data JPA 整合Spring 第二篇
主要是在CustomerDao中去写一些代码,在调用Query中去用SQL 例如 public interface CustomerDao extends JpaRepository<Custo ...
- Egret入门学习日记 --- 第十六篇(书中 6.10~7.3节 内容)
第十六篇(书中 6.10~7.3节 内容) 昨天搞定了6.9节,今天就从6.10节开始. 其实这个蛮简单的. 这是程序员模式. 这是设计师模式. 至此,6.10节 完毕. 开始 6.11节. 有点没营 ...
随机推荐
- Linux 环境安装 Node、nginx、docker、vsftpd、gitlab
Linux 环境安装 centos7 # 更新yum yum update -y 0. 防火墙 firewalld 新入的JD云服务器,发现防火墙默认是关闭的. # 查看防火墙状态 systemctl ...
- SpringBoot项目启动不走内嵌容器
一.问题 springboot项目java -jar启动不走内嵌容器,如下图,可以看到是直接走系统环境变量里配置的tomcat容器了 二.分析 我的pom.xml文件关键依赖: <depende ...
- Elasticsearch-6.7.0系列(八)开启kibana监控
修改ES配置: 修改elasticsearch.yml,添加如下xpack配置: xpack.security.enabled: true xpack.ml.enabled: true xpack.l ...
- C#中的Process类使用
有时候,用户在进行某个操作时,程序运行到某个阶段突然崩溃,我们需要让程序自动重启,重新执行用户之前的操作,当发现主程序还存在,就重启用户执行的附加程序.这时候Process就派上了用场. 控制台测试用 ...
- 支付宝支付回调方法RSA2验签失败处理方法
支付宝支付签名方式RSA2生成支付时使用的是支付宝公钥和应用私钥, 而不是应用公钥,支付宝公钥的生成是根据上传应用公钥而变动的, 所以在做回调的时候参数ALIPAY_PUBLIC_KEY也需要传支付宝 ...
- ConsoleLoggerExtensions.AddConsole(ILoggerFactory)已过时代码修复
0x00.问题 netcoreapp2.2环境下, Startup.cs 代码配置如下 public void Configure(IApplicationBuilder app, IHostingE ...
- NIO开发Http服务器(3):核心配置和Request封装
最近学习了Java NIO技术,觉得不能再去写一些Hello World的学习demo了,而且也不想再像学习IO时那样编写一个控制台(或者带界面)聊天室.我们是做WEB开发的,整天围着tomcat.n ...
- 并发编程之Callable异步,Future模式
Callable 在Java中,创建线程一般有两种方式,一种是继承Thread类,一种是实现Runnable接口.然而,这两种方式的缺点是在线程任务执行结束后,无法获取执行结果.我们一般只能采用共享变 ...
- datagridview控件 索引-1没有值
很多WINFORM的开发人员在DataGridView的开发当中,都会出现“索引-1没有值”这个烦人的问题,其实较早之前,我已经大概知道问题的所在,也找到了解决方法,不过一直没有时间去深入研究一下,今 ...
- 第五周(web,machine learning笔记)
2019/11/2 1. 表现层状态转换(REST, representational state transfer.)一种万维网软件架构风格,目的是便于不同软件/程序在网络(例如互联网)中互相 ...