今日笔记0810

mybatis一对多有几种写法,各有什么优缺点

嵌套查询(Nested Query):

优点:简单易懂,可读性好;适用于关联表数据量不大的情况。
缺点:存在 N+1 查询问题,当关联数据量较大时,会产生大量的查询语句,影响性能。
嵌套结果映射(Nested Result Maps):

优点:通过定义嵌套的 resultMap 实现一次查询加载所有关联数据,避免了 N+1 查询问题。
缺点:配置相对复杂,需要额外定义 resultMap。
延迟加载(Lazy Loading):

优点:在查询主对象时,只查询主对象的数据,当访问关联对象时才触发关联对象的查询,节省了数据库查询开销。
缺点:需要在关联对象上使用延迟加载注解或配置,增加了代码和配置的复杂性。
前向延迟加载(Eager Loading with SubSelect):

优点:通过使用子查询一次性加载所有关联对象的数据,避免了 N+1 查询问题。
缺点:由于使用了子查询,可能会产生较大的 SQL 查询语句,执行效率较低。
关联查询(Join Query):

优点:通过使用关联查询一次性加载主对象及其关联对象的数据,避免了 N+1 查询问题。
缺点:需要手动编写关联查询语句,增加了 SQL 编写的复杂性。

对自旋锁的理解

自旋锁在不同语言都有不同的实现,但核心逻辑都是一样的,你可以看做是一个死循环去判断锁变量是否可用,如果可用则跳出循环,否则继续死循环。
自旋锁必须基于CPU的数据总线锁定,它通过读取一个内存单元(spinlock_t)来判断这个自旋锁是否已经被别的CPU锁住。如果否,它写进一个特定值,表示锁定了总线,然后返回。如果是,它会重复以上操作直到成功,或者spin次数超过一个设定值。记住上面提及到的:锁定数据总线的指令只能保证一个指令操作期间CPU独占数据总线。(自旋锁在锁定的时侯,不会睡眠而是会持续地尝试)。其作用是为了解决某项资源的互斥使用。因为自旋锁不会引起调用者睡眠,所以自旋锁的效率远高于互斥锁。虽然自旋锁的效率比互斥锁高,但是它也有些不足之处:

1.自旋锁一直占用CPU,它在未获得锁的情况下,一直运行——自旋,所以占用着CPU,如果不能在很短的时间内获得锁,这无疑会使CPU效率降低。

2.在用自旋锁时有可能造成死锁,当递归调用时有可能造成死锁,调用有些其他函数(如copy_to_user()、copy_from_user()、kmalloc()等)也可能造成死锁。
因此我们要慎重使用自旋锁,自旋锁只有在内核可抢占式或SMP的情况下才真正需要,在单CPU且不可抢占式的内核下,自旋锁的操作为空操作。自旋锁适用于锁使用者保持锁时间比较短的情况。

红黑树是标准二叉树吗?为什么要转红黑树,优势在哪里,什么办法保证了这个优势

红黑树相对于其他平衡二叉搜索树(如AVL树)的优势在于插入、删除和查找等操作的性能较好,并且能够保持树的平衡性。

红黑树的优势主要体现在以下几个方面:

平衡性:红黑树通过一系列的规则来保持树的平衡,从而确保树的高度保持在较小的范围内。这使得插入、删除和查找等操作的时间复杂度能够保持在O(log n)级别,其中n是树中节点的数量。与其他平衡二叉搜索树相比,红黑树的平衡性相对较弱,但它的平衡调整操作更简单,性能更高。

灵活性:红黑树支持高效的动态更新,即可以在树中高效地执行插入和删除操作。通过旋转和颜色变换等操作,红黑树能够保持平衡,使树的结构尽量保持均衡而不需要频繁的平衡调整。

应用广泛:红黑树广泛应用于各种数据结构和算法中,例如在C++的STL库中的set和map容器的实现中就使用了红黑树。此外,红黑树还被用于实现各种高效的算法,如各种图算法、线程调度等。

为了保证红黑树的优势,需要遵循以下几个原则:

节点颜色规则:每个节点要么是红色,要么是黑色。
根节点规则:根节点必须是黑色的。
叶子节点规则:每个叶子节点(NIL节点,空节点)都是黑色的。
红色节点规则:如果一个节点是红色的,则它的两个子节点都是黑色的。
黑色节点规则:从任意节点到其每个叶子节点的路径上包含相同数量的黑色节点。
插入规则:新插入的节点默认为红色,并通过旋转和颜色变换等操作来维持红黑树的平衡性。
删除规则:删除节点时,通过旋转和颜色变换等操作来维持红黑树的平衡性,并保持其他节点的特性。
通过遵循以上规则,红黑树能够保持平衡性并提供较好的性能。

ArrayList 底层数组,那数组的动态扩容,扩容算法,1.7 与1.8 有什么不同,为什么要这样设计

ArrayList底层使用数组来存储元素。当ArrayList的容量不足以容纳新元素时,它会自动进行动态扩容。

在Java 1.7版本中,ArrayList的扩容算法是采用增加固定大小的策略。具体而言,当需要扩容时,它会创建一个新的数组,长度是当前数组的1.5倍(通过int newCapacity = oldCapacity + (oldCapacity >> 1)计算)。然后,它将原来数组中的元素复制到新数组中。

而在Java 1.8版本中,ArrayList的扩容算法发生了改变。它引入了更加智能的扩容算法,称为"倍增"(Doubling)策略。具体来说,当需要扩容时,它会创建一个新的数组,长度是当前数组的两倍(通过int newCapacity = oldCapacity + (oldCapacity >> 1)计算)。然后,它将原来数组中的元素复制到新数组中。

Java 1.8版本中的这种改变是为了优化ArrayList的性能。相比于1.7版本,倍增策略可以减少扩容次数,从而提高插入操作的效率。由于数组长度的增长是指数级的,所以平均而言,ArrayList会有更少的扩容操作。

总结起来,Java 1.8版本中的ArrayList使用倍增策略进行动态扩容,相比于1.7版本的固定增量策略,可以减少扩容次数,提高插入操作的效率。这种设计是为了优化ArrayList的性能和内存利用率。

MySQL多进程加载如何保证操作事务一致性

在MySQL中,多进程加载数据时,为了保证操作事务的一致性,可以采用以下几种方式:

1. 使用事务:在多进程加载数据时,每个进程可以独立开启一个事务,并在操作结束时提交事务。通过事务的隔离级别和锁机制,可以确保多个进程间的操作不会相互干扰,并保证数据的一致性。

2. 锁机制:在多进程加载数据时,可以使用锁机制来保证操作的互斥性和一致性。例如,可以使用行级锁或表级锁来控制并发访问。在加载数据时,对需要访问的数据进行加锁,确保在某个进程正在修改数据时,其他进程无法同时修改相同的数据。

3. 数据分片:将要加载的数据分成多个片段,每个进程负责加载其中的一部分数据。这样可以降低并发加载时的冲突概率,并提高加载效率。在加载过程中,可以使用分布式事务管理机制来协调多个进程之间的操作,保证整体的一致性。

4. 合理设计操作逻辑:在多进程加载数据时,需要合理设计操作逻辑,避免产生冲突或数据丢失。例如,可以使用乐观锁机制或悲观锁机制来处理并发问题,确保数据的一致性。

需要注意的是,多进程加载数据时可能存在并发冲突和数据竞争的问题,因此在设计和实现时需要仔细考虑,并且合理选择合适的技术手段来解决这些问题。同时,根据具体情况选择事务隔离级别、锁机制等策略也是很重要的。

乐观锁机制或悲观锁机制

乐观锁机制和悲观锁机制是数据库并发控制的两种常见策略,用于处理多个事务同时对同一数据进行读写操作时可能出现的并发冲突。

1. 乐观锁机制:
   - 原理:乐观锁机制假设并发冲突的可能性较低,因此在读取数据时不加锁,而是在更新数据时进行冲突检测。
   - 实现方式:通常使用版本号或时间戳来标识数据的版本,在更新数据时检查版本是否发生变化,若变化则认为有冲突发生。
   - 实际应用:乐观锁适用于并发冲突较少的情况,可以提高系统的吞吐量和性能。
   - 优点:减少了锁的竞争,提高了并发性能;实现简单,没有额外的锁开销。
   - 缺点:无法解决高并发下的严重冲突问题;需要应用代码进行版本检测,增加了开发复杂性。

2. 悲观锁机制:
   - 原理:悲观锁机制假设并发冲突的可能性较高,因此在读取数据时会先加上锁,确保其他事务无法修改该数据,直到当前事务完成。
   - 实现方式:数据库中的行锁或表锁是常见的悲观锁实现方式,也可以使用数据库提供的锁机制如 SELECT FOR UPDATE。
   - 实际应用:悲观锁适用于并发冲突较多的情况,能够确保数据的一致性和完整性。
   - 优点:能够有效地避免并发冲突,保证数据的一致性;适用于高并发写入场景。
   - 缺点:加锁会限制并发度,可能导致性能下降;容易出现死锁或长时间等待的问题。

选择乐观锁机制还是悲观锁机制取决于具体业务场景和并发冲突的程度。若并发冲突较少且性能要求较高,可选择乐观锁机制;若并发冲突较多且数据一致性要求较高,可选择悲观锁机制。在实际应用中,也可以结合两者,根据具体情况选用不同的并发控制策略。

HashMap默认多大size

在Java中,HashMap的默认初始容量是16,即默认大小为16个桶(buckets)。这是在创建HashMap对象时,如果没有显式指定初始容量的情况下的默认值。

另外,HashMap还有一个默认的加载因子(load factor),即负载因子,默认值为0.75。负载因子表示HashMap在达到容量与大小之比时开始扩容。当HashMap中的元素个数超过容量乘以负载因子时,将进行扩容操作。

需要注意的是,HashMap的容量并不是固定的,它会根据实际情况进行动态调整。在每次进行扩容时,HashMap的容量会翻倍。这是为了提高HashMap的性能,减少哈希冲突的概率。

总结起来,HashMap的默认初始容量是16,负载因子是0.75。当HashMap中的元素个数超过容量乘以负载因子时,将进行动态扩容。你也可以通过构造函数来显式指定初始容量和负载因子。

java 基础数据类型

在Java中,有以下基础数据类型(Primitive Data Types):

1. 整数类型:
   - byte: 8位有符号整数,范围为-128到127。
   - short: 16位有符号整数,范围为-32,768到32,767。
   - int: 32位有符号整数,范围为-2,147,483,648到2,147,483,647。
   - long: 64位有符号整数,范围为-9,223,372,036,854,775,808到9,223,372,036,854,775,807。

2. 浮点类型:
   - float: 32位单精度浮点数,范围约为±3.40282347E+38F,精度为6-7位小数。
   - double: 64位双精度浮点数,范围约为±1.79769313486231570E+308,精度为15位小数。

3. 布尔类型:
   - boolean: 只有两个值,true和false。

4. 字符类型:
   - char: 单个16位Unicode字符,范围为'\u0000'到'\uffff'。

这些基础数据类型是Java语言的构造块,用于存储不同类型的数据。它们具有不同的大小和范围,可以根据实际需求选择合适的数据类型来存储数据。除了基础数据类型外,Java还提供了包装类(Wrapper Classes)来处理基础数据类型的对象表示形式和操作。

arraylist和LinkedList的区别

ArrayList和LinkedList是Java中常用的两种集合类型,它们在实现上有一些区别。

1. 数据结构:
   - ArrayList:内部使用数组实现,元素在内存中是连续存储的。它可以随机访问元素,根据索引快速定位元素。
   - LinkedList:内部使用双向链表实现,每个元素包含对前后元素的引用。它不能像ArrayList那样直接根据索引定位元素,需要从头或尾开始遍历链表,直到找到目标位置。

2. 插入和删除操作:
   - ArrayList:插入和删除元素时,涉及到数组元素的移动操作,特别是在数组的开头或中间插入/删除元素时,需要将后续的元素进行位移。这可能会导致性能下降。
   - LinkedList:插入和删除元素时,只需要改变相邻节点的引用,不需要移动其他元素。因此,在链表的开头或中间执行插入/删除操作时,LinkedList的性能更好。

3. 访问效率:
   - ArrayList:由于其中的元素在内存中是连续存储的,并且可以根据索引直接访问,因此随机访问的效率很高。
   - LinkedList:由于需要遍历链表来查找元素,因此访问效率比ArrayList低。但是,在链表的开头和结尾进行操作时,LinkedList的性能较好。

根据上述特点,可以根据实际需求选择合适的集合类型。如果需要快速随机访问元素或有大量的索引操作,使用ArrayList更合适。如果需要频繁执行插入和删除操作,特别是在集合的开头或中间进行操作,LinkedList可能更适合。

hashMap和Hashtable的区别

HashMap和Hashtable都是Java中用于存储键值对的集合类,它们在实现上有以下区别:

1. 线程安全性:
   - HashMap:HashMap是非线程安全的,它不提供内置的同步机制。在多线程环境下,如果多个线程同时对HashMap进行修改,可能会导致数据不一致或产生竞态条件。
   - Hashtable:Hashtable是线程安全的,它通过使用synchronized关键字来保证同时只有一个线程访问Hashtable的方法。这种同步机制会引入一定的性能开销。

2. null值:
   - HashMap:HashMap允许键和值都为null。也就是说,可以将null作为键或值存储在HashMap中。
   - Hashtable:Hashtable不允许键或值为null。如果试图将null键或值放入Hashtable中,将会抛出NullPointerException。

3. 继承关系:
   - HashMap:HashMap继承自AbstractMap类,并且实现了Map接口。
   - Hashtable:Hashtable继承自Dictionary类,并且实现了Map接口。Dictionary类已被弃用,推荐使用Map接口。

4. 性能:
   - HashMap:HashMap相对于Hashtable在性能上更优。由于Hashtable提供了同步机制,执行许多操作时需要进行同步,而HashMap不需要这样的开销。因此,在单线程环境下,HashMap的性能通常比Hashtable更好。

5. 迭代器:
   - HashMap:HashMap的迭代器(Iterator)是fail-fast的。如果在迭代过程中对HashMap进行修改,将会抛出ConcurrentModificationException异常。
   - Hashtable:Hashtable的迭代器是不fail-fast的,它不会检测并发修改。

总的来说,HashMap更常用,它在大多数情况下提供了更好的性能和灵活性。Hashtable仍然可以在需要线程安全的环境中使用,但在单线程环境下,建议使用HashMap。此外,请注意HashMap和Hashtable在API和用法上也存在一些差异。

抽象类和接口的区别

抽象类和接口是面向对象编程中的两个重要概念,它们有一些区别和特点,下面是它们的主要区别:
1. 定义方式:抽象类使用`abstract`关键字定义,而接口使用`interface`关键字定义。
2. 实现方式:一个类可以继承(extends)一个抽象类,但只能实现(implements)一个或多个接口。
3. 构造函数:抽象类可以有构造函数,而接口不能有构造函数。抽象类的构造函数在子类实例化时会被调用。
4. 方法实现:抽象类可以包含抽象方法和非抽象方法的实现,子类可以选择性地覆盖抽象方法。接口只能包含方法的声明,实现接口的类必须提供方法的具体实现。
5. 属性:抽象类可以包含属性(成员变量),可以有各种类型的访问修饰符。接口只能包含常量(`public static final`修饰的变量),默认为`public`访问修饰符。
6. 多继承支持:由于Java中单继承的限制,一个类只能继承一个抽象类,但是可以实现多个接口,从而实现多个继承。
7. 设计目的:抽象类主要用于定义类的层次结构和共享代码,它是对具有相似特征的类的一种抽象。接口主要用于定义行为契约,它规定了实现类必须提供哪些方法

Java设计模式

Java中常用的设计模式有很多种,下面列举几种常见的设计模式:
1. 单例模式(Singleton Pattern):确保一个类只有一个实例,并提供全局访问点。
2. 工厂模式(Factory Pattern):通过工厂类创建对象,而不是直接使用new关键字实例化对象。
3. 抽象工厂模式(Abstract Factory Pattern):提供一个创建一系列相关或相互依赖对象的接口,而无需指定具体的类。
4. 原型模式(Prototype Pattern):利用已有的对象作为原型,通过复制创建新的对象。
5. 建造者模式(Builder Pattern):将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
6. 适配器模式(Adapter Pattern):将一个类的接口转换成客户端所期望的另一个接口。
7. 桥接模式(Bridge Pattern):将抽象部分与实现部分分离,使它们可以独立地变化。
8. 装饰者模式(Decorator Pattern):动态地给对象添加额外的功能,而不需要修改其原始类。
9. 观察者模式(Observer Pattern):定义一种一对多的依赖关系,使得当一个对象状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。
10. 策略模式(Strategy Pattern):定义一系列算法,将每个算法封装起来,并使它们可以相互替换。

索引不生效的情况

在使用数据库中的索引时,有一些情况下索引可能不会生效,导致查询性能下降。以下是一些常见导致索引不生效的情况:
1. 列类型不匹配:如果查询条件中的列与索引列的数据类型不匹配,例如将字符串与数字进行比较,或者对日期进行字符串比较,索引可能无法有效使用。
2. 使用函数或表达式:如果在查询条件中使用了函数或表达式来操作索引列,例如使用`LOWER()`函数、`CONCAT()`函数等,这样会导致索引无法被利用。
3. 列值范围过宽:如果索引列上的数据分布非常广泛(例如,大多数行具有唯一的值),那么使用索引可能不会比全表扫描更高效,数据库优化器可能会选择全表扫描而不使用索引。
4. 统计信息不准确:索引是根据数据库中的统计信息进行查询优化的,如果统计信息不准确或者过时,数据库优化器可能会做出错误的选择,不使用索引。
5. 表数据量太小:对于非常小的表,使用索引可能不会带来明显的性能提升,因为全表扫描的开销相对较小。
6. 查询使用了OR条件:当查询条件中使用了OR条件时,索引可能无法同时应用到所有条件上,从而无法充分利用索引。
7. 高选择性的列:如果某个列的值重复非常多,即高选择性的列,那么使用索引可能不会有明显的性能提升。

总结起来,当索引列的数据类型不匹配、使用了函数或表达式、列值范围过宽、统计信息不准确、表数据量太小、查询使用了OR条件或者高选择性的列时,索引可能不会生效。在实际应用中,我们需要结合具体的SQL语句和数据分布情况来分析索引是否生效,如果发现索引不生效,可以考虑重新设计索引,更新统计信息或者优化查询语句,以提高查询性能。

springBoot的一些基本问题-比如IOC和AOP

在Java中,`@Resource`和`@Autowired`是两个常用的依赖注入(Dependency Injection)注解,它们的作用是为类成员变量、方法参数或构造函数参数自动注入依赖对象。它们之间有以下区别:

1. 来源不同:`@Resource`是JavaEE提供的注解,而`@Autowired`是Spring框架提供的注解。

2. 注入规则不同:`@Resource`默认按照名称进行依赖注入,即通过名称匹配来确定要注入的对象。如果找不到与名称匹配的对象,则会尝试按照类型进行匹配。`@Autowired`默认按照类型进行依赖注入,并且要求依赖对象必须存在。如果存在多个类型匹配的对象,可以配合`@Qualifier`注解指定具体的注入对象。

3. 支持的注入方式不同:`@Resource`可以用于成员变量、方法、构造函数参数、setter方法等位置进行注入。`@Autowired`主要用于成员变量以及构造函数参数上的注入。

4. 所属框架不同:`@Resource`是JavaEE的一部分,可以在JavaEE容器(如Tomcat、Jboss等)中使用。`@Autowired`是Spring框架的一部分,主要用于Spring应用程序中。

总结来说,`@Resource`是JavaEE提供的注解,支持按照名称和类型进行注入,可以在JavaEE容器中使用;而`@Autowired`是Spring框架提供的注解,支持按照类型进行注入,主要用于Spring应用程序中。根据具体的应用场景和所使用的框架,选择合适的注解进行依赖注入。

事务

事务(Transaction)是指一组数据库操作(或其他系统操作),被视为一个不可分割的工作单元,要么全部成功执行,要么全部失败回滚。事务的目的是保证数据的一致性和完整性。
事务具备以下四个特性(ACID特性):
1. 原子性(Atomicity):事务中的所有操作要么全部执行成功,要么全部失败回滚。原子性确保了事务的不可分割性,如果事务执行失败或终止,所有已执行的操作都会被撤销,数据库回滚到事务开始前的状态。
2. 一致性(Consistency):事务在执行前和执行后都必须保持数据库的一致性。一致性的定义由应用程序决定,例如,转账操作中要求总金额不变,即使发生故障或并发操作也不会打破这个一致性约束。
3. 隔离性(Isolation):并发执行多个事务时,每个事务都应该与其他事务相互隔离,互不干扰。事务的隔离性可以防止并发执行时出现问题,如脏读(读取到未提交的数据)、不可重复读(同一事务内多次读取数据结果不一致)、幻读(同一事务内多次查询结果不一致)等。
4. 持久性(Durability):事务一旦提交,其结果应该是永久的,即使系统发生故障也不会丢失。持久性通常通过将事务日志记录到磁盘来实现。
在Java中,可以使用数据库事务来确保数据的一致性。一般而言,使用以下步骤来处理事务:

1. 开启事务:开始一个新的事务,可以通过数据库连接的begin或startTransaction方法实现。

2. 执行操作:执行一系列数据库操作,如插入、更新、删除等。

3. 提交事务:如果所有的操作都执行成功,并且没有出现异常,则提交事务,将操作的结果永久保存到数据库中,可以通过commit方法实现。

4. 回滚事务:如果中途发生错误或异常,可以选择回滚事务,将操作撤销到事务开始前的状态,可以通过rollback方法实现。

使用事务可以确保在复杂的数据库操作中保持数据的一致性和完整性,避免了在并发环境下出现的问题,并提供了可靠的数据操作机制。

Java中常用的数据结构包括

1. 数组(Array):用于存储固定大小的连续内存空间,可以通过索引访问元素。
2. 集合框架(Collection Framework):提供了一系列接口和类,用于存储和操作一组对象。
   - List:有序集合,允许重复元素。常见的实现类有ArrayList和LinkedList。
   - Set:无序集合,不允许重复元素。常见的实现类有HashSet和TreeSet。
   - Queue:队列,按照特定顺序访问元素。常见的实现类有LinkedList和PriorityQueue。
   - Map:键值对集合,每个键关联一个值。常见的实现类有HashMap和TreeMap。
3. 栈(Stack):后进先出(LIFO)的数据结构,常用的实现类是Stack类。
4. 队列(Queue):先进先出(FIFO)的数据结构,常用的实现类有LinkedList和PriorityQueue。
5. 哈希表(HashMap):使用哈希函数将键映射到值的集合,常用的实现类是HashMap和LinkedHashMap。
6. 树(Tree):由节点组成的层次结构,常用的实现类有BinarySearchTree、 AVL树等。
7. 图(Graph):由节点和边组成的数据结构,常用的实现类有邻接表和邻接矩阵。
8. 堆(Heap):一种特殊的树结构,常用的实现类有最大堆和最小堆。
9. 链表(LinkedList):由节点组成的线性数据结构,每个节点持有下一个节点的引用。
10. 栈(Stack):一种特殊的线性数据结构,后进先出(LIFO)的访问方式。
这些数据结构在不同场景下有不同的应用,选择合适的数据结构可以提高程序的效率和性能。
posted @ 2023-08-11 00:07  博客-涛  阅读(13)  评论(0编辑  收藏  举报