Java提升营

这篇文章主要讲解TransactionalEventListener是怎样工作的?适合在什么场景,能解决哪些问题?以及和EventListener不同之处。

示例

这里举个业务场景,假如我们有个需求,用户创建成功后给用户发送一个邮件。这里有两个事情要做:

  1. 创建用户
  2. 给用户发送邮件
阅读全文 »

在平时的工作中,我们经常接触到数据库表用户以及角色的使用,由于经常使用默认的数据库表空间模式(Schema),所以我们往往忽略了数据库表空间和模式的概念以及作用。

接下来,先介绍一下模式和表空间的定义以及作用。

什么是Schema?

一个数据库包含一个或多个已命名的模式,模式又包含表。模式还可以包含其它对象, 包括数据类型函数操作符等。同一个对象名可以在不同的模式里使用而不会导致冲突; 比如,herschemamyschema都可以包含一个名为mytable的表。 和数据库不同,模式不是严格分离的:只要有权限,一个用户可以访问他所连接的数据库中的任意模式中的对象。

阅读全文 »

如果我们使用的是PostgreSQL数据库,那么我们可以使用LIKE和ILIKE做模糊查询,LIKE语法是SQL标准而ILIKE是PostgreSQL的一个扩展。

构造数据

先创建一张表,然后插入一些数据;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
create table test(
id serial,
name varchar(16)
);

my_test_db=#select * from test;
id | name
----+---------
1 | One
2 | Two
3 | One_Two
4 | One/Two
5 | oNE
6 | 99%
(6 rows)
阅读全文 »

Redis 6.0.0稳定版(GA)终于发布了。这个版本提供了很多令人振奋的新特性和功能改进,比如新的网络协议RESP3、新的集群代理、ACL等,其中最受关注的应该是 多线程,带着诸多疑问,让我们一起开始《Redis 6.0新特性》。

Redis 6.0之前的版本真的是单线程吗?

Redis在处理客户端请求时,包括 获取(socket读)、解析、执行、内容返回(socket写),都是由一个顺序的串行主线程来处理的,这就是所谓的 单线程。但如果严格来说,从Redis 4.0开始就不是单线程了,除了主线程外,它还有后台线程,这些线程在处理一些比较慢的操作,比如清理脏数据、释放无用连接、删除大量keys等。

阅读全文 »

在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。

概念

守护线程,是指程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

特性

  • 所有用户线程完成执行时,守护线程无法阻止JVM的退出
  • 当所有用户线程完成执行时,JVM将退出
  • 如果JVM发现有正在运行的守护线程,它将结束这些线程然后退出。JVM不在乎守护线程是否正在运行。

将线程转换为守护线程可以通过调用Thread对象的**setDaemon(true)**方法来实现。在使用守护线程时需要注意以下几点:

  1. thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

  2. Daemon线程中产生的新线程也是Daemon的。

  3. 守护线程应该永远不要去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

    阅读全文 »

Java 8中引入了新的Date-Time API,解决了旧的日期时间API的以下缺点:

  • 线程不安全问题:旧的 java.util.Date 是线程不安全的,而 新的 Date-Time API 是不可变的且没有提供setter方法
  • 操作单调:旧API仅提供了少量的方法去操作时间,而新的API提供了许多日期操作

Java 8在java.time包下引入了新的Date-Time API,其中最重要的类是:

  • Local :简化版的Date-Time API,没有时区处理的复杂性。
  • Zoned :专门用于处理各种时区的Date-Time API。
    阅读全文 »

在介绍Spring整合Mybatis原理之前,我们得先来稍微介绍Mybatis的工作原理。

Mybatis的基本工作原理

在Mybatis中,我们可以使用一个接口去定义要执行sql,简化代码如下:

定义一个接口,@Select表示要执行查询sql语句。

1
2
3
4
public interface UserMapper {
@Select("select * from user where id = #{id}")
User selectById(Integer id);
}

以下为执行sql代码:

1
2
3
4
5
6
7
InputStream inputStream = Resources.getResourceAsStream("mybatis.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
// 以下使我们需要关注的重点
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Integer id = 1;
User user = mapper.selectById(id);
阅读全文 »

Stream概念

Stream(流)是一个来自数据源的元素队列并支持聚合操作

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流,流在管道中传输,并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

Java Stream的特性

  • Stream不是一种数据结构,而是一个来自数据源的元素队列并支持聚合操作
  • Stream不会改变来源的数据结构
  • 每个中间操作都是延迟执行的,并返回一个流,元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果
阅读全文 »

双冒号(::)操作,也被称为方法引用运算符,用于直接调用指定类的方法。它的行为与la​​mbda表达式完全相同。它与lambda表达式的唯一区别在于,它使用名称直接引用方法,而不是提供方法的委托。

语法:

1
<Class name>::<method name>

示例:打印Stream的所有元素:

  • 使用Lambda表达式:
1
stream.forEach(s-> System.out.println(s));
阅读全文 »

Default Methods

在Java 8之前,接口只能定义抽象方法。这些方法的实现必须在单独的类中提供。因此,如果要在接口中添加新方法,则必须在实现接口的类中提供其实现代码。为了克服此问题,Java 8引入了默认方法的概念,允许接口定义具有实现体的方法,而不会影响实现接口的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// A simple program to Test Interface default 
// methods in java
interface TestInterface
{
// abstract method
public void square(int a);

// default method
default void show()
{
System.out.println("Default Method Executed");
}
}

class TestClass implements TestInterface
{
// implementation of square abstract method
public void square(int a)
{
System.out.println(a*a);
}

public static void main(String args[])
{
TestClass d = new TestClass();
d.square(4);

// default method executed
d.show();
}
}

阅读全文 »