编程大杂烩(一)

编程大杂烩(一)

1.SQL之case when用法

参考链接:https://blog.csdn.net/weixin_44688973/article/details/118808601?spm=1001.2014.3001.5506

Case具有两种格式:简单Case函数和Case搜索函数。

一:简单Case函数

case sex
    when '1' then '男'
    when '2' then '女'
    else '其他' end

二:Case搜索函数

case 
    when sex = '1' then '男' 
    when sex = '2' then '女' 
    else '其他' end

这两种方式,可以实现相同的功能。简单Case函数的写法相对比较简洁,但是和Case搜索函数相比,功能方面会有些限制,比如写判断式。还有一个需要注意的问题,Case函数只返回第一个符合条件的值,剩下的Case部分将会被自动忽略。

  • 比如说,下面这段sql,你永远无法得到“第二类”这个结果
case 
    when col_1 in ( 'a', 'b') then '第一类' 
    when col_1 in ('a')  then '第二类' 
    else'其他' end

THEN后边的值与ELSE后边的值类型应一致,否则会报错。如下:

CASE SCORE WHEN 'A' THEN '优' ELSE 0 END

'优’和0数据类型不一致则报错:

下面介绍几种常用场景。

场景1:有分数score,score<60返回不及格,score>=60返回及格,score>=80返回优秀

SELECT
    STUDENT_NAME,
    (CASE WHEN score < 60 THEN '不及格'
        WHEN score >= 60 AND score < 80 THEN '及格'
        WHEN score >= 80 THEN '优秀'
        ELSE '异常' END) AS REMARK
FROM
    TABLE

注意:如果你想判断score是否null的情况,WHEN score = null THEN ‘缺席考试’,这是一种错误的写法,正确的写法应为:

CASE WHEN score IS NULL THEN '缺席考试' ELSE '正常' END

场景2:现老师要统计班中,有多少男同学,多少女同学,并统计男同学中有几人及格,女同学中有几人及格,要求用一个SQL输出结果。

表结构如下:其中STU_SEX字段,0表示男生,1表示女生。

STU_CODE STU_NAME STU_SEX STU_SCORE
XM 小明 0 88
XL 小磊 0 55
XF 小峰 0 45
XH 小红 1 66
XN 晓妮 1 77
XY 小伊 1 99
SELECT 
	SUM (CASE WHEN STU_SEX = 0 THEN 1 ELSE 0 END) AS MALE_COUNT,
	SUM (CASE WHEN STU_SEX = 1 THEN 1 ELSE 0 END) AS FEMALE_COUNT,
	SUM (CASE WHEN STU_SCORE >= 60 AND STU_SEX = 0 THEN 1 ELSE 0 END) AS MALE_PASS,
	SUM (CASE WHEN STU_SCORE >= 60 AND STU_SEX = 1 THEN 1 ELSE 0 END) AS FEMALE_PASS
FROM 
	THTF_STUDENTS

输出结果如下:

MALE_COUNT FEMALE_COUNT MALE_PASS FEMALE_PASS
3 3 1 3

2.SQL之coalesce函数用法

参考链接:https://blog.csdn.net/yilulvxing/article/details/86595725?spm=1001.2014.3001.5506

  • coalesce用途:

(1):将控制替换成其他值;

(2):返回第一个非空值

  • 表达式

COALESCE是一个函数, (expression_1, expression_2, …,expression_n)依次参考各参数表达式,遇到非null值即停止并返回该值。如果所有的表达式都是空值,最终将返回一个空值。使用COALESCE在于大部分包含空值的表达式最终将返回空值。

  • SQL实例
select coalesce(success_cnt, 1) from tableA

当success_cnt 为null值的时候,将返回1,否则将返回success_cnt的真实值。

select coalesce(success_cnt, period, 1) from tableA

当success_cnt不为null,那么无论period是否为null,都将返回success_cnt的真实值(因为success_cnt是第一个参数),当success_cnt为null,而period不为null的时候,返回period的真实值。只有当success_cnt和period均为null的时候,将返回1。

3.使用DBeaver导入导出数据库的数据

导出为CSV文件

1.在DBeaver中右键表,选择导出数据

编程大杂烩(一)_第1张图片

2.之后选择导出为CSV文件

编程大杂烩(一)_第2张图片

3.之后,默认,点击下一步

编程大杂烩(一)_第3张图片

4.之后自定义导出目录,导出文件名称等信息,点击下一步

编程大杂烩(一)_第4张图片

5.确认,点击完成即可

编程大杂烩(一)_第5张图片

导入CSV数据

1.在DBeaver中选择表,右键导入数据

编程大杂烩(一)_第6张图片

2.选择CSV,下一步即可

编程大杂烩(一)_第7张图片

3.之后选择你要导入的CSV文件,点击下一步

编程大杂烩(一)_第8张图片

4.数据导入预览,此处需要注意数据与列名是否对应,同时CSV第一行是列名,不会进行导入

要导入的CSV文件:

编程大杂烩(一)_第9张图片

编程大杂烩(一)_第10张图片

5.点击下一步等待导入成功即可

4.ThreadPoolTaskExecutor和ThreadPoolExecutor区别

参考链接:https://blog.csdn.net/weixin_43168010/article/details/97613895?spm=1001.2014.3001.5506

ThreadPoolExecutor

这个类是JDK中的线程池类,继承自Executor, Executor 顾名思义是专门用来处理多线程相关的一个接口,所有线程相关的类都实现了这个接口,里面有一个execute()方法,用来执行线程,线程池主要提供一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁的额外开销,提高了响应的速度。相关的继承实现类图如下。

编程大杂烩(一)_第11张图片

一、线程池接口:ExecutorService为线程池接口,提供了线程池生命周期方法,继承自Executor接口,ThreadPoolExecutor为线程池实现类,提供了线程池的维护操作等相关方法,继承自AbstractExecutorService,AbstractExecutorService实现了ExecutorService接口。

二、线程池的体系结构:

java.util.concurrent.Executor 负责线程的使用和调度的根接口

​ |–ExecutorService 子接口: 线程池的主要接口

​ |–ThreadPoolExecutor 线程池的实现类

​ |–ScheduledExceutorService 子接口: 负责线程的调度

​ |–ScheduledThreadPoolExecutor : 继承ThreadPoolExecutor,实现了ScheduledExecutorService

三、工具类 : Executors

Executors为线程迟工具类,相当于一个工厂类,用来创建合适的线程池,返回ExecutorService类型的线程池。有如下方法。

  • ExecutorService newFixedThreadPool() : 创建固定大小的线程池
  • ExecutorService newCachedThreadPool() : 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
  • ExecutorService newSingleThreadExecutor() : 创建单个线程池。 线程池中只有一个线程
  • ScheduledExecutorService newScheduledThreadPool() : 创建固定大小的线程,可以延迟或定时的执行任务

其中AbstractExecutorService是他的抽象父类,继承自ExecutorService,ExecutorService 接口扩展Executor接口,增加了生命周期方法。

ThreadPoolTaskExecutor

这个类则是spring包下的,是sring为我们提供的线程池类,这里重点讲解这个类的用法,可以使用基于xml配置的方式创建


<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
    
    <property name="corePoolSize" value="10"/>
    
    <property name="maxPoolSize" value="200"/>
    
    <property name="queueCapacity" value="10"/>
    
    <property name="keepAliveSeconds" value="20"/>
    
    <property name="rejectedExecutionHandler">
        <bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy"/>
    property>
bean>

然后通过自动注入的方式注入线程池,

@Resource(name="taskExecutor")
ThreadPoolTaskExecutor taskExecutor;

// 或者可以直接@Autowried
@AutoWired
ThreadPoolTaskExecutor taskExecutor

或者是通过配置类的方式配置线程池,然后注入。

@Configuration
public class ExecturConfig {
    @Bean("taskExector")
    public Executor taskExector() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        int i = Runtime.getRuntime().availableProcessors();//获取到服务器的cpu内核
        executor.setCorePoolSize(5);//核心池大小
        executor.setMaxPoolSize(100);//最大线程数
        executor.setQueueCapacity(1000);//队列程度
        executor.setKeepAliveSeconds(1000);//线程空闲时间
        executor.setThreadNamePrefix("tsak-asyn");//线程前缀名称
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());//配置拒绝策略
        return executor;
    }
}    

上面注解中已经注释了参数的详解,这里重点讲解一下spring线程池的拒绝策略和处理流程。

  • 拒绝策略

rejectedExectutionHandler参数字段用于配置绝策略,常用拒绝策略如下

AbortPolicy:用于被拒绝任务的处理程序,它将抛出RejectedExecutionException

CallerRunsPolicy:用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。

DiscardOldestPolicy:用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。

DiscardPolicy:用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。

  • 处理流程

1.查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第二步。

2.查看任务队列是否已满,不满就将任务存储在任务队列中,否则执行第三步。

3.查看线程池是否已满,即就是是否达到最大线程池数,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。

  • 流程图如下

编程大杂烩(一)_第12张图片

5.SecurityContextHolder多线程无法获取登录信息的原因

在 Spring Security 中,我就想从子线程获取用户登录信息,怎么办?

大家知道在 Spring Security 中想要获取登录用户信息,不能在子线程中获取,只能在当前线程中获取,其中一个重要的原因就是 SecurityContextHolder 默认将用户信息保存在 ThreadLocal 中。

但是实际上 SecurityContextHolder 一共定义了三种存储策略:

public class SecurityContextHolder {
 public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
 public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
 public static final String MODE_GLOBAL = "MODE_GLOBAL";
    ...
    ...
}

第二种存储策略 MODE_INHERITABLETHREADLOCAL 就支持在子线程中获取当前登录用户信息,而 MODE_INHERITABLETHREADLOCAL 的底层使用的就是 InheritableThreadLocal,那么 InheritableThreadLocal 和 ThreadLocal 有什么区别呢?为什么它就可以支持从子线程中获取数据呢?

1.抛出问题

先来看一个大家可能都见过的例子:

@Test
void contextLoads() {
    ThreadLocal threadLocal = new ThreadLocal();
    threadLocal.set("javaboy");
    System.out.println("threadLocal.get() = " + threadLocal.get());
    new Thread(new Runnable() {
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            System.out.println("name+threadLocal.get() = " + name + ":" + threadLocal.get());
        }
    }).start();
}

这段代码的打印结果,相信大家都很清楚:

threadLocal.get() = javaboy
name+threadLocal.get() = Thread-121:null

数据在哪个线程存储,就要从哪个线程读取,子线程是读取不到的。如果我们把上面案例中的 ThreadLocal 修改为 InheritableThreadLocal,如下:

@Test
void contextLoads() {
    ThreadLocal threadLocal = new InheritableThreadLocal();
    threadLocal.set("javaboy");
    System.out.println("threadLocal.get() = " + threadLocal.get());
    new Thread(new Runnable() {
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            System.out.println("name+threadLocal.get() = " + name + ":" + threadLocal.get());
        }
    }).start();
}

此时的运行结果就会发生变化,如下:

threadLocal.get() = javaboy
name+threadLocal.get() = Thread-121:javaboy

可以看到,如果使用了 InheritableThreadLocal,即使在子线程中也能获取到父线程 ThreadLocal 中的数据。

那么这是怎么回事呢?我们一起来分析一下。

2.ThreadLocal

我们先来分析一下 ThreadLocal。

不看源码,仅从使用的角度来分析 ThreadLocal,大家会发现一个 ThreadLocal 只能存储一个对象,如果你需要存储多个对象,就需要多个 ThreadLocal 。

我们通过 ThreadLocal 源码来分析下。

当我们想要去调用 set 方法存储一个对象时,如下:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

大家可以看到,存储的时候会首先获取到一个 ThreadLocalMap 对象,获取的时候需要传入当前线程,看到这里大家可能就猜出来几分了,数据存储在一个类似于 Map 的 ThreadLocalMap 中,ThreadLocalMap 又和线程关联起来,怪不得每个线程只能获取到自己的数据。接下来我们来验证一下,继续看 getMap 方法:

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

getMap 方法返回的是一个 threadLocals 变量,也就是说,数据是存在 threadLocals 中的。threadLocals 则就是一个 ThreadLocalMap。数据存入 ThreadLocalMap 实际上是保存在一个 Entry 数组中。在同一个线程中,一个 ThreadLocal 只能保存一个对象,如果需要保存多个对象,就需要多个 ThreadLocal,同一个线程中的多个 ThreadLocal 最终所保存的变量实际上在同一个 ThreadLocalMap 即同一个 Entry 数组之中。不同线程的 ThreadLocal 所保存的变量在不同的 Entry 数组中。Entry 数组中的 key 实际上就是 ThreadLocal 对象,value 则是 set 进来的数据。

我们再来看下数据读取:

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

首先根据当前线程获取到对应的 ThreadLocalMap,再传入当前对象获取到 Entry,然后将 Entry 对象中的 value 返回即可。有人可能会问,Entry 不是一个数组吗?为什么不传入一个数组下标去获取 Entry ,而是通过当前 ThreadLocal 对象去获取 Entry 呢?其实在 getEntry 方法中,就是根据当前对象计算出数组下标,然后将获取到的 Entry 返回。

3.InheritableThreadLocal

InheritableThreadLocal 实际上是 ThreadLocal 的子类,我们来看下 InheritableThreadLocal 的定义:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    protected T childValue(T parentValue) {
        return parentValue;
    }
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

可以看到,主要就是重写了三个方法。getMap 方法的返回值变成了 inheritableThreadLocals 对象,createMap 方法中,构建出来的 inheritableThreadLocals 还依然是 ThreadLocalMap 的对象。和 ThreadLocal 相比,主要是保存数据的对象从 threadLocals 变为 inheritableThreadLocals。

这样的变化,对于前面的我们所说的 ThreadLocal 中的 get/set 并不影响,也就是 ThreadLocal 的特性依然不变。

变化发生在线程的初始化方法里,我们来看一下 Thread#init 方法:

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    ...
    ...
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    ...
    ...
}

可以看到,在创建子线程的时候,如果父线程存在 inheritableThreadLocals 变量且不为空,就调用 ThreadLocal.createInheritedMap 方法为子线程的 inheritableThreadLocals 变量赋值。ThreadLocal.createInheritedMap 方法所做的事情,其实就是将父线程的 inheritableThreadLocals 变量值赋值给子线程的 inheritableThreadLocals 变量。因此,在子线程中就可以访问到父线程 ThreadLocal 中的数据了。

需要注意的是,这种复制不是实时同步,有一个时间节点。在子线程创建的一瞬间,会将父线程 inheritableThreadLocals 变量的值赋值给子线程,一旦子线程创建成功了,如果用户再次去修改了父线程 inheritableThreadLocals 变量的值(即修改了父线程 ThreadLocal 中的数据),此时子线程是感知不到这个变化的。

好啦,经过上面的介绍相信大家就搞清楚 ThreadLocal 和 InheritableThreadLocal 的区别了。

4.SpringSecurity

先来看一段代码:

@GetMapping("/user")
public void userInfo() {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    String name = authentication.getName();
    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
    System.out.println("name = " + name);
    System.out.println("authorities = " + authorities);
    new Thread(new Runnable() {
        @Override
        public void run() {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            String name = authentication.getName();
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName + ":name = " + name);
            System.out.println(threadName + ":authorities = " + authorities);
        }
    }).start();
}

默认情况下,子线程中方法是无法获取到登录用户信息的。因为 SecurityContextHolder 中的数据保存在 ThreadLocal 中。

SecurityContextHolder 中通过 System.getProperty 来获取默认的数据存储策略,所以我们可以在项目启动时通过修改系统变量进而修改 SecurityContextHolder 的默认数据存储策略:

-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL

修改完成后,再次启动项目,就可以在子线程中获取到登录用户数据了,至于原理,就是前面所讲的。

6.线程池中各个参数如何合理设置

————————————————
版权声明:本文为CSDN博主「老周聊架构」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/riemann_/article/details/104704197

一、前言

在开发过程中,好多场景要用到线程池。每次都是自己根据业务场景来设置线程池中的各个参数。这两天又有需求碰到了,索性总结一下方便以后再遇到可以直接看着用。虽说根据业务场景来设置各个参数的值,但有些万变不离其宗,掌握它的原理对如何用好线程池起了至关重要的作用。那我们接下来就来进行线程池的分析。

二、ThreadPoolExecutor的重要参数

我们先来看下ThreadPoolExecutor的带的那些重要参数的构造器。

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    ...
}

1、corePoolSize: 核心线程数

这个应该是最重要的参数了,所以如何合理的设置它十分重要。

  • 核心线程会一直存活,及时没有任务需要执行。
  • 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理。
  • 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。

如何设置好的前提我们要很清楚的知道CPU密集型和IO密集型的区别。

(1)、CPU密集型

CPU密集型也叫计算密集型,指的是系统的硬盘、内存性能相对CPU要好很多,此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就可以完成,而CPU还有许多运算要处理,CPU Loading 很高。

在多重程序系统中,大部分时间用来做计算、逻辑判断等CPU动作的程序称之CPU bound。例如一个计算圆周率至小数点一千位以下的程序,在执行的过程当中绝大部分时间用在三角函数和开根号的计算,便是属于CPU bound的程序。

CPU bound的程序一般而言CPU占用率相当高。这可能是因为任务本身不太需要访问I/O设备,也可能是因为程序是多线程实现因此屏蔽掉了等待I/O的时间。

(2)、IO密集型

IO密集型指的是系统的CPU性能相对硬盘、内存要好很多,此时,系统运作,大部分的状况是CPU在等I/O (硬盘/内存) 的读/写操作,此时CPU Loading并不高。

I/O bound的程序一般在达到性能极限时,CPU占用率仍然较低。这可能是因为任务本身需要大量I/O操作,而pipeline做得不是很好,没有充分利用处理器能力。

好了,了解完了以后我们就开搞了。

(3)、先看下机器的CPU核数,然后在设定具体参数:

自己测一下自己机器的核数

System.out.println(Runtime.getRuntime().availableProcessors());

即CPU核数 = Runtime.getRuntime().availableProcessors()

(4)、分析下线程池处理的程序是CPU密集型还是IO密集型

  • CPU密集型:corePoolSize = CPU核数 + 1

  • IO密集型:corePoolSize = CPU核数 * 2

2、maximumPoolSize:最大线程数

  • 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务。

  • 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常。

3、keepAliveTime:线程空闲时间

  • 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize。

  • 如果allowCoreThreadTimeout=true,则会直到线程数量=0。

4、queueCapacity:任务队列容量(阻塞队列)

  • 当核心线程数达到最大时,新任务会放在队列中排队等待执行

5、allowCoreThreadTimeout:允许核心线程超时

6、rejectedExecutionHandler:任务拒绝处理器

两种情况会拒绝处理任务:

  • 当线程数已经达到maxPoolSize,且队列已满,会拒绝新任务。

  • 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务。

线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常。

ThreadPoolExecutor 采用了策略的设计模式来处理拒绝任务的几种场景。这几种策略模式都实现了RejectedExecutionHandler 接口。

  • AbortPolicy 丢弃任务,抛运行时异常。

  • CallerRunsPolicy 由调用者(一般为main线程)执行任务。

  • DiscardPolicy 忽视,什么都不会发生。

  • DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务。

三、如何设置参数

默认值:

corePoolSize = 1

maxPoolSize = Integer.MAX_VALUE

queueCapacity = Integer.MAX_VALUE

keepAliveTime = 60s

allowCoreThreadTimeout = false

rejectedExecutionHandler = AbortPolicy()

如何来设置呢?

需要根据几个值来决定

  • tasks :每秒的任务数,假设为500~1000

  • taskcost:每个任务花费时间,假设为0.1s

  • responsetime:系统允许容忍的最大响应时间,假设为1s

做几个计算

  • corePoolSize = 每秒需要多少个线程处理?

  • threadcount = tasks/(1/taskcost) = tasks*taskcout = (500 ~ 1000)*0.1 = 50~100 个线程。

  • corePoolSize设置应该大于50。

根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可。

queueCapacity = (coreSizePool/taskcost)*responsetime

计算可得 queueCapacity = 80/0.1*1 = 800。意思是队列里的线程可以等待1s,超过了的需要新开线程来执行。

切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,不能新开线程来执行,响应时间会随之陡增。

maxPoolSize 最大线程数在生产环境上我们往往设置成corePoolSize一样,这样可以减少在处理过程中创建线程的开销。

rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理。

keepAliveTime和allowCoreThreadTimeout采用默认通常能满足。

以上都是理想值,实际情况下要根据机器性能来决定。如果在未达到最大线程数的情况机器cpu load已经满了,则需要通过升级硬件和优化代码,降低taskcost来处理。

以下是我自己的的线程池配置:

@Configuration
public class ConcurrentThreadGlobalConfig {
    @Bean
    public ThreadPoolTaskExecutor defaultThreadPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //核心线程数目
        executor.setCorePoolSize(65);
        //指定最大线程数
        executor.setMaxPoolSize(65);
        //队列中最大的数目
        executor.setQueueCapacity(650);
        //线程名称前缀
        executor.setThreadNamePrefix("DefaultThreadPool_");
        //rejection-policy:当pool已经达到max size的时候,如何处理新任务
        //CALLER_RUNS:不在新线程中执行任务,而是由调用者所在的线程来执行
        //对拒绝task的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //线程空闲后的最大存活时间
        executor.setKeepAliveSeconds(60);
        //加载
        executor.initialize();

        return executor;
    }
}

四、线程池队列的选择

workQueue - 当线程数目超过核心线程数时用于保存任务的队列。主要有3种类型的BlockingQueue可供选择:无界队列,有界队列和同步移交。从参数中可以看到,此队列仅保存实现Runnable接口的任务。

这里再重复一下新任务进入时线程池的执行策略:

  • 当正在运行的线程小于corePoolSize,线程池会创建新的线程。

  • 当大于corePoolSize而任务队列未满时,就会将整个任务塞入队列。

  • 当大于corePoolSize而且任务队列满时,并且小于maximumPoolSize时,就会创建新额线程执行任务。

  • 当大于maximumPoolSize时,会根据handler策略处理线程。

1、无界队列

队列大小无限制,常用的为无界的LinkedBlockingQueue,使用该队列作为阻塞队列时要尤其当心,当任务耗时较长时可能会导致大量新任务在队列中堆积最终导致OOM。阅读代码发现,Executors.newFixedThreadPool 采用就是 LinkedBlockingQueue,而博主踩到的就是这个坑,当QPS很高,发送数据很大,大量的任务被添加到这个无界LinkedBlockingQueue 中,导致cpu和内存飙升服务器挂掉。

当然这种队列,maximumPoolSize 的值也就无效了。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

2、有界队列

当使用有限的 maximumPoolSizes 时,有界队列有助于防止资源耗尽,但是可能较难调整和控制。常用的有两类,一类是遵循FIFO原则的队列如ArrayBlockingQueue,另一类是优先级队列如PriorityBlockingQueue。PriorityBlockingQueue中的优先级由任务的Comparator决定。

使用有界队列时队列大小需和线程池大小互相配合,线程池较小有界队列较大时可减少内存消耗,降低cpu使用率和上下文切换,但是可能会限制系统吞吐量。

3、同步移交队列

如果不希望任务在队列中等待而是希望将任务直接移交给工作线程,可使用SynchronousQueue作为等待队列。SynchronousQueue不是一个真正的队列,而是一种线程之间移交的机制。要将一个元素放入SynchronousQueue中,必须有另一个线程正在等待接收这个元素。只有在使用无界线程池或者有饱和策略时才建议使用该队列。

7.fastjson,对象转json字符串的过程中对value为null的值的一些处理

参考链接:https://blog.csdn.net/Amelia__Liu/article/details/123710217?spm=1001.2014.3001.5506

前言

fastjson是一个非常好用的java库,用于操作对象json序列化等等。

问题

最近在写代码的时候遇到问题,通过JSON.toJSONString方法将一个实体对象转为json字符串,转出来发现原本对象中value为null的字段被过滤掉了。。。

实体对象

package so.sao.code;
import lombok.Data;
 
@Data
public class Person {
    String name;
 
    String gender;
 
    String nickname;
 
    String phone;
}

测试代码

public static void main(String[] args) {
    Person person = new Person();
    person.setName("wyz");
    String json = JSON.toJSONString(person);
    System.out.println(json);
}

输出结果

在这里插入图片描述

发现只有设置了值的字段name才有输出,而其余为空的字段则都被过滤掉了

解决:怎样让值为null的key不被过滤掉

  • 设置SerializerFeature

设置SerializerFeature里面有很多特性可以使用

编程大杂烩(一)_第13张图片

要让空值也能够输出的特性值是SerializerFeature.WriteMapNullValue

改造代码:

public static void main(String[] args) {
    Person person = new Person();
    person.setName("wyz");
    // String json = JSON.toJSONString(person);
    // System.out.println(json);

    // 设置SerializerFeature
    System.out.println(JSON.toJSONString(person, SerializerFeature.WriteMapNullValue));
}

输出结果:

在这里插入图片描述

值为null属性直接放到json字符串里面不太好看,能不能把null值转成其他的值,比如空字符串。这个时候就需要用到过滤器了,仔细看看JSON.toJSONString的其他几个方法

编程大杂烩(一)_第14张图片

要实现把null值转为空字符串的效果可以使用ValueFilter

代码实现:

public static void main(String[] args) {
    Person person = new Person();
    person.setName("wyz");
    // String json = JSON.toJSONString(person);
    // System.out.println(json);

    // 设置SerializerFeature
    // System.out.println(JSON.toJSONString(person, SerializerFeature.WriteMapNullValue));

    // 设置NameFilter
    System.out.println(JSON.toJSONString(person, (ValueFilter) (object, name, value) -> {
        if(value == null){
            return "";
        }

        return value;
    }));
}

输出结果:

在这里插入图片描述

一个特殊的需求,有的值为null需要过滤,有的又不需要过滤。。。(这就是我遇到的)

使用PropertyFilter

代码实现:

public static void main(String[] args) {
    Person person = new Person();
    person.setName("wyz");
    // String json = JSON.toJSONString(person);
    // System.out.println(json);

    // 设置SerializerFeature
    // System.out.println(JSON.toJSONString(person, SerializerFeature.WriteMapNullValue));

    // 设置NameFilter
    // System.out.println(JSON.toJSONString(person, (ValueFilter) (object, name, value) -> {
    // if(value == null){
    //    return "";
    // }
    //
    // return value;
    // }));

    // 设置PropertyFilter
    System.out.println(JSON.toJSONString(person, new PropertyFilter() {
        @Override
        public boolean apply(Object object, String name, Object value) {
            return name.equals("gender") || value != null;
        }
    }, SerializerFeature.WriteMapNullValue));
}

输出结果:

在这里插入图片描述

8.拦截器(Interceptor)和过滤器(Filter)的执行顺序和区别

————————————————
版权声明:本文为CSDN博主「止步前行」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zxd1435513775/article/details/80556034

1、过滤器(Filter)

首先说一下Filter的使用地方,我们在配置web.xml时,总会配置下面一段设置字符编码,不然会导致乱码问题:

<filter>
    <filter-name>encodingfilter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class>
    <init-param>
        <param-name>encodingparam-name>
        <param-value>UTF-8param-value>
    init-param>
    <init-param>
        <param-name>forceEncodingparam-name>
        <param-value>trueparam-value>
    init-param>
filter>

<filter-mapping>
    <filter-name>encodingfilter-name>
    <servlet-name>/*servlet-name>
filter-mapping>

配置这个地方的目的,是让所有的请求都需要进行字符编码的设置,下面来介绍一下Filter。

(1)过滤器(Filter):它依赖于servlet容器。它可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的,是用来做一些过滤操作,获取我们想要获取的数据,比如:在Javaweb中,对传入的request、response提前过滤掉一些信息,或者提前设置一些参数,然后再传入servlet或者Controller进行业务逻辑操作。通常用的场景是:在过滤器中修改字符编码(CharacterEncodingFilter)、在过滤器中修改HttpServletRequest的一些参数(XSSFilter(自定义过滤器)),如:过滤低俗文字、危险字符等。

2、拦截器(Interceptor)

拦截器的配置一般在SpringMVC的配置文件中,使用Interceptors标签,具体配置如下:

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <bean class="com.scorpios.atcrowdfunding.web.LoginInterceptor">bean>
    mvc:interceptor>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <bean class="com.scorpios.atcrowdfunding.web.AuthInterceptor">bean>
    mvc:interceptor>
mvc:interceptors>

(2)拦截器(Interceptor):它依赖于web框架,在SpringMVC中就是依赖于SpringMVC框架。在实现上,基于Java的反射机制,属于面向切面编程(AOP)的一种运用,就是在service或者一个方法前,调用一个方法,或者在方法后,调用一个方法,比如动态代理就是拦截器的简单实现,在调用方法前打印出字符串(或者做其它业务逻辑的操作),也可以在调用方法后打印出字符串,甚至在抛出异常的时候做业务逻辑的操作。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。拦截器可以对静态资源的请求进行拦截处理。

3、代码


下面在一个项目中我们使用既有多个过滤器,又有多个拦截器,并观察它们的执行顺序:

(1)第一个过滤器:

public class TestFilter1 implements Filter {  

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {  
        //在DispatcherServlet之前执行  
        System.out.println("############TestFilter1 doFilterInternal executed############");  
        filterChain.doFilter(request, response);  
        //在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后  
        System.out.println("############TestFilter1 doFilter after############");  
    }  
}  

(2)第二个过滤器:

public class TestFilter2 implements Filter {  

    @Override
    protected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {  
        //在DispatcherServlet之前执行  
        System.out.println("############TestFilter2 doFilterInternal executed############");  
        filterChain.doFilter(request, response);  
        //在视图页面返回给客户端之前执行,但是执行顺序在Interceptor之后 
        System.out.println("############TestFilter2 doFilter after############");  
    }  
}  

(3)在web.xml中注册这两个过滤器:

   
<filter>  
    <filter-name>testFilter1filter-name>  
    <filter-class>com.scorpios.filter.TestFilter1filter-class>  
filter>  
<filter-mapping>  
    
    <filter-name>testFilter1filter-name>   
    <url-pattern>/*url-pattern>  
filter-mapping>  

   
<filter>  
    <filter-name>testFilter2filter-name>  
    <filter-class>com.scorpios.filter.TestFilter2filter-class>  
filter>  
<filter-mapping>  
    <filter-name>testFilter2filter-name>  
    <url-pattern>/*url-pattern>  
filter-mapping>  

再定义两个拦截器:

(4)第一个拦截器:

public class BaseInterceptor implements HandlerInterceptor{  

    /** 
     * 在DispatcherServlet之前执行 
     * */  
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {  
        System.out.println("************BaseInterceptor preHandle executed**********");  
        return true;  
    }  

    /** 
     * 在controller执行之后的DispatcherServlet之后执行 
     * */  
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {  
        System.out.println("************BaseInterceptor postHandle executed**********");  
    }  

    /** 
     * 在页面渲染完成返回给客户端之前执行 
     * */  
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)  
        throws Exception {  
        System.out.println("************BaseInterceptor afterCompletion executed**********");  
    }  
}  

(5)第二个拦截器:

public class TestInterceptor implements HandlerInterceptor {  

    /** 
     * 在DispatcherServlet之前执行 
     * */  
    public boolean preHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2) throws Exception {  
        System.out.println("************TestInterceptor preHandle executed**********");  
        return true;  
    }  

    /** 
     * 在controller执行之后的DispatcherServlet之后执行 
     * */
    public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception {  
        System.out.println("************TestInterceptor postHandle executed**********");  
    }  

    /** 
     * 在页面渲染完成返回给客户端之前执行 
     * */  
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {  
        System.out.println("************TestInterceptor afterCompletion executed**********");  
    }  
}  

(6)、在SpringMVC的配置文件中,加上拦截器的配置:

  
<mvc:interceptors>  
      
    <bean name="baseInterceptor" class="com.scorpios.interceptor.BaseInterceptor" />  

    <mvc:interceptor> 
               
        <mvc:mapping path="/test.jsp"/>  
          
        <bean class="com.scorpios.interceptor.TestInterceptor" />  
    mvc:interceptor>  
mvc:interceptors>  

(7)、定义一个Controller控制器:

package com.scorpios.controller;  
import org.springframework.stereotype.Controller;  
import org.springframework.web.bind.annotation.RequestMapping;  
import org.springframework.web.servlet.ModelAndView;  
  
@Controller  
public class TestController {  
    @RequestMapping("/test")  
    public ModelAndView handleRequest(){  
        System.out.println("---------TestController executed--------");  
        return new ModelAndView("test");  
    }  
}  

(8)、测试结果:
编程大杂烩(一)_第15张图片

4、总结


对于上述过滤器和拦截器的测试,可以得到如下结论:

  • Filter需要在web.xml中配置,依赖于Servlet
  • Interceptor需要在SpringMVC中配置,依赖于框架
  • Filter的执行顺序在Interceptor之前,具体的流程见下图

编程大杂烩(一)_第16张图片

两者的本质区别:拦截器(Interceptor)是基于Java的反射机制,而过滤器(Filter)是基于函数回调。从灵活性上说拦截器功能更强大些,Filter能做的事情,都能做,而且可以在请求前,请求后执行,比较灵活。Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类),太细的话,还是建议用interceptor。不过还是根据不同情况选择合适的。

9.拦截器中,request中getReader()和getInputStream()只能调用一次,构建可重复读取inputStream的request

参考链接:https://blog.csdn.net/robinson_911/article/details/106924937?spm=1001.2014.3001.5506

由于 request中getReader()和getInputStream()只能调用一次,在项目中,可能会出现需要针对接口参数进行校验等问题。因此,针对这问题,给出一下解决方案

实现方法:先将RequestBody保存为一个byte数组,然后通过Servlet自带的HttpServletRequestWrapper类覆盖getReader()和getInputStream()方法,使流从保存的byte数组读取。然后再Filter中将ServletRequest替换为ServletRequestWrapper。代码如下:

BodyReaderHttpServletRequestWrapper类包装ServletRequest,将流保存为byte[],然后将getReader()和getInputStream()方法的流的读取指向byte[]

step 1:

添加RepeatedlyReadRequestWrapper 类并继承 HttpServletRequestWrapper 包装类

package com.config;
 
import org.apache.commons.lang3.StringUtils;
 
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
 
public class RepeatedlyReadRequestWrapper extends HttpServletRequestWrapper {
    private final byte[] body;
 
    public RepeatedlyReadRequestWrapper(HttpServletRequest request)
            throws IOException {
        super(request);
        body = readBytes(request.getReader(), "utf-8");
    }
 
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }
 
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream bais = new ByteArrayInputStream(body);
        return new ServletInputStream() {
 
            @Override
            public boolean isFinished() {
            	return false;
            }
 
            @Override
            public boolean isReady() {
            	return false;
            }
 
            @Override
            public void setReadListener(ReadListener listener) {
 
            }
 
            @Override
            public int read() throws IOException {
            	return bais.read();
            }
        };
    }
 
    /**
     * 通过BufferedReader和字符编码集转换成byte数组
     * @param br
     * @param encoding
     * @return
     * @throws IOException
     */
    private byte[] readBytes(BufferedReader br,String encoding) throws IOException{
        String str = null,retStr="";
        while ((str = br.readLine()) != null) {
            retStr += str;
        }
        if (StringUtils.isNotBlank(retStr)) {
            return retStr.getBytes(Charset.forName(encoding));
        }
        return null;
    }
}

step 2:

添加 RepeatedlyReadFilter 实现 filter 过滤器接口方法,当客户端的请求先 过滤 进入SpringMvc Dispatch 路由前先包装下

package com.filter;
 
import com.config.RepeatedlyReadRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
 
/**
 * 复制请求数据包body
 * 以提供 拦截器下 可数次获取Body数据包*/
public class RepeatedlyReadFilter implements Filter {
 
    private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadFilter.class);
 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
 
    }
 
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        logger.debug("复制request.getInputStream流");
        ServletRequest requestWrapper = null;
        if (request instanceof HttpServletRequest) {
            requestWrapper = new RepeatedlyReadRequestWrapper((HttpServletRequest) request);
        }
        if (null == requestWrapper) {
            chain.doFilter(request, response);
        } else {
            chain.doFilter(requestWrapper, response);
        }
    }
 
    @Override
    public void destroy() {
 
    }
}

step 3:

添加拦截器 RepeatedlyReadInterceptor 继承 HandlerInterceptorAdapter 拦截适配器

package com.interceptor;
 
import com.config.RepeatedlyReadRequestWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
 
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.Charset;
 
public class RepeatedlyReadInterceptor extends HandlerInterceptorAdapter {
 
    private static final Logger logger = LoggerFactory.getLogger(RepeatedlyReadInterceptor.class);
 
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        /**
         * 对来自后台的请求统一进行日志处理
         */
        RepeatedlyReadRequestWrapper requestWrapper;
        if (request instanceof RepeatedlyReadRequestWrapper) {
            // 签名处理过程 start.... 
            requestWrapper = (RepeatedlyReadRequestWrapper) request;
            logger.info("请求Body: {} ", getBodyString(requestWrapper));
            // 签名处理过程 end....
        }
        // 默认记录后台接口请求日志记录
        String url = request.getRequestURL().toString();
        String method = request.getMethod();
        String uri = request.getRequestURI();
        String queryString = request.getQueryString();
        logger.info(String.format("请求参数, url: %s, method: %s, uri: %s, params: %s ", url, method, uri, queryString));
        return super.preHandle(request, response, handler);
    }
 
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        RepeatedlyReadRequestWrapper requestWrapper;
        if (request instanceof RepeatedlyReadRequestWrapper) {
            // 测试再次获取Body start.... 
            requestWrapper = (RepeatedlyReadRequestWrapper) request;
            logger.info("请求Body: {} ", getBodyString(requestWrapper));
            // 测试再次获取Body end....
        }
    }
 
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
 
    }
 
    /**
     * 获取请求Body
     *
     * @param request
     *
     * @return
     */
    public static String getBodyString(final ServletRequest request) {
        StringBuilder sb = new StringBuilder();
        InputStream inputStream = null;
        BufferedReader reader = null;
        try {
            inputStream = cloneInputStream(request.getInputStream());
            reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
            String line = "";
            while ((line = reader.readLine()) != null) {
            sb.append(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            }
            if (reader != null) {
            try {
                reader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            }
        }
        return sb.toString();
    }
 
    /**
     * Description: 复制输入流
* * @param inputStream * * @return
*/
public static InputStream cloneInputStream(ServletInputStream inputStream) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; try { while ((len = inputStream.read(buffer)) > -1) { byteArrayOutputStream.write(buffer, 0, len); } byteArrayOutputStream.flush(); } catch (IOException e) { e.printStackTrace(); } InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); return byteArrayInputStream; } }

step 4:

配置过滤器与拦截器 WebMvcConfig

package com.config;
 
import com.filter.RepeatedlyReadFilter;
import com.interceptor.MyInterceptor;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
 
/**
 * SpringMVC 配置类*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RepeatedlyReadInterceptor()).addPathPatterns("/**");
        super.addInterceptors(registry);
    }
 
    @Bean
    public FilterRegistrationBean repeatedlyReadFilter() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        RepeatedlyReadFilter repeatedlyReadFilter = new RepeatedlyReadFilter();
        registration.setFilter(repeatedlyReadFilter);
        registration.addUrlPatterns("/*");
        return registration;
    }
}

10.Oracle SQL日期比较

————————————————
版权声明:本文为CSDN博主「aspnet2002web」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/aspnet2002web/article/details/51543625

需要借助to_date函数把常量转换成日期:

select * from TBL_STUDENT where LAST_UPDATE_TIME <= to_date('2016-05-30 00:00:00','yyyy-mm-dd hh24:mi:ss')

如果输入库里的时间不是24小时的,是带am,pm的

select * from TBL_STUDENT where LAST_UPDATE_TIME <= to_date('2016-05-30 00:00:00 am','yyyy-mm-dd hh24:mi:ss am')

可能会出现以下错误

ORA-01855:am/a.m or pm/p.m. required

这是因为NLS_DATE_LANGUAGE的设置问题。执行以下语句查看

select * from v$nls_parameters;

PARAMETER VALUE
NLS_LANGUAGE SIMPLIFIED CHINESE
NLS_TERRITORY CHINA
NLS_CURRENCY ¥
NLS_ISO_CURRENCY CHINA
NLS_NUMERIC_CHARACTERS .,
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE SIMPLIFIED CHINESE
NLS_CHARACTERSET UTF8
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY ¥
NLS_NCHAR_CHARACTERSET UTF8
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE

发现NLS_DATE_LANGUAGE是SIMPLIFIED CHINESE,并不是AMERICAN。所以修改如下

select * from TBL_STUDENT where LAST_UPDATE_TIME <= to_date('2016-05-30 00:00:00 上午','yyyy-mm-dd hh24:mi:ss am')

11.Mock的使用(虚拟接口)

————————————————
版权声明:本文为CSDN博主「YaaLee_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yrqlyq/article/details/119757872

一、为什么使用mockjs

在做开发时,当后端的接口还未完成,前端为了不影响工作效率,手动模拟后端接口

  • 我们可以使用json文件来模拟后台数据,但比较局限,无法模拟数据的增删改查

  • 使用json-server模拟,但不能随机生成所需数据

  • 使用mockjs模拟后端接口,可随机生成所需数据,可模拟对数据的增删改查

二、mock优点

  • 前后端分离,

  • 可随机生成大量的数据

  • 用法简单

  • 数据类型丰富

  • 可扩展数据类型

  • 在已有接口文档的情况下,我们可以直接按照接口文档来开发,将相应的字段写好,在接口完成 之后,只需要改变url地址即可。

三、mock使用

npm install mockjs --save-dev
// 引入 Mock
var Mock = require('mockjs')

// 定义数据类型
var data = Mock.mock({
  // 20条数据
  "data|20": [{
    // 商品种类
    "goodsClass": "女装",
    // 商品Id
    "goodsId|+1": 1,
    //商品名称
    "goodsName": "@ctitle(10)",
    //商品地址
    "goodsAddress": "@county(true)",
    //商品等级评价★
    "goodsStar|1-5": "★",
    //商品图片
    "goodsImg": "@Image('100x100','@color','小甜甜')",
    //商品售价
    "goodsSale|30-500": 30
  }]
})
// 输出结果随机生成的数据(node index.js)
 console.log(data);

四、mockjs数据类型及语法规范

1、Mock.Random

Mock.Random是一个工具类,用于生成各种随机数据。

Mock.Random 的方法在数据模板中称为『占位符』,书写格式为 @占位符(参数 [, 参数])

2、Mock.Random 提供的完整方法(占位符)如下

编程大杂烩(一)_第17张图片

3、定义数据类型,详情见官方文档

// 引入 Mock
var Mock = require('mockjs')

// 定义数据类型
var data = Mock.mock({
  // 20条数据
  "data|3": [{
    // 商品种类
    "goodsClass": "女装",
    // 商品Id
    "goodsId|+1": 1,
    //商品名称
    "goodsName": "@ctitle(10)",
    //商品地址
    "goodsAddress": "@county(true)",
    //商品等级评价★
    "goodsStar|1-5": "★",
    //商品图片
    "goodsImg": "@Image('100x100','@color','小甜甜')",
    //商品售价
    "goodsSale|30-500": 30,

    // 邮箱:
    "email": "@email",
    // 颜色
    "color": "@color",

    // name
    "name": "@name",

    //img,参数1:背景色,参2:前景色,参3:图片的格式,默认png,参4:图片上的文字

    "img": "@image('100*100','@color')",
    //英文文本(句子)参1:句子的个数,参2:句子的最小个数  参3:句子的最大个数,没有参1时,参2参3才会生效
    "Etext":"@paragraph(1,1,3)",

    //中文文本(句子)参1:句子的个数,参2:句子的最小个数  参3:句子的最大个数,没有参1时,参2参3才会生效
    "Ctext":"@cparagraph(1,1,3)",

    //中国大区
    "cregion":"@region",
    // 省
    "cprovince":"@province",
    //市
    "ccity":"@city",
    //省 + 市
    "ss":"@city(true)",
    //县
    "country":"@county",
    //省市县
    "countrysx":"@county(true)",
    //邮政编码
    "code":"@zip"
  }]
})

// 输出结果
// console.log(data);

4、mockjs语法规范

数据模板中的每个属性由 3 部分构成:属性名、生成规则、属性值:

// 属性名   name
// 生成规则 rule
// 属性值   value
'name|rule': value

注意:
属性名 和 生成规则 之间用竖线 | 分隔。 
生成规则 是可选的。 
生成规则 有 7 种格式: 'name|min-max': value 'name|count': value 
                    'name|min-max.dmin-dmax': value 'name|min-max.dcount': value 
                    'name|count.dmin-dmax': value 'name|count.dcount': value

//属性值自动加 1,初始值为 `number` 'name|+step': value    

生成规则 的 含义 需要依赖 属性值的类型 才能确定。
属性值 中可以含有 @占位符。
属性值 还指定了最终值的初始值和类型。

1.属性值是字符串 String

‘name|min-max’: string

通过重复 string 生成一个字符串,重复次数大于等于 min,小于等于 max。

‘name|count’: string

通过重复 string 生成一个字符串,重复次数等于 count。

2.属性值是数字 Number

‘name|+1’: number

属性值自动加 1,初始值为 number。

‘name|min-max’: number

生成一个大于等于 min、小于等于 max 的整数,属性值 number 只是用来确定类型。

‘name|min-max.dmin-dmax’: number

生成一个浮点数,整数部分大于等于 min、小于等于 max,小数部分保留 dmin 到 dmax 位。

3.属性值是布尔型 Boolean

‘name|1’: boolean

随机生成一个布尔值,值为 true 的概率是 1/2,值为 false 的概率同样是 1/2。

‘name|min-max’: value

随机生成一个布尔值,值为 value 的概率是 min / (min + max),值为 !value 的概率是 max / (min + max)。

4.属性值是对象 Object

‘name|count’: object

从属性值 object 中随机选取 count 个属性。

‘name|min-max’: object

从属性值 object 中随机选取 min 到 max 个属性。

5.属性值是数组 Array

‘name|1’: array

从属性值 array 中随机选取 1 个元素,作为最终值。

‘name|+1’: array

从属性值 array 中顺序选取 1 个元素,作为最终值。

‘name|min-max’: array

通过重复属性值 array 生成一个新数组,重复次数大于等于 min,小于等于 max。

‘name|count’: array

通过重复属性值 array 生成一个新数组,重复次数为 count。

6.属性值是函数 Function

‘name’: function

执行函数 function,取其返回值作为最终的属性值,函数的上下文为属性 ‘name’ 所在的对象。

7.属性值是正则表达式 RegExp

‘name’: regexp

根据正则表达式 regexp 反向生成可以匹配它的字符串。用于生成自定义格式的字符串。

Mock.mock({
    'regexp1': /[a-z][A-Z][0-9]/,
    'regexp2': /\w\W\s\S\d\D/,
    'regexp3': /\d{5,10}/
})
// =>
{
    "regexp1": "pJ7",
    "regexp2": "F)\fp1G",
    "regexp3": "561659409"
}

五、 Mock.setup()

Mock.setup( settings )

配置拦截 Ajax 请求时的行为。支持的配置项有:timeout

指定被拦截的 Ajax 请求的响应时间,单位是毫秒。

值可以是正整数,例如 400,表示 400 毫秒 后才会返回响应内容;

也可以是横杠 ‘-’ 风格的字符串,例如 ‘200-600’,表示响应时间介于 200 和 600 毫秒之间。默认值是’10-100’。

Mock.setup({
    timeout: 400
})
Mock.setup({
    timeout: '200-600'
})
目前,接口 Mock.setup( settings ) 仅用于配置 Ajax 请求,将来可能用于配置 Mock 的其他行为。

六、Mock.Random 扩展方法

// 引入 Mock
var Mock = require('mockjs')

var random = Mock.Random;

//扩展数据模板
random.extend({
  type: function (index:number) {
    const types = ['products', 'industryApp', 'solution', 'experts'];
    return this.pick(types[index])
  }
});

// 定义数据类型
const  menuSource:Array<any> = [];
 menuSource[0] = Mock.mock({
  "type": "@type(0)",
   'data|3-4':[{
     'id|+1': 1,
     name: "@ctitle( 4,6)",
     "childs|5-10": [{
       'id|+1': 1,
       name: "@ctitle(4,6)",
     }]
   }]
});
// 输出结果
 console.log(data);

七、mockjs获取前端传递的数据

axios({
      method: "get",
      url: "/getGoods",
      data: {
        id:2
      }
    }).then(data => {
      //成功的回调函数,返回的是增加的数据
      console.log(data.data.data);
      this.url = data.data.data[0].goodsImg
    });
  }

  Mock.mock("/getGoods", "get", (config) => {
  console.log(config);
  return data;
})

mockjs可以通过option.body获取前端传递的数据

12.加个final就能防止被修改?是我太naive了

————————————————
版权声明:本文为CSDN博主「Jerrycodes」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/kyle_wu_/article/details/119157457

1、什么是不变性

要想回答上面的问题,我们首先得知道什么是不变性(Immutable)。如果对象在被创建之后,其状态就不能修改了,那么它就具备“不变性”。

我们举个例子,比如下面这个 Person 类:

public class Person {
    final int id = 1;
    final int age = 18;
}

如果我们创建一个 person 对象,那么里面的属性会有两个,即 id 和 age,并且由于它们都是被 final 修饰的,所以一旦这个 person 对象被创建好,那么它里面所有的属性,即 id 和 age 就都是不能变的。我们如果想改变其中属性的值就会报错,代码如下所示:

public class Person {
 
    final int id = 1;
    final int age = 18;
 
    public static void main(String[] args) {
        Person person = new Person();
//        person.age=5;//编译错误,无法修改 final 变量的值
    }
}

比如我们尝试去改变这个 person 对象,例如将 age 改成 5,则会编译通不过,所以像这样的 person 对象就具备不变性,也就意味着它的状态是不能改变的。

final 修饰对象时,只是引用不可变!

这里有个非常重要的注意点,那就是当我们用 final 去修饰一个指向对象类型(而不是指向 8 种基本数据类型,例如 int 等)的变量时候,那么 final 起到的作用只是保证这个变量的引用不可变,而对象本身的内容依然是可以变化的。下面我们对此展开讲解。

被 final 修饰的变量意味着一旦被赋值就不能修改,也就是只能被赋值一次,如果我们尝试对已经被 final 修饰过的变量再次赋值的话,则会报编译错误。我们用下面的代码来说明:

/**
 * 描述:     final变量一旦被赋值就不能被修改
 */
public class FinalVarCantChange {
 
    private final int finalVar = 0;
    private final Random random = new Random();
    private final int array[] = {1,2,3};
 
    public static void main(String[] args) {
        FinalVarCantChange finalVarCantChange = new FinalVarCantChange();
//        finalVarCantChange.finalVar=9;     //编译错误,不允许修改final的变量(基本类型)
//        finalVarCantChange.random=null;    //编译错误,不允许修改final的变量(对象)
//        finalVarCantChange.array = new int[5];//编译错误,不允许修改final的变量(数组)
    }
}

我们首先在这里分别创建了一个 int 类型的变量、一个 Random 类型的变量,还有一个是数组,它们都是被 final 修饰的;然后尝试对它们进行修改,比如把 int 变量的值改成 9,或者把 random 变量置为 null,或者给数组重新指定一个内容,这些代码都无法通过编译。

这就证明了“被 final 修饰的变量意味着一旦被赋值就不能修改”,而这个规则对于基本类型的变量是没有歧义的,但是对于对象类型而言,final 其实只是保证这个变量的引用不可变,而对象本身依然是可以变化的。这一点同样适用于数组,因为在 Java 中数组也是对象。那我们就来举个例子,看一看以下 Java 程序的输出:

class Test {
    public static void main(String args[]) {
       final int arr[] = {1, 2, 3, 4, 5};  //  注意,数组 arr 是 final 的
       for (int i = 0; i < arr.length; i++) {
           arr[i] = arr[i]*10;
           System.out.println(arr[i]);
       }
    }
}

首先来猜测一下,假设不看下面的输出结果,只看这段代码,你猜它打印出什么样的结果?

这段代码中有个 Test 类,而且这个类只有一个 main 方法,方法里面有一个 final 修饰的 arr 数组。注意,数组是对象的一种,现在数组是被 final 修饰的,所以它的意思是一旦被赋值之后,变量的引用不能修改。

但是我们现在想证明的是,数组对象里面的内容可以修改,所以接下来我们就用 for 循环把它里面的内容都乘以 10,最后打印出来结果如下:

10 
20 
30 
40 
50

可以看到,它打印出来的是 10 20 30 40 50,而不是最开始的 1 2 3 4 5,这就证明了,虽然数组 arr 被 final 修饰了,它的引用不能被修改,但是里面的内容依然是可以被修改的。

同样,对于非数组的对象而言也是如此,我们来看下面的例子:

class Test { 
    int p = 20; 
    public static void main(String args[]){ 
       final Test t = new Test();
       t.p = 30; 
       System.out.println(t.p);
    }
}

这个 Test 类中有一个 int 类型的 p 属性,我们在 main 函数中新建了 Test 的实例 t 之后,把它用 final 修饰,然后去尝试改它里面成员变量 p 的值,并打印出结果,程序会打印出“30”。一开始 p 的值是 20,但是最后修改完毕变成了 30,说明这次修改是成功的。

以上我们就得出了一个结论,final 修饰一个指向对象的变量的时候,对象本身的内容依然是可以变化的。

2、final 和不可变的关系

这里就引申出一个问题,那就是 final 和不变性究竟是什么关系?

那我们就来具体对比一下 final 和不变性。关键字 final 可以确保变量的引用保持不变,但是不变性意味着对象一旦创建完毕就不能改变其状态,它强调的是对象内容本身,而不是引用,所以 final 和不变性这两者是很不一样的。

对于一个类的对象而言,你必须要保证它创建之后所有内部状态(包括它的成员变量的内部属性等)永远不变,才是具有不变性的,这就要求所有成员变量的状态都不允许发生变化。

有一种说法就认为:“要想保证对象具有不变性的最简单的办法,就是把类中所有属性都声明为 final”,这条规则是不完全正确的,它通常只适用于类的所有属性都是基本类型的情况,比如前面的例子:

public class Person {
    final int id = 1;
    final int age = 18;
}

Person 类里面有 final int id 和 final int age 两个属性,都是基本类型的,且都加了 final,所以 Person 类的对象确实是具备不变性的。

但是如果一个类里面有一个 final 修饰的成员变量,并且这个成员变量不是基本类型,而是对象类型,那么情况就不一样了。有了前面基础之后,我们知道,对于对象类型的属性而言,我们如果给它加了 final,它内部的成员变量还是可以变化的,因为 final 只能保证其引用不变,不能保证其内容不变。所以这个时候若一旦某个对象类型的内容发生了变化,就意味着这整个类都不具备不变性了。

所以我们就得出了这个结论:不变性并不意味着,简单地使用 final 修饰所有类的属性,这个类的对象就具备不变性了。

那就会有一个很大的疑问,假设我的类里面有一个对象类型的成员变量,那要怎样做才能保证整个对象是不可变的呢?

我们来举个例子,即一个包含对象类型的成员变量的类的对象,具备不可变性的例子。

代码如下:

public class ImmutableDemo {
 
    private final Set<String> lessons = new HashSet<>();
 
    public ImmutableDemo() {
        lessons.add("第01讲:为何说只有 1 种实现线程的方法?");
        lessons.add("第02讲:如何正确停止线程?为什么 volatile 标记位的停止方法是错误的?");
        lessons.add("第03讲:线程是如何在 6 种状态之间转换的?");
    }
 
    public boolean isLesson(String name) {
        return lessons.contains(name);
    }
}

在这个类中有一个 final 修饰的、且也是 private 修饰的的一个 Set 对象,叫作 lessons,它是个 HashSet;然后我们在构造函数中往这个 HashSet 里面加了三个值,分别是第 01、02、03 讲的题目;类中还有一个方法,即 isLesson,去判断传入的参数是不是属于本课前 3 讲的标题,isLesson 方法就是利用 lessons.contains 方法去判断的,如果包含就返回 true,否则返回 false。这个类的内容就是这些了,没有其他额外的代码了。

在这种情况下,尽管 lessons 是 Set 类型的,尽管它是一个对象,但是对于 ImmutableDemo 类的对象而言,就是具备不变性的。因为 lessons 对象是 final 且 private 的,所以引用不会变,且外部也无法访问它,而且 ImmutableDemo 类也没有任何方法可以去修改 lessons 里包含的内容,只是在构造函数中对 lessons 添加了初始值,所以 ImmutableDemo 对象一旦创建完成,也就是一旦执行完构造方法,后面就再没有任何机会可以修改 lessons 里面的数据了。

而对于 ImmutableDemo 类而言,它就只有这么一个成员变量,而这个成员变量一旦构造完毕之后又不能变,所以就使得这个 ImmutableDemo 类的对象是具备不变性的,这就是一个很好的“包含对象类型的成员变量的类的对象,具备不可变性”的例子。

3、总结

我们首先介绍了什么是不变性,然后介绍了用 final 修饰一个对象类型的变量的时候,只能保证它的引用不变,但是对象内容自身依然是可以变的。仅仅把所有的成员变量都用 final 修饰并不能代表类的对象就是具备不变性的。

13.oracle数据库安装、创建实例、配置服务和监听程序、连接

————————————————
版权声明:本文为CSDN博主「仗剑天涯777i」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41419769/article/details/90318022

1、oracle数据库安装

一、Oracle下载

注意Oracle分成两个文件,下载完后,将两个文件解压到同一目录下即可。 路径名称中,最好不要出现中文,也不要出现空格等不规则字符。

官方下地址:
http://www.oracle.com/technetwork/database/enterprise-edition/downloads/index.html

二、Oracle安装

1.解压缩文件,将两个压缩包一起选择, 鼠标右击 -> 解压文件 如图

编程大杂烩(一)_第18张图片

2、到相应的解压路径上面,找到可执行安装文件【 setup.exe 】双击安装

3、配置安全更新,这步可将自己的电子邮件地址填写进去(也可以不填写,只是收到一些没什么用的邮件而已)。取消下面的“我希望通过My Oracle Support接受安全更新(W)”。 如图:

编程大杂烩(一)_第19张图片

4、安全选项,直接选择默认创建和配置一个数据库(安装完数据库管理软件后,系统会自动创建一个数据库实例)。 如图:

编程大杂烩(一)_第20张图片

5、系统类,直接选择默认的桌面类就可以了,图略

6、典型安装,重要步骤。建议只需要将Oracle基目录更新下,目录路径不要含有中文或其它的特殊字符。全局数据库名可以默认,且口令密码,必须要牢记。密码输入时,有提示警告,不符合Oracel建议时不用管。 (因Oracel建议的密码规则比较麻烦, 必须是大写字母加小写字母加数字,而且必须是8位以上。) 如图:

编程大杂烩(一)_第21张图片

7、先决条件检查。 安装程序会检查软硬件系统是否满足,安装此Oracle版本的最低要求。 直接下一步就OK 了。如图:
编程大杂烩(一)_第22张图片

8、概要,安装前的一些相关选择配置信息。 可以保存成文件或不保存文件直接点完成即可,然后开始进行安装,如图:

编程大杂烩(一)_第23张图片

9、数据库管理软件文件及dbms文件安装完后,会自动创建安装一个实例数据库,数据库的名称默认是前面设置的orcl。如图:

编程大杂烩(一)_第24张图片

10、 实例数据库创建完成了,系统默认把所有账户都锁定不可用(除sys和system账户可用外),建议点右边的口令管理,将常用的scott账户解锁并输入密码。 如图:

编程大杂烩(一)_第25张图片

11、解锁scott账户, 去掉前面的绿色小勾,输入密码

编程大杂烩(一)_第26张图片

12、 安装成功,完成即可

三、测试连接

可以通过开始,应用程序中的 “Oracle 11g” -> “应用程序开发” -> “Sql Developer 或Sql Plus” 连接

使用sql plus测试连接如下:
编程大杂烩(一)_第27张图片

上图是Oracle只有一个数据库实例的情况(即安装时配置的orcl),如果有多个数据库实例,则要用如下方式连接到某个实例:

编程大杂烩(一)_第28张图片

2、创建数据库实例

1)点击开始菜单:开始=>程序=>Oracle=>oraDb11g_home1=>配置和移植工具=>Database Configuration Assistant,出现下图所示界面,单击“下一步”,系统提示选择需要进行的操作,选择“创建数据库”,点击下一步

编程大杂烩(一)_第29张图片

2)选择“一般用途或事务处理”,点击下一步

3)输入“全局数据库名“和”SID“,SID即数据库实例名,如db001,全局数据库名格式通常为name.domain,name一般与SID相同(输入全局数据库名的时候,SID会自动填充,填充的值也就是name),domain可以为空,所以,全局数据库名可以和SID相同,如,都为db001,点击下一步

4)Enterprise Manager默认即可,点击下一步

5)设置数据库实例必备的用户的密码,如图:

编程大杂烩(一)_第30张图片

6)数据库文件类型及存储,默认即可,点击下一步

7)数据库恢复选项默认即可,点击下一步

8)字符集选择Unicode AL32UTF8

9)后面的选项默认即可,然后点击确定,进行安装

10)安装时会警告,如下图,原因是监听程序未启动,可以先不用管,后面再配置,点击确定:

编程大杂烩(一)_第31张图片

11)配置口令,默认SCOTT用户是锁定的,我们可以将其解锁,然后点击“确定”,最后点击“退出”,至此,数据库实例创建完成。

12)下面是配置服务和监听程序(开始,应用程序中的 “Oracle 11g” -> “配置和移植工具” ->“Net Manager”)

3、配置服务和监听程序

编程大杂烩(一)_第32张图片

配置监听程序

编程大杂烩(一)_第33张图片

然后再重启监听服务【重要!重要!重要!】,如下图:

编程大杂烩(一)_第34张图片

如果此数据库配置到阿里云&腾讯云服务器时,通过IP无法连接,解决方式如下:

4、启动oracle的监听

1.打开NetConfiguration Assistant

编程大杂烩(一)_第35张图片

2.选择监听程序配置,下一步

编程大杂烩(一)_第36张图片

3.选择重新配置,下一步

编程大杂烩(一)_第37张图片

4.选择监听程序,默认,下一步

编程大杂烩(一)_第38张图片

注:如果你的监听已启动,则出现提示框,选择是

编程大杂烩(一)_第39张图片

5.选择协议,使用默认的TCP协议,下一步

编程大杂烩(一)_第40张图片

6.选择端口号,使用标准端口号1521,下一步

编程大杂烩(一)_第41张图片

7.不配置另一个监听程序,选择否,下一步

编程大杂烩(一)_第42张图片

8.监听程序配置完成,下一步

编程大杂烩(一)_第43张图片

重配服务名,测试连接

1.选择Net服务器配置,下一步

编程大杂烩(一)_第44张图片

2.选择重新配置,下一步

编程大杂烩(一)_第45张图片

3.选择数据库名,下一步

编程大杂烩(一)_第46张图片

4.填写服务名,也就是你创建数据库时的全数据库名,下一步

编程大杂烩(一)_第47张图片

5.选择TCP协议,下一步

编程大杂烩(一)_第48张图片

6.填写主机名(可填写你的IP地址,也可填写你的主机名),使用标准端口号1521,下一步

编程大杂烩(一)_第49张图片

7.进行测试,下一步

编程大杂烩(一)_第50张图片

8.选择更改登录

编程大杂烩(一)_第51张图片

9.填写用户名(system)和口令(bjpowernode),确定

编程大杂烩(一)_第52张图片

10.测试连接成功,下一步

编程大杂烩(一)_第53张图片

11.网络服务名,默认(和之前的数据库名一样),下一步

编程大杂烩(一)_第54张图片

12.不配置另一个Net服务名,选择否,下一步

编程大杂烩(一)_第55张图片

13.Net服务名配置完毕,下一步,完成

编程大杂烩(一)_第56张图片

启动PL/SQLDeveloper或Dbvisualizer可视化管理工具,输入用户名和口令,登录成功!

14.Oracle中的sys和system用户

————————————————
版权声明:本文为CSDN博主「Vino-24」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_39344692/article/details/80385331

1、sys

(1) sys用户是超级用户,具有最高权限即sysdba角色,有create database的权限,所有oracle的数据字典的基表和视图都存放在sys用户中,这些基表和视图对于oracle的运行是至关重要的,由数据库自己维护,任何用户都不能手动更改。

该用户的默认密码是: change_on_install 。

编程大杂烩(一)_第57张图片

注意: 如果用pl/sql登录的话,用户权限项选择SYSDBA;

编程大杂烩(一)_第58张图片

2、system

(2 ) system用户即普通dba用户角色,用于存放一级的内部数据,如oracle的一些特性或工具管理信息。

该用户的默认密码是:manager 。

oracle数据库中有三个较大的角色sysdba , sysoper和dba 。system用户只拥有其中的两项: sysoper和dba ,而sys用户拥有全部三种角色。此外:system用户以sysdba身份登录时就是sys,准确地说,任何用户以sysdba身份登录时都是sys,这一点,你登陆后执行show
user可以验证。

以下参考链接:https://blog.csdn.net/Ahuuua/article/details/107565181?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165787053616782350882331%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165787053616782350882331&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-2-107565181-null-null.142^v32^pc_search_result_control_group,185^v2^control&utm_term=sysdba%20%2C%20sysoper%E5%92%8Cdba%20&spm=1018.2226.3001.4187

3、sys和system用户区别

SYS用数据库的超级用户,数据库内很多重要的东西(数据字典表、内置包、静态数据字典视图等)都属于这个用户,SYS用户必须以SYSDBA身份登录。

SYSTEM是数据库内置的一个普通管理员,你手工创建的任何用户在被授予DBA角色后都跟这个用户差不多(赋予DBA角色相当于给予了一组数据操作的权限)。

相当于sys是qq群主,system是qq群管理员

两者区别

1)最重要的区别,存储的数据的重要性不同

sys所有oracle的数据字典的基表和视图都存放在sys用户中,这些基表和视图对于oracle的运行是至关重要的,由数据库自己维护,任何用户都不能手动更改。sys用户拥有dba角色和sysdba,sysoper身份(系统权限),是oracle权限最高的用户。

system用户用于存放次一级的内部数据,如oracle的一些特性或工具的管理信息。system用户拥有普通dba角色权限。

2)其次的区别,权限的不同

sys用户具有“SYSDBA”或者“SYSOPER”系统权限,登陆也只能用这两个身份,不能用normal。

system用户只能用normal身份登陆,除非你对它授予了sysdba的系统权限或者syspoer系统权限。

4、normal 、sysdba、 sysoper区别

  • normal是普通用户(system用户只能用normal身份登陆)

  • sysdba不是权限,sysdba拥有最高的系统权限,当用户以SYSDBA身份登陆数据库时,登陆用户都会变成sys。

  • sysoper身份主要用来启动、关闭数据库,sysoper登陆后用户是public

  • sysdba和sysoper属于system privilege,也称为administrative privilege,拥有例如数据库开启关闭之类一些系统管理级别的权限

sysdba和sysoper具体的权限可以看下表:

编程大杂烩(一)_第59张图片

5、dba、 sysoper区别

DBA是一种role对应的是对Oracle实例里对象的操作权限的集合,而SYSDBA是概念上的role是一种登录认证时的身份标识而已。

15.Oracle的命令行常用操作

1. 登录

运行cmd进入命令行,sqlplus 用户名/密码,如果是超级管理员需要在用户名/密码后面加上 as sysdba,表示是以系统管理员的身份来登录的,如图。

编程大杂烩(一)_第60张图片

如果是普通用户不需要as sysdba,如果本地有多个数据库实例,则通过以下方式进行连接具体的oracle数据库实例:

编程大杂烩(一)_第61张图片

2.查看当前连接数据库的用户

使用show user查看当前的用户

SQL> show user
USER"SYS"

3. 用户的切换

在登录的状态下输入:conn 用户名/密码 [as sysdba]

如图:

Ø 切换为普通的用户

SQL> conn system/123456
已连接。

Ø 切换为超级管理员

SQL> conn sys/123456 as sysdba
已连接。

4.查看用户下的表

在用户登录的情况下输入:select * from tab;

16.jdbcTemplate爬坑之queryForList

————————————————
版权声明:本文为CSDN博主「小小的人儿居然已存在」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq1049545450/article/details/112844582

先看看jdbcTemplate.queryForList这个方法,有7个传参形式,我们就看第一个,我也就是被第一个坑了下,以我图片中的代码为例,我想查询出所有的user,我用jdbcTemplate,他既然提供了queryForList接口,并且支持传一个class类型进去,那么这不就是告诉我这个方法可以直接返回List嘛,我也是这么做的:

编程大杂烩(一)_第62张图片

但是当我运行之后结果却是:

在这里插入图片描述

诶?啥意思,期望返回一列,实际返回两列?但是select * FROM USER确实是应该返回两列啊,我实际场景的sql比这个复杂,我最开始以为是sql的问题,结果去检查了sql确认sql没问题,然后再去看queryForList这个方法才发现问题:

在这里插入图片描述

getSingleColumnRowMapper构建的mapper只有一个列啊?看下报错的地方:

编程大杂烩(一)_第63张图片

返回结果列不等于1就抛异常,好吧,看来singleColumnRowMapper这个不能拿来查对象了,那怎么办呢?

  • queryForList只支持返回单列对象结果,或者返回List>自己去组对象

  • 如果想直接返回对象集合,用query(sql, BeanPropertyRowMapper.newInstance(T.class));

17.JDBCTemplate如何用in批量查询或者修改

参考链接:https://zjj1994.blog.csdn.net/article/details/121041633?spm=1001.2014.3001.5506

可以通过使用 NamedParameterJdbcTemplate 来进行操作,如入参ids 是一个 “3,4,2” 的字符串

@Autowired
private JdbcTemplate jdbcTemplate;

@Override
public int updateSolveByIds(String ids) {
    System.out.println("ids = " + ids);  // ids = 3,4,2
    //字符串根据逗号切割成数组
    String[] split = ids.split(",");
    //将数组转成集合
    List<String> idsList = Arrays.asList(split);
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("ids", idsList); //放入Map里面,
    // 实例化一个NamedParameterJdbcTemplate 对象,通过构造传参给jdbcTemplate 传进来
    NamedParameterJdbcTemplate nameJdbc = new NamedParameterJdbcTemplate(jdbcTemplate);
    // 注意sql,用 冒号ids 代表从params里面取参数
    String sql = "UPDATE oms_order_mq_retry_upper_limit_log   SET is_solve = 1  WHERE id In (:ids) AND is_del = 0";
    //执行NamedParameterJdbcTemplate 的update方法
    return nameJdbc.update(sql, params);
}

喜欢请关注我

至此,我们的编程大杂烩(一)就告一段落了。喜欢我的话可以关注我的微信公众号我爱学习呀嘻嘻,不定期分享各类资源哦。

编程大杂烩(二)

编程大杂烩(三)

编程大杂烩(四)

在这里插入图片描述

你可能感兴趣的:(java,intellij-idea,spring,spring,boot,spring,cloud)