java开发日常

spring

1.事务传播行为

spring的事务传播行为有7类,

事务传播就是:一个带有事务的方法被调用的时候,他的事务应该怎么处理。

方法A带事务,方法B带事务

  1. require
当方法B被调用时,如果调用它的那个方法带事务,就按照它的事务执行,如果不带就开启一个新的事务

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // do something
}

当执行方法B的时候,发现没有事务,这时就会开启一个事务。

当执行方法A的时候,也没有事务,然后开启一个新事务;再执行方法B的时候,发现有事务,就加入进去。

  1. supports

就是有事务就加入进去,没有事务就以非事务运行。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
// 事务属性为SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // do something
}

单独执行方法B的时候,没有事务就以非事务运行。

在方法A中执行的时候,发现有事务,就加入。

  1. mandatory(强制的)

如果已经存在一个事务,支持当前事务。如果没有活动的事务,则抛出异常。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
// 事务属性为MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // do something
}

当单独调用方法B的时候,由于没有事务,抛出:throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);

当在方法A中执行的时候,存在事务,就加入进去。

  1. require_new

它会开启一个新事务,如果已经存在一个事务,会将已经存在的事务挂起。

  1. not_support

总是以非事务运行,如果存在事务,就将事务挂起

  1. never

总是以非事务方式运行,如果存在一个活动事务,则抛出异常

  1. nested(嵌套)

如果在一个事务中,则运行在嵌套事务中;如果没有活动事务,就按require执行。

2.lombok

@EqualsAndHashCode(callSuper = true)

使用@Data 默认会重写EqualsAndHashCode(callSuper = false)方法 但是 callSuper默认是false,即忽略父类的成员变量。
(callSuper=true) 

3.Transactional

事务:

    @Transactional(rollbackFor = Exception.class,noRollbackFor = BizServiceException.class)

noRollbackFor

意思就是在你声明的这个事务里,如果发生了BizServiceException这个异常就noRollBack,就是数据库事务不发生回滚。

4.BeanUtils

BeanUtils.copy()

作用:方法两个对象之间进行字段值的拷贝

 BeanUtils.copy(from,target);

​ 把from拷贝到target中,条件:拷贝实体类之间只要保证字段名称一样即可。

​ 但是要知道存在三个问题:

1. 字段名必须一样

需要两个拷贝类的属性字段名必须一致,当他们的字段值不一致的时候,需要手动获取并赋值。缺点就是容易忽略。
a.setXX(b.getXX);

2.性能问题

​ BeanUtils内部实现采用的是反射功能,当反射的操作数达到万级别的时候,消耗非常明显。我没试过

3.泛型擦除问题

​ 在进行集合直接拷贝数据时,以为泛型擦除问题,导致拷贝失败。比如我们需要将po集合列表转化为Dto的集合,因为泛型参数问题,最终得到的还是po的集合。当然这个问题我也没遇到过,(我是caiji)。

5.java中的fail-fast(快速失败机制)

1.什么是fail-fast

fail-fast 机制,及快速失败机制,是java集合(Collection)中的一种错误检测机制。当在迭代集合的过程中,该集合在数据结构上发生改变的时候,就有可能发生fail-fast ,即抛出 ConcurrentModificationException异常,fail-fast机制并不保证在不同步的修改下一定会抛出异常,它只是尽最大的努力取抛出,所以这种机制一般仅用于检测bug。

可能出现的场景

​ 在常见的集合 ArrayList,HashMap。在多线程和单线程的环境下,都有可能出现快速失败。

2.为什么会出现fail-fast

首先:对于集合如:list,map,我们都可以通过迭代器来遍历,而Iterator其实就是一个接口,具体的实现还是要看具体的集合类中的内部类取实现Iterator 并实现相关的方法。以ArrayList为例:当调用list.iterator()时:其源码为:

public Iterator<E> iterator() {
        return new Itr();
    }

即它会返回一个新的Itr类,而Itr类是ArrayList的内部类,实现了Iterator的接口,下面是该类的源码;

/**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

其中,有三个属性:

int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

cursor是指集合遍历过程中的即将遍历的元素的索引,lastRet是cursor -1,默认为 -1 ,即不存在上一个时,为 -1,它主要用于记录刚刚遍历过的元素的索引。expectedModCount 这个就是fail-fast 判断的关键变量,它初始值就为ArrayList中的modCount。(modCount是抽象类AbstractList中的变量),默认为0 ,而ArrayList继承了AbstractList,所以也有这个变量,modCount用于记录集合操作过程中作的修改次数,与size还是有区别的,并不等于size。

        public boolean hasNext() {
            return cursor != size;
        }

​ 迭代器结束的标志就是hasNext()返回false ,而该方法就是用cursor游标和size(集合中的元素数目)进行对比,当cursor等于size时,标识遍历已经完成。

​ 然后是next()方法,为什么在迭代的过程中,如果有线程对集合的结构做出改变,就会发生fail-fast

       @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

​ 从源码可以看到,每次调用next()方法的时候,在实际访问元素前,都会调用 checkForComodification()方法,

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

​ 可以看出,该方法才是判断是否抛出 ConcurrentModificationException 异常的关键。在该段代码中,当 modCount != expectModCount 时,就会抛出该异常。但是在一开始的时候,expectionModCount 初始值默认等于 modCount,为什么会出现 modCount !=expectModCount,很明显expectedModCount 在整个迭代过程中,除了一开始赋予初始值modCount 外,并没有发生该表,所以可能发生该表的值是 modCount,在前面关于ArrayList 扩容机制的分析中,就可以知道 ArrayList 进行 add,remove,clear 等涉及修改集合中的元素的个数的操作时,modCount 就会发生改变(modCount++),所以当另一个线程(并发修改)或者同一个线程遍历的过程中,调用相关方法使集合的个数方法改变,就会使modCount发生变化,这样在 checkForComfdification 方法中 就会抛出ConcurrentModificationException 异常。

3.如何避免fail-fast

​ 在了解了fail-fast的产生原理之后,接下来就看看如何解决。

方法1

​ 在单线程的遍历过程中,如果要进行remove操作,可以使用迭代器的remove方法而不是集合类的remove方法。下面是ArrayList 中 迭代器remove方法的源码:

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
 
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

​ 可以看到,该remove方法并不会修改modCount 的值,并且不会对后面remove不能指定元素,只能remove当前遍历过的那个元素,所以调用该方法并不会发生fail-fast现象。该方法有局限性:

例子:

 public static void main(String[] args) {
        //ConcurrentHashMap:并发容器
        Map<Object, Object> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
            map.put(i+"",+i+"");
        }
        Iterator<Map.Entry<Object, Object>> iterator = map.entrySet().iterator();
        int i = 0;
        while (iterator.hasNext()) {
            if (i == 3) {
                map.remove(3+"");
                iterator.remove();
            }
            Map.Entry<Object, Object> next = iterator.next();
            System.out.println("key:"+next.getKey()+" and value="+next.getValue());
            i++;
        }
    }

方法2

​ 就是使用并发包中的并发容器。

​ 比如使用CopyOnWriteArrayList 代替 ArrayList,CopyOnWriteArrayList 在使用上跟ArrayList 几乎一样,CopyOnWrite 是写时复制的容器(COW),在读写时是线程安全的。该容器在对add和remove等操作时,并不是在原数组上进行修改的,而是将原数组拷贝一份,在新数组上进行修改,待完成后,才将指向旧数组的指引指向新数组,所以对于CopyOnWriterArrayList 在迭代过程并不会发生fail-fast现象。但CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。

​ 对于HashMap,可以使用ConcurrentHashMap,ConcurrentHashMap使用了锁机制,是线程安全的,在迭代方法,ConcurrentHashMap使用了一种不同的迭代方法。在这种迭代方式中,当iterator被创建后集合再发生改变就不在抛出ConcurrentModificaitionExecption,取而代之的是再改变时new新的数据从而不影响原有的数据,iterator 完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。即迭代不会发生fail-fast,但不保证获取的是最新的数据。(即弱一致性)。

6.@Transactional

:dog:关于@Transactional (事务),我就想知道默认的(没有任何参数的),是什么一种效果;@Transactional 作用在实现类的方法上(具体点就是增删改方法上),当遇到 RuntimeException (也就是运行时异常,关于异常的分类,会放在最后进行讲解)异常的时候,方法发生回滚(数据回到执行方法之前)。 

1.@Transactional的参数

属性类型描述
valueString可选的限定描述符,指定使用的事务管理器
propagationenum:Propagation可选的事务传播行为设置
isolationenum:Isolation可选的事务隔离级别设置
readOnlyboolean读写或只读事务,默认读写
timeOutint(in seconds granularity)事务超时时间设置
rollbackForClass对象数组,必须继承自Throwable导致事务回滚的异常类数组
rollbackForClassName类名数组,必须继承自Throwable导致事务回滚的异常类名字数组
noRollbackForClass对象数组,必须继承自Throwable不会导致事务回滚的异常类数组
noRollbackForClassName类名数组,必须继承自Throwable不会导致事务回滚的异常类名字数组

这些参数我用过的也就 readOnly(读写或只读事务)rollbackFor(会回滚的异常类),noRollbackFor(不会回滚的异常类)

然后我觉的比较难理解的就是 propagation(事务传播)isolation(隔离级别)

2. 隔离级别

3.异常

7.ResultMap:结果集映射

​ 主要分为一对一和一对多,也就是关联:assocation,集合:collection。当对应的是一个对象用:association,当对应的是Lists:用collection。

1.用法

1.1association
1.2collection

8.第三范式或 BCNF 范式

9.quartZ

​ quartZ就是来完成定时任务的框架,除了框架之外,java本身的用来实现定时任务的有:Timer,和ScheduleExecutorService;同时Timer本身有一定的缺陷,一般推荐使用:ScheduleExecutorService。

1.quartZ的组成

  • 调度器:Scheduler
  • 任务:JobDetail/task
  • 触发器:Trigger,包括SimpleTrigger和CronTrigger
1.job

(1)、Job和JobDetail

​ job是QuartZ中的一个接口,接口下只有excute()方法,在这个方法中编写业务逻辑。

​ JobDetail用来绑定Job,为job实例提供许多属性:

  • name,group
  • jobDataMap
  • jobClass

​ JobDetail绑定指定的Job,每次Scheduler调度执行一个Job的时候,首先会拿到对应的Job,然后创建该Job实例,再去执行Job中的execute()的内容,任务执行结束后,关联的job对象实例会被释放,且会被JVM GC清除。

​ JobDetail+Job的设计:

jobDetail定义的是任务数据,而真正的执行逻辑是在job中;
这是因为任务是有可能并发执行的,如果Scheduler直接使用job,就会存在对同一个job实例并发访问的问题。而jobDetail & Job方式,Scheduler每次执行是,都会根据JobDetail创建一个新的JOb实例,这样就可以规避并发访问的问题。

(2)、JobExecutionContext

​ JobExecutionContext中包含了Quartz运行时的环境以及job本身的详细数据。

​ 当Schedule调度执行一个job的时候,就会将jobExecutionContext传递给该job的execute()中,job就可以通过JobExecutionContext对象获取信息。

 /**
     * cashGather 定时任务job
     * @param jobExecutionContext
     * @throws JobExecutionException
     */
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        cashGatherService.updateStatusAll();
    }

(3)、jobDataMap

​ jobDataMap实现了jdk的map接口,可以以key-value的形式存储数据。

​ jobDetail,Trigger都可以使用jobDataMap来设置一些参数或信息。

2.Trigger

​ Trigger是quartz的触发器,会去通知Scheduler何时去执行对应job。

​ Trigger有SimpleTrigger和CronTrigger

Simpletrigger

​ SimpleTrigger可以实现一个指定时间段内执行一次作业任务或一个时间段内多次执行作业任务。

​ 下面的程序就是实现了程序运行5s后,开始执行job,执行job5s后结束执行。

Date startDate = new Date();
startDate.setTime(startDate.getTime() + 5000);

 Date endDate = new Date();
 endDate.setTime(startDate.getTime() + 5000);

        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .usingJobData("trigger1", "这是jobDetail1的trigger")
                .startNow()//立即生效
                .startAt(startDate)
                .endAt(endDate)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(1)//每隔1s执行一次
                .repeatForever()).build();//一直执行

CronTrigger

​ CronTrigger是基于日历的作业调度,而SimpleTrigger是精准指定间隔,CronTrigger更加常用,是基于Cron表达式的;

​ Cron表达式:7个字段

秒 分 时 日 月 周 年

10.@AliasFor

1.@AliasFor是什么

​ @AliasFor是一个注解,用于为注解属性声明别名。

源码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {

	/**
	 * Alias for {@link #attribute}.
	 * <p>Intended to be used instead of {@link #attribute} when {@link #annotation}
	 * is not declared &mdash; for example: {@code @AliasFor("value")} instead of
	 * {@code @AliasFor(attribute = "value")}.
	 */
	@AliasFor("attribute")
	String value() default "";

	/**
	 * The name of the attribute that <em>this</em> attribute is an alias for.
	 * @see #value
	 */
	@AliasFor("value")
	String attribute() default "";

	/**
	 * The type of annotation in which the aliased {@link #attribute} is declared.
	 * <p>Defaults to {@link Annotation}, implying that the aliased attribute is
	 * declared in the same annotation as <em>this</em> attribute.
	 */
	Class<? extends Annotation> annotation() default Annotation.class;

}

从代码上看:它有两个属性 value 和 attribute ;同时@AliasFor注解还注解了自身的属性,意思是value和attribute互为别名

2.@AliasFor能做什么

1.复合注解

​ 如我们熟知的 @SpringBootApplication:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication {

	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

​ 如上所示:@SpringBootApplication 并没有定义新的属性而是复用其他注解已有的注解属性,并对其进行组合,从而形成新的注解从而达到便捷的目的。这样的注解我们称之为复合注解。

​ 所有我们在使用SpringBoot的时候,只需要@SpringBootApplication 一个注解就能开启:自动配置,自动扫描的功能。

而不再需要使用下面三个注解来达到同样的目的。

​ @Configuration

​ @ComponentScan

​ @EnnableAutoConfiguration


2.继承注解的功能

​ 如@Controller,@Service,@Repository 都继承了@Component的功能,他们的基本作用和@Component完全一样都是用来标明某个类是spring的Bean,需要Spring容器的管理。不同的是对Spring Bean 进行了归类,从而能对不同类型的Bean进行不同的处理。

​ @Service 代码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

	@AliasFor(annotation = Component.class)
	String value() default "";

}

11.@RequestParam @RequestBody @PathVariable

1.@RequestParam

12.ResultMap

ResultMap的三种应用场景,1.只是作为单个resp的结果集映射;2.resp中嵌套了其他的类,一对一的场景;3.resp中嵌套了List等集合,一对多的场景

1.作为结果集映射

resultMap:

​ id:

​ result:

  1. column:数据库的字段
  2. property:类中的字段
<resultMap id="result_base_map" type="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotation">
        <id column="ID" property="id"/>
        <result column="PARTY_ID" property="partyId"/>
        <result column="PARTY_NAME" property="partyName"/>
        <result column="PARTY_TYPE" property="partyType"/>
        <result column="SECURITY_NUM" property="securityNum"/>
        <result column="securityNumAll" property="securityNumAll"/>
    </resultMap>

2.结果集映射一对一

associate:一对一

​ 关联:

  1. property:关联的类的参数名
  2. javaType:关联的类
<resultMap id="result_base_map" type="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotation">
        <id column="ID" property="id"/>
        <result column="PARTY_ID" property="partyId"/>
        <result column="PARTY_NAME" property="partyName"/>
        <result column="PARTY_TYPE" property="partyType"/>
        <result column="SECURITY_NUM" property="securityNum"/>
        <result column="securityNumAll" property="securityNumAll"/>
        //一对1
        <associate property="marketDealQuotationAlls" javaType="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotationAll"
                    ofType="">
            <result column="PARTY_TYPE" property="partyType"/>
            <result column="SECURITY_NUM" property="securityNum"/>
        </associate>

    </resultMap>

3.结果集映射一对多

collection:集合

参数:

  1. property:对应的就是返回结果类中集合的参数名
  2. javaType:集合的类型
  3. ofType:集合存放的类的类型
<resultMap id="result_base_map" type="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotation">
        <id column="ID" property="id"/>
        <result column="PARTY_ID" property="partyId"/>
        <result column="PARTY_NAME" property="partyName"/>
        <result column="PARTY_TYPE" property="partyType"/>
        <result column="SECURITY_NUM" property="securityNum"/>
        <result column="securityNumAll" property="securityNumAll"/>
        //一对多
        <collection property="marketDealQuotationAlls" javaType="list"
                    ofType="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotationAll">
            <result column="PARTY_TYPE" property="partyType"/>
            <result column="SECURITY_NUM" property="securityNum"/>
        </collection>

    </resultMap>

13.map的遍历方式

map的四种遍历方式

  1. 普通使用,二次取值,通过map.keyset()
  2. 通过iterator迭代器遍历循环Map.entrySet().iterator()
  3. 通过Map.entrySet()
  4. 通过Map.values() 遍历所有的values,但是不能遍历key

1.普通使用,二次取值

 HashMap<String, String> map = new HashMap<>();
        map.put("1", "java");
        map.put("2", "vue");
        map.put("3", "linux");
//第一种遍历方式:普通使用,二次取值
        for (String s : map.keySet()) {
            System.out.println("key = " + s + " and value = " + map.get(s));
        }

2.通过iterator迭代器遍历循环

 //第二种遍历方式:通过iterator迭代器遍历循环Map.entrySet().iterator()
        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> next = iterator.next();
            System.out.println("key = "+next.getKey()+" and value "+next.getValue());
        }

3.通过Map.entrySet()

 //第三种遍历方式:通过Map.entrySet()
        for (Map.Entry<String, String> stringStringEntry : map.entrySet()) {
            System.out.println("key = "+stringStringEntry.getKey()+" and value = "+stringStringEntry.getValue());
        }

4.通过Map.values() 遍历所有的values

 //第四种遍历方式:通过Map.values() 遍历所有的values,但是不能遍历key
        for (String value : map.values()) {
            System.out.println("value = "+value);
        }

14.过滤重复值的方式

集合中的数据去重

  1. Stream
  2. Map

1.Stream

使用filter进行过滤,或者使用规约Collectors操作

1.filter

​ 使用filter自定义过滤条件,过滤完毕之后再使用Collectors规约操作转成想要的集合。

​ 自定义过滤条件使用Predicate(断言函数)判断一个元素是否能加入set。

filter

 List<AllMarketQtQuotationTradingResp> collect2 =
                list.stream().filter(dis(AllMarketQtQuotationTradingResp::getPartyId)).collect(Collectors.toList());

        return list2;

predicate

 /**
     * 去重,根据
     * 断言函数,判断一个元素能否加入到set中
     * @param key
     * @param <T>
     * @return
     */
    private static <T> Predicate<T> dis(Function<? super T,?> key) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t->seen.add(key.apply(t));
    }
2.Collectors

使用Collectors中的collectingAndThen(此方法是在规约动作结束之后,对规约的结果进行二次处理

​ 对集合按照自己想要的去重条件去重。

//第一种方法
        ArrayList<AllMarketQtQuotationTradingResp> collect = list.stream().collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(
                                Comparator.comparing(AllMarketQtQuotationTradingResp::getPartyId))), ArrayList::new)
        );

2.使用map

将查询到的数据放到map中,由于map的key的唯一性,所以能够做到去重的效果

map

map.put(key,value);

15.内连接和外连接

数据库:内连接和外连接的区别

使用场景

​ 多表联合查询

1.内连接和外连接的区别

1、内连接

语法:inner join/join

内连接就像是求两个 集合的交集

2、外连接

​ 又分为:左外连接 left out join/left join和 右外连接right out join/right join

​ **左外连接:**左表显示全部,右表符合条件的显示,不符合条件的显示 null

​ **右外连接:**同上

2.交叉连接

​ 语法:

cross join

分为隐式和显式

​ **隐式:**就是不写 cross join

​ **显式:**就是写

3.关于join

​ 当执行join语句时会在内存中产生一个笛卡尔积表,当数据量较大的时候。使用join onwhere产生的笛卡尔积表数据少,相当于优化sql了吧

sql 关键字执行顺序

from  
on  
join  
where  
group by  
having  
select  
distinct  
union  
order by  

16.基本类型和包装类型

基本类型和包装类型的区别

  1. 包装类型是对象,拥有方法和字段,对象的调用都是通过引用对象的地址;基本类型不是
  2. 包装类型是引用的传递;基本类型是值的传递
  3. 声明方式不同:
    • 基本数据类型不需要new关键字
    • 包装类型需要new关键字在堆内存中进行new来分配内存空间
  4. 存储位置不同:
    • 基本数据类型直接将值保存在值栈中;
    • 包装类型是把对象放在堆中,然后通过对象的引用来调用他们
  5. 初始值不同:
    • int的初始值为0,boolean的初始值为false
    • 包装类型的初始值为null
  6. 使用方式不同:
    • 基本数据类型直接赋值使用就好;
    • 包装类型是在集合中如 list map中使用;

理解:

  1. 关于:包装类型是对象,拥有方法和字段不是很理解。
对象就包含属性和方法,比如hashCode、getClass、max、min等。确实,Integerint 确实多了方法。

1.包装类的特征

  1. 功能丰富:包装类本质上是一个对象,对象就包含属性和方法,比如hashCode、getClass、max、min等。确实,Integer 比 int 确实多了方法。

  2. 可定义泛型类型参数

    1. 包装类可以定义泛型,而基本类型不行。

    2. 比如使用Integer定义泛型,List<Integer> list = new ArrayList<>();如果使用int定义就会报错。

    3. 序列化:因为包装类都实现了Serializable接口,所以包装类天然支持序列化和反序列化。

    4. 类型转换

      1. 包装类提供了类型转换的方法,可以很方便的实现类型之间的转换,比如Integer类型的转换;

        Integer.parseInt();
        Integer.ValueOf();
        
    5. 高频区的数据缓存

    各包装类高频区域的取值范围:
    Boolean:使用静态final定义,就会返回静态值;
    Byte:缓冲区 -128~127;
    Short: 缓冲区 -128~127;
    Character: 缓冲区 0~127;
    Long: 缓冲区 -128~127;
    Integer: 缓冲区 -128~127;
    FloatDouble 不会有缓存,其他包装类都有缓存;
    Integer: 是唯一一个可以修改缓存范围的包装类,在vm options 加入参数
    

2.包装类下的== 和 equals

  • 由于包装列高频区间数据缓存的特征,在缓存区内的包装类对象,在第一次创建对象之后,就开辟了内存空间,下次再次出现的时候直接获取使用,超过缓存区间的数据才需要重新创建堆来保存。所以在缓存区间内的包装类使用==的时候返回的是true,超过缓存区间的时候使用==才返回false。
  • 包装类都重写了Object类中的equals方法,重写后的equals是对包装类的对象进行判断,如果是同种类型的才继续判断他们的值是否一样,医用返回true,否则返回false。

17.parse和valueOf的区别

parse和valueOf的区别

1.使用场景

​ 在进行类型转换的时候

2.区别

​ 1.parse()的返回值都为基本类型

​ 2.valueOf返回值都为对应的包装类型,并且valueOf会调用parse();

 public static Double valueOf(String s) throws NumberFormatException {
        return new Double(parseDouble(s));
    }

18.CompareTo

CompareTo方法和Comparetor接口

CompareTo方法

  • 字符串与对象进行比较
  • 按字典顺序比较两个字符串

常用于两个字符串之间的比较

用于字符串比较的时候

​ 使用:

String s1 = "a";
String s2 = "b";
int i = s1.compareTo(s2);
System.out.println(i);
//结果 -1

​ 就是 字符串的ascll码的比较。等于就是 0 ,大于或小于,就是他们的差值。

Comparetor接口

​ 当需要做比较的是两个对象的时候,需要重写 Comparetor接口。

19.java中判断list是否为空

​ ==当我在新增的时候要查一下是否已经存在同样的对象,所以要判断一下查询接口的返回值是否为空,我直接 null ,问题出现了,它一直走不为空的条件,因为 返回结果是list,没有数据list.size() == 0 ,所以判空一直不成立;

1.判空list

if(list == null || list.size == o/list.isEmpty()) {
  //为空的情况
}else {
  //不为空的情况
}

2.list.size == o/list.isEmpty()的区别

​ 没有区别,isEmpty()判断有没有元素,而list.size()返回有几个元素。

​ 如果判断一个集合有无元素,建议使用isEmpty()方法,比较符合逻辑。

3.list == null 和 list.isEmpty()

​ 就相当于,你要到商店买东西。

​ list == null ,首先判断是否有商店。

​ list.isEmpty(),就是判断商店是否有商品。

​ 所以一般的用法是:

if(list == null || list.size == o/list.isEmpty()) {
  //为空的情况
}else {
  //不为空的情况
}

20.@ConditionalOnMissingBean(Default.class)

加上这个注解的类,当Default.class不存在的时候才生效

示例

@ConditionalOnMissingBean(IRiskService.class)

1、当 IRiskService.class 不存在的时候,它才生效。

21.transation rolled back bacause it has been mark rollback-only

简单来说就是事务传播机制引起的问题,在REQUIRED等级下。方法a带有事务,方法b带有事务,a调用b,b发生异常,并捕获了,就会导致这个问题

其他说法

1、当整个方法中每个子方法没报错时,整个方法执行完才提交事务,如果某个子方法有异常,spring将该事务标志未 rollback only,在被标记后和将该异常抛出前,如果继续去操作数据库的话,会报transation rolled back bacause it has been mark rollback-only的异常。如果想要操作数据库的话,重新启动一个独立的事务去处理。

也就是说事务在整个方法执行完成后提交(commit),或者收到子方法往上抛的异常,spring就会捕获异常,并执行回滚。# spring

1.事务传播行为

spring的事务传播行为有7类,

事务传播就是:一个带有事务的方法被调用的时候,他的事务应该怎么处理。

方法A带事务,方法B带事务

  1. require
当方法B被调用时,如果调用它的那个方法带事务,就按照它的事务执行,如果不带就开启一个新的事务

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // do something
}

当执行方法B的时候,发现没有事务,这时就会开启一个事务。

当执行方法A的时候,也没有事务,然后开启一个新事务;再执行方法B的时候,发现有事务,就加入进去。

  1. supports

就是有事务就加入进去,没有事务就以非事务运行。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
// 事务属性为SUPPORTS
@Transactional(propagation = Propagation.SUPPORTS)
public void methodB() {
    // do something
}

单独执行方法B的时候,没有事务就以非事务运行。

在方法A中执行的时候,发现有事务,就加入。

  1. mandatory(强制的)

如果已经存在一个事务,支持当前事务。如果没有活动的事务,则抛出异常。

示例:

@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
 methodB();
// do something
}
 
// 事务属性为MANDATORY
@Transactional(propagation = Propagation.MANDATORY)
public void methodB() {
    // do something
}

当单独调用方法B的时候,由于没有事务,抛出:throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);

当在方法A中执行的时候,存在事务,就加入进去。

  1. require_new

它会开启一个新事务,如果已经存在一个事务,会将已经存在的事务挂起。

  1. not_support

总是以非事务运行,如果存在事务,就将事务挂起

  1. never

总是以非事务方式运行,如果存在一个活动事务,则抛出异常

  1. nested(嵌套)

如果在一个事务中,则运行在嵌套事务中;如果没有活动事务,就按require执行。

2.lombok

@EqualsAndHashCode(callSuper = true)

使用@Data 默认会重写EqualsAndHashCode(callSuper = false)方法 但是 callSuper默认是false,即忽略父类的成员变量。
(callSuper=true) 

3.Transactional

事务:

    @Transactional(rollbackFor = Exception.class,noRollbackFor = BizServiceException.class)

noRollbackFor

意思就是在你声明的这个事务里,如果发生了BizServiceException这个异常就noRollBack,就是数据库事务不发生回滚。

4.BeanUtils

BeanUtils.copy()

作用:方法两个对象之间进行字段值的拷贝

 BeanUtils.copy(from,target);

​ 把from拷贝到target中,条件:拷贝实体类之间只要保证字段名称一样即可。

​ 但是要知道存在三个问题:

1. 字段名必须一样

需要两个拷贝类的属性字段名必须一致,当他们的字段值不一致的时候,需要手动获取并赋值。缺点就是容易忽略。
a.setXX(b.getXX);

2.性能问题

​ BeanUtils内部实现采用的是反射功能,当反射的操作数达到万级别的时候,消耗非常明显。我没试过

3.泛型擦除问题

​ 在进行集合直接拷贝数据时,以为泛型擦除问题,导致拷贝失败。比如我们需要将po集合列表转化为Dto的集合,因为泛型参数问题,最终得到的还是po的集合。当然这个问题我也没遇到过,(我是caiji)。

5.java中的fail-fast(快速失败机制)

1.什么是fail-fast

fail-fast 机制,及快速失败机制,是java集合(Collection)中的一种错误检测机制。当在迭代集合的过程中,该集合在数据结构上发生改变的时候,就有可能发生fail-fast ,即抛出 ConcurrentModificationException异常,fail-fast机制并不保证在不同步的修改下一定会抛出异常,它只是尽最大的努力取抛出,所以这种机制一般仅用于检测bug。

可能出现的场景

​ 在常见的集合 ArrayList,HashMap。在多线程和单线程的环境下,都有可能出现快速失败。

2.为什么会出现fail-fast

 首先:对于集合如:list,map,我们都可以通过迭代器来遍历,而Iterator其实就是一个接口,具体的实现还是要看具体的集合类中的内部类取实现Iterator 并实现相关的方法。以ArrayList为例:当调用list.iterator()时:其源码为:

public Iterator<E> iterator() {
        return new Itr();
    }

即它会返回一个新的Itr类,而Itr类是ArrayList的内部类,实现了Iterator的接口,下面是该类的源码;

/**
     * An optimized version of AbstractList.Itr
     */
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

其中,有三个属性:

int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

cursor是指集合遍历过程中的即将遍历的元素的索引,lastRet是cursor -1,默认为 -1 ,即不存在上一个时,为 -1,它主要用于记录刚刚遍历过的元素的索引。expectedModCount 这个就是fail-fast 判断的关键变量,它初始值就为ArrayList中的modCount。(modCount是抽象类AbstractList中的变量),默认为0 ,而ArrayList继承了AbstractList,所以也有这个变量,modCount用于记录集合操作过程中作的修改次数,与size还是有区别的,并不等于size。

        public boolean hasNext() {
            return cursor != size;
        }

​ 迭代器结束的标志就是hasNext()返回false ,而该方法就是用cursor游标和size(集合中的元素数目)进行对比,当cursor等于size时,标识遍历已经完成。

​ 然后是next()方法,为什么在迭代的过程中,如果有线程对集合的结构做出改变,就会发生fail-fast

       @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

​ 从源码可以看到,每次调用next()方法的时候,在实际访问元素前,都会调用 checkForComodification()方法,

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

​ 可以看出,该方法才是判断是否抛出 ConcurrentModificationException 异常的关键。在该段代码中,当 modCount != expectModCount 时,就会抛出该异常。但是在一开始的时候,expectionModCount 初始值默认等于 modCount,为什么会出现 modCount !=expectModCount,很明显expectedModCount 在整个迭代过程中,除了一开始赋予初始值modCount 外,并没有发生该表,所以可能发生该表的值是 modCount,在前面关于ArrayList 扩容机制的分析中,就可以知道 ArrayList 进行 add,remove,clear 等涉及修改集合中的元素的个数的操作时,modCount 就会发生改变(modCount++),所以当另一个线程(并发修改)或者同一个线程遍历的过程中,调用相关方法使集合的个数方法改变,就会使modCount发生变化,这样在 checkForComfdification 方法中 就会抛出ConcurrentModificationException 异常。

3.如何避免fail-fast

​ 在了解了fail-fast的产生原理之后,接下来就看看如何解决。

方法1

​ 在单线程的遍历过程中,如果要进行remove操作,可以使用迭代器的remove方法而不是集合类的remove方法。下面是ArrayList 中 迭代器remove方法的源码:

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();
 
            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

​ 可以看到,该remove方法并不会修改modCount 的值,并且不会对后面remove不能指定元素,只能remove当前遍历过的那个元素,所以调用该方法并不会发生fail-fast现象。该方法有局限性:

例子:

 public static void main(String[] args) {
        //ConcurrentHashMap:并发容器
        Map<Object, Object> map = new HashMap<>();
        for (int i = 0; i < 10; i++) {
            map.put(i+"",+i+"");
        }
        Iterator<Map.Entry<Object, Object>> iterator = map.entrySet().iterator();
        int i = 0;
        while (iterator.hasNext()) {
            if (i == 3) {
                map.remove(3+"");
                iterator.remove();
            }
            Map.Entry<Object, Object> next = iterator.next();
            System.out.println("key:"+next.getKey()+" and value="+next.getValue());
            i++;
        }
    }

方法2

​ 就是使用并发包中的并发容器。

​ 比如使用CopyOnWriteArrayList 代替 ArrayList,CopyOnWriteArrayList 在使用上跟ArrayList 几乎一样,CopyOnWrite 是写时复制的容器(COW),在读写时是线程安全的。该容器在对add和remove等操作时,并不是在原数组上进行修改的,而是将原数组拷贝一份,在新数组上进行修改,待完成后,才将指向旧数组的指引指向新数组,所以对于CopyOnWriterArrayList 在迭代过程并不会发生fail-fast现象。但CopyOnWrite容器只能保证数据的最终一致性,不能保证数据的实时一致性。

​ 对于HashMap,可以使用ConcurrentHashMap,ConcurrentHashMap使用了锁机制,是线程安全的,在迭代方法,ConcurrentHashMap使用了一种不同的迭代方法。在这种迭代方式中,当iterator被创建后集合再发生改变就不在抛出ConcurrentModificaitionExecption,取而代之的是再改变时new新的数据从而不影响原有的数据,iterator 完成后再将头指针替换为新的数据,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。即迭代不会发生fail-fast,但不保证获取的是最新的数据。(即弱一致性)。

6.@Transactional

:dog:关于@Transactional (事务),我就想知道默认的(没有任何参数的),是什么一种效果;@Transactional 作用在实现类的方法上(具体点就是增删改方法上),当遇到 RuntimeException (也就是运行时异常,关于异常的分类,会放在最后进行讲解)异常的时候,方法发生回滚(数据回到执行方法之前)。 

1.@Transactional的参数

属性类型描述
valueString可选的限定描述符,指定使用的事务管理器
propagationenum:Propagation可选的事务传播行为设置
isolationenum:Isolation可选的事务隔离级别设置
readOnlyboolean读写或只读事务,默认读写
timeOutint(in seconds granularity)事务超时时间设置
rollbackForClass对象数组,必须继承自Throwable导致事务回滚的异常类数组
rollbackForClassName类名数组,必须继承自Throwable导致事务回滚的异常类名字数组
noRollbackForClass对象数组,必须继承自Throwable不会导致事务回滚的异常类数组
noRollbackForClassName类名数组,必须继承自Throwable不会导致事务回滚的异常类名字数组

这些参数我用过的也就 readOnly(读写或只读事务)rollbackFor(会回滚的异常类),noRollbackFor(不会回滚的异常类)

然后我觉的比较难理解的就是 propagation(事务传播)isolation(隔离级别)

2. 隔离级别

3.异常

7.ResultMap:结果集映射

​ 主要分为一对一和一对多,也就是关联:assocation,集合:collection。当对应的是一个对象用:association,当对应的是Lists:用collection。

1.用法

1.1association
1.2collection

8.第三范式或 BCNF 范式

9.quartZ

​ quartZ就是来完成定时任务的框架,除了框架之外,java本身的用来实现定时任务的有:Timer,和ScheduleExecutorService;同时Timer本身有一定的缺陷,一般推荐使用:ScheduleExecutorService。

1.quartZ的组成

  • 调度器:Scheduler
  • 任务:JobDetail/task
  • 触发器:Trigger,包括SimpleTrigger和CronTrigger
1.job

(1)、Job和JobDetail

​ job是QuartZ中的一个接口,接口下只有excute()方法,在这个方法中编写业务逻辑。

​ JobDetail用来绑定Job,为job实例提供许多属性:

  • name,group
  • jobDataMap
  • jobClass

​ JobDetail绑定指定的Job,每次Scheduler调度执行一个Job的时候,首先会拿到对应的Job,然后创建该Job实例,再去执行Job中的execute()的内容,任务执行结束后,关联的job对象实例会被释放,且会被JVM GC清除。

​ JobDetail+Job的设计:

jobDetail定义的是任务数据,而真正的执行逻辑是在job中;
这是因为任务是有可能并发执行的,如果Scheduler直接使用job,就会存在对同一个job实例并发访问的问题。而jobDetail & Job方式,Scheduler每次执行是,都会根据JobDetail创建一个新的JOb实例,这样就可以规避并发访问的问题。

(2)、JobExecutionContext

​ JobExecutionContext中包含了Quartz运行时的环境以及job本身的详细数据。

​ 当Schedule调度执行一个job的时候,就会将jobExecutionContext传递给该job的execute()中,job就可以通过JobExecutionContext对象获取信息。

 /**
     * cashGather 定时任务job
     * @param jobExecutionContext
     * @throws JobExecutionException
     */
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        cashGatherService.updateStatusAll();
    }

(3)、jobDataMap

​ jobDataMap实现了jdk的map接口,可以以key-value的形式存储数据。

​ jobDetail,Trigger都可以使用jobDataMap来设置一些参数或信息。

2.Trigger

​ Trigger是quartz的触发器,会去通知Scheduler何时去执行对应job。

​ Trigger有SimpleTrigger和CronTrigger

Simpletrigger

​ SimpleTrigger可以实现一个指定时间段内执行一次作业任务或一个时间段内多次执行作业任务。

​ 下面的程序就是实现了程序运行5s后,开始执行job,执行job5s后结束执行。

Date startDate = new Date();
startDate.setTime(startDate.getTime() + 5000);

 Date endDate = new Date();
 endDate.setTime(startDate.getTime() + 5000);

        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
                .usingJobData("trigger1", "这是jobDetail1的trigger")
                .startNow()//立即生效
                .startAt(startDate)
                .endAt(endDate)
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                .withIntervalInSeconds(1)//每隔1s执行一次
                .repeatForever()).build();//一直执行

CronTrigger

​ CronTrigger是基于日历的作业调度,而SimpleTrigger是精准指定间隔,CronTrigger更加常用,是基于Cron表达式的;

​ Cron表达式:7个字段

秒 分 时 日 月 周 年

10.@AliasFor

1.@AliasFor是什么

​ @AliasFor是一个注解,用于为注解属性声明别名。

源码

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface AliasFor {

	/**
	 * Alias for {@link #attribute}.
	 * <p>Intended to be used instead of {@link #attribute} when {@link #annotation}
	 * is not declared &mdash; for example: {@code @AliasFor("value")} instead of
	 * {@code @AliasFor(attribute = "value")}.
	 */
	@AliasFor("attribute")
	String value() default "";

	/**
	 * The name of the attribute that <em>this</em> attribute is an alias for.
	 * @see #value
	 */
	@AliasFor("value")
	String attribute() default "";

	/**
	 * The type of annotation in which the aliased {@link #attribute} is declared.
	 * <p>Defaults to {@link Annotation}, implying that the aliased attribute is
	 * declared in the same annotation as <em>this</em> attribute.
	 */
	Class<? extends Annotation> annotation() default Annotation.class;

}

从代码上看:它有两个属性 value 和  attribute ;同时@AliasFor注解还注解了自身的属性,意思是value和attribute互为别名

2.@AliasFor能做什么

1.复合注解

​ 如我们熟知的 @SpringBootApplication:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
@ConfigurationPropertiesScan
public @interface SpringBootApplication {

	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

	@AliasFor(annotation = Configuration.class)
	boolean proxyBeanMethods() default true;

}

​ 如上所示:@SpringBootApplication 并没有定义新的属性而是复用其他注解已有的注解属性,并对其进行组合,从而形成新的注解从而达到便捷的目的。这样的注解我们称之为复合注解。

​ 所有我们在使用SpringBoot的时候,只需要@SpringBootApplication 一个注解就能开启:自动配置,自动扫描的功能。

而不再需要使用下面三个注解来达到同样的目的。

​ @Configuration

​ @ComponentScan

​ @EnnableAutoConfiguration


2.继承注解的功能

​ 如@Controller,@Service,@Repository 都继承了@Component的功能,他们的基本作用和@Component完全一样都是用来标明某个类是spring的Bean,需要Spring容器的管理。不同的是对Spring Bean 进行了归类,从而能对不同类型的Bean进行不同的处理。

​ @Service 代码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

	@AliasFor(annotation = Component.class)
	String value() default "";

}

11.@RequestParam @RequestBody @PathVariable

1.@RequestParam

12.ResultMap

ResultMap的三种应用场景,1.只是作为单个resp的结果集映射;2.resp中嵌套了其他的类,一对一的场景;3.resp中嵌套了List等集合,一对多的场景

1.作为结果集映射

resultMap:

​ id:

​ result:

  1. column:数据库的字段
  2. property:类中的字段
<resultMap id="result_base_map" type="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotation">
        <id column="ID" property="id"/>
        <result column="PARTY_ID" property="partyId"/>
        <result column="PARTY_NAME" property="partyName"/>
        <result column="PARTY_TYPE" property="partyType"/>
        <result column="SECURITY_NUM" property="securityNum"/>
        <result column="securityNumAll" property="securityNumAll"/>
    </resultMap>

2.结果集映射一对一

associate:一对一

​ 关联:

  1. property:关联的类的参数名
  2. javaType:关联的类
<resultMap id="result_base_map" type="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotation">
        <id column="ID" property="id"/>
        <result column="PARTY_ID" property="partyId"/>
        <result column="PARTY_NAME" property="partyName"/>
        <result column="PARTY_TYPE" property="partyType"/>
        <result column="SECURITY_NUM" property="securityNum"/>
        <result column="securityNumAll" property="securityNumAll"/>
        //一对1
        <associate property="marketDealQuotationAlls" javaType="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotationAll"
                    ofType="">
            <result column="PARTY_TYPE" property="partyType"/>
            <result column="SECURITY_NUM" property="securityNum"/>
        </associate>

    </resultMap>

3.结果集映射一对多

collection:集合

参数:

  1. property:对应的就是返回结果类中集合的参数名
  2. javaType:集合的类型
  3. ofType:集合存放的类的类型
<resultMap id="result_base_map" type="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotation">
        <id column="ID" property="id"/>
        <result column="PARTY_ID" property="partyId"/>
        <result column="PARTY_NAME" property="partyName"/>
        <result column="PARTY_TYPE" property="partyType"/>
        <result column="SECURITY_NUM" property="securityNum"/>
        <result column="securityNumAll" property="securityNumAll"/>
        //一对多
        <collection property="marketDealQuotationAlls" javaType="list"
                    ofType="com.hengtiansoft.oms.evaluate.dto.resp.MarketDealQuotationAll">
            <result column="PARTY_TYPE" property="partyType"/>
            <result column="SECURITY_NUM" property="securityNum"/>
        </collection>

    </resultMap>

13.map的遍历方式

map的四种遍历方式

  1. 普通使用,二次取值,通过map.keyset()
  2. 通过iterator迭代器遍历循环Map.entrySet().iterator()
  3. 通过Map.entrySet()
  4. 通过Map.values() 遍历所有的values,但是不能遍历key

1.普通使用,二次取值

 HashMap<String, String> map = new HashMap<>();
        map.put("1", "java");
        map.put("2", "vue");
        map.put("3", "linux");
//第一种遍历方式:普通使用,二次取值
        for (String s : map.keySet()) {
            System.out.println("key = " + s + " and value = " + map.get(s));
        }

2.通过iterator迭代器遍历循环

 //第二种遍历方式:通过iterator迭代器遍历循环Map.entrySet().iterator()
        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> next = iterator.next();
            System.out.println("key = "+next.getKey()+" and value "+next.getValue());
        }

3.通过Map.entrySet()

 //第三种遍历方式:通过Map.entrySet()
        for (Map.Entry<String, String> stringStringEntry : map.entrySet()) {
            System.out.println("key = "+stringStringEntry.getKey()+" and value = "+stringStringEntry.getValue());
        }

4.通过Map.values() 遍历所有的values

 //第四种遍历方式:通过Map.values() 遍历所有的values,但是不能遍历key
        for (String value : map.values()) {
            System.out.println("value = "+value);
        }

14.过滤重复值的方式

集合中的数据去重

  1. Stream
  2. Map

1.Stream

使用filter进行过滤,或者使用规约Collectors操作

1.filter

​ 使用filter自定义过滤条件,过滤完毕之后再使用Collectors规约操作转成想要的集合。

​ 自定义过滤条件使用Predicate(断言函数)判断一个元素是否能加入set。

filter

 List<AllMarketQtQuotationTradingResp> collect2 =
                list.stream().filter(dis(AllMarketQtQuotationTradingResp::getPartyId)).collect(Collectors.toList());

        return list2;

predicate

 /**
     * 去重,根据
     * 断言函数,判断一个元素能否加入到set中
     * @param key
     * @param <T>
     * @return
     */
    private static <T> Predicate<T> dis(Function<? super T,?> key) {
        Set<Object> seen = ConcurrentHashMap.newKeySet();
        return t->seen.add(key.apply(t));
    }
2.Collectors

使用Collectors中的collectingAndThen(此方法是在规约动作结束之后,对规约的结果进行二次处理

​ 对集合按照自己想要的去重条件去重。

//第一种方法
        ArrayList<AllMarketQtQuotationTradingResp> collect = list.stream().collect(
                Collectors.collectingAndThen(
                        Collectors.toCollection(() -> new TreeSet<>(
                                Comparator.comparing(AllMarketQtQuotationTradingResp::getPartyId))), ArrayList::new)
        );

2.使用map

将查询到的数据放到map中,由于map的key的唯一性,所以能够做到去重的效果

map

map.put(key,value);

15.内连接和外连接

数据库:内连接和外连接的区别

使用场景

​ 多表联合查询

1.内连接和外连接的区别

1、内连接

语法:inner join/join

内连接就像是求两个 集合的交集

2、外连接

​ 又分为:左外连接 left out join/left join和 右外连接right out join/right join

​ **左外连接:**左表显示全部,右表符合条件的显示,不符合条件的显示 null

​ **右外连接:**同上

2.交叉连接

​ 语法:

cross join

分为隐式和显式

​ **隐式:**就是不写 cross join

​ **显式:**就是写

3.关于join

​ 当执行join语句时会在内存中产生一个笛卡尔积表,当数据量较大的时候。使用join onwhere产生的笛卡尔积表数据少,相当于优化sql了吧

sql 关键字执行顺序

from  
on  
join  
where  
group by  
having  
select  
distinct  
union  
order by  

16.基本类型和包装类型

基本类型和包装类型的区别

  1. 包装类型是对象,拥有方法和字段,对象的调用都是通过引用对象的地址;基本类型不是
  2. 包装类型是引用的传递;基本类型是值的传递
  3. 声明方式不同:
    • 基本数据类型不需要new关键字
    • 包装类型需要new关键字在堆内存中进行new来分配内存空间
  4. 存储位置不同:
    • 基本数据类型直接将值保存在值栈中;
    • 包装类型是把对象放在堆中,然后通过对象的引用来调用他们
  5. 初始值不同:
    • int的初始值为0,boolean的初始值为false
    • 包装类型的初始值为null
  6. 使用方式不同:
    • 基本数据类型直接赋值使用就好;
    • 包装类型是在集合中如 list map中使用;

理解:

  1. 关于:包装类型是对象,拥有方法和字段不是很理解。
对象就包含属性和方法,比如hashCode、getClass、max、min等。确实,Integerint 确实多了方法。

1.包装类的特征

  1. 功能丰富:包装类本质上是一个对象,对象就包含属性和方法,比如hashCode、getClass、max、min等。确实,Integer 比 int 确实多了方法。

  2. 可定义泛型类型参数

    1. 包装类可以定义泛型,而基本类型不行。

    2. 比如使用Integer定义泛型,List<Integer> list = new ArrayList<>();如果使用int定义就会报错。

    3. 序列化:因为包装类都实现了Serializable接口,所以包装类天然支持序列化和反序列化。

    4. 类型转换

      1. 包装类提供了类型转换的方法,可以很方便的实现类型之间的转换,比如Integer类型的转换;

        Integer.parseInt();
        Integer.ValueOf();
        
    5. 高频区的数据缓存

    各包装类高频区域的取值范围:
    Boolean:使用静态final定义,就会返回静态值;
    Byte:缓冲区 -128~127;
    Short: 缓冲区 -128~127;
    Character: 缓冲区 0~127;
    Long: 缓冲区 -128~127;
    Integer: 缓冲区 -128~127;
    FloatDouble 不会有缓存,其他包装类都有缓存;
    Integer: 是唯一一个可以修改缓存范围的包装类,在vm options 加入参数
    

2.包装类下的== 和 equals

  • 由于包装列高频区间数据缓存的特征,在缓存区内的包装类对象,在第一次创建对象之后,就开辟了内存空间,下次再次出现的时候直接获取使用,超过缓存区间的数据才需要重新创建堆来保存。所以在缓存区间内的包装类使用==的时候返回的是true,超过缓存区间的时候使用==才返回false。
  • 包装类都重写了Object类中的equals方法,重写后的equals是对包装类的对象进行判断,如果是同种类型的才继续判断他们的值是否一样,医用返回true,否则返回false。

17.parse和valueOf的区别

parse和valueOf的区别

1.使用场景

​ 在进行类型转换的时候

2.区别

​ 1.parse()的返回值都为基本类型

​ 2.valueOf返回值都为对应的包装类型,并且valueOf会调用parse();

 public static Double valueOf(String s) throws NumberFormatException {
        return new Double(parseDouble(s));
    }

18.CompareTo

CompareTo方法和Comparetor接口

CompareTo方法

  • 字符串与对象进行比较
  • 按字典顺序比较两个字符串

常用于两个字符串之间的比较

用于字符串比较的时候

​ 使用:

String s1 = "a";
String s2 = "b";
int i = s1.compareTo(s2);
System.out.println(i);
//结果 -1

​ 就是 字符串的ascll码的比较。等于就是 0 ,大于或小于,就是他们的差值。

Comparetor接口

​ 当需要做比较的是两个对象的时候,需要重写 Comparetor接口。

19.java中判断list是否为空

​ ==当我在新增的时候要查一下是否已经存在同样的对象,所以要判断一下查询接口的返回值是否为空,我直接 null ,问题出现了,它一直走不为空的条件,因为 返回结果是list,没有数据list.size() == 0 ,所以判空一直不成立;

1.判空list

if(list == null || list.size == o/list.isEmpty()) {
  //为空的情况
}else {
  //不为空的情况
}

2.list.size == o/list.isEmpty()的区别

​ 没有区别,isEmpty()判断有没有元素,而list.size()返回有几个元素。

​ 如果判断一个集合有无元素,建议使用isEmpty()方法,比较符合逻辑。

3.list == null 和 list.isEmpty()

​ 就相当于,你要到商店买东西。

​ list == null ,首先判断是否有商店。

​ list.isEmpty(),就是判断商店是否有商品。

​ 所以一般的用法是:

if(list == null || list.size == o/list.isEmpty()) {
  //为空的情况
}else {
  //不为空的情况
}

20.@ConditionalOnMissingBean(Default.class)

加上这个注解的类,当Default.class不存在的时候才生效

示例

@ConditionalOnMissingBean(IRiskService.class)

1、当 IRiskService.class 不存在的时候,它才生效。

21.transation rolled back bacause it has been mark rollback-only

简单来说就是事务传播机制引起的问题,在REQUIRED等级下。方法a带有事务,方法b带有事务,a调用b,b发生异常,并捕获了,就会导致这个问题

其他说法

1、当整个方法中每个子方法没报错时,整个方法执行完才提交事务,如果某个子方法有异常,spring将该事务标志未 rollback only,在被标记后和将该异常抛出前,如果继续去操作数据库的话,会报transation rolled back bacause it has been mark rollback-only的异常。如果想要操作数据库的话,重新启动一个独立的事务去处理。

也就是说事务在整个方法执行完成后提交(commit),或者收到子方法往上抛的异常,spring就会捕获异常,并执行回滚。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值