十五 JDBC
1 JDBC 访问数据库的基本步骤
加载驱动,通过 DriverManager 对象获取连接对象 Connection,通过连接对象获取会话,通过会话进行数据的增删改查封装对象,关闭资源
2 PreparedStatement 和 Statement 的区别
PreparedStatement和Statement都是Java JDBC API中用于执行SQL查询语句的接口,但它们之间存在一些重要的区别。
预编译与编译:PreparedStatement支持预编译SQL语句,它会将编译好的SQL语句放在数据库端,类似于缓存。每次执行时,不需要再对SQL语句进行解析和编译,这可以提高代码的执行效率。相比之下,Statement不对SQL语句做处理,而是直接交给数据库,每次执行SQL语句时,相关数据库都要执行SQL语句的编译。因此,对于多次重复执行的SQL语句,使用PreparedStatement可以显著提高执行效率。
参数化查询:PreparedStatement允许使用占位符,这使得执行带有动态参数的SQL语句成为可能。在执行SQL语句之前,可以通过PreparedStatement对象提供的一系列设置参数的方法(如setInt、setString、setDate等)来指定预编译SQL语句中的参数值。这种参数化查询不仅提高了代码的可读性和灵活性,还能有效防止SQL注入攻击。而Statement则不支持参数化查询。
资源开销:对于只执行一次存取的数据库操作,使用Statement对象的开销比PreparedStatement小。因为PreparedStatement对象的创建和准备需要更多的系统资源。但是,对于需要重复执行的SQL语句,由于PreparedStatement的预编译特性,其总体性能优势会体现出来。
批处理:PreparedStatement支持批处理操作,即可以一次性执行多条SQL语句,这进一步提高了执行效率。而Statement则不支持批处理。
PreparedStatement和Statement的主要区别在于预编译、参数化查询、资源开销和批处理支持等方面。在选择使用哪个接口时,应根据具体的应用场景和需求进行权衡。对于需要重复执行或带有动态参数的SQL语句,使用PreparedStatement通常更为合适;而对于一次性或简单的数据库操作,使用Statement可能更为高效。
3 数据库为什么要使用连接池
数据库连接池是一个连接数据库的缓存池,用于管理和分配数据库连接对象。数据库使用连接池可以显著提高性能、优化资源管理和确保连接的可靠性,是数据库管理中的重要工具。数据库使用连接池的主要原因包括:
提高性能:数据库连接的创建和销毁是一项耗时的操作。使用连接池可以避免频繁的创建和销毁连接,从而减少了系统的开销。连接池中的连接可以被多个线程共享,提高了数据库访问的并发性能。此外,连接池中的连接可以被多个请求复用,避免了每次请求都需要重新创建连接的开销,减少了数据库的负载,提高了系统的响应速度。
资源管理:数据库连接是一种关键的、有限的、昂贵的资源。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,以及程序的性能指标。连接池可以限制同时访问数据库的连接数量,防止系统因为连接过多而导致资源耗尽或者崩溃。通过连接池的管理,可以对连接进行有效的分配和回收,保证连接的有效利用。
连接的可靠性:连接池可以对连接进行有效的监控和管理。当连接出现异常或者超时时,连接池可以自动将其标记为无效,并重新创建一个新的连接,保证了连接的可靠性。
4 drop,delete 与 truncate 的区别
drop、delete和truncate都是SQL中用于处理数据库表数据的命令,但它们各自的功能和用法有着显著的区别。
drop:drop命令用于删除整个表及其所有数据和定义。执行drop语句后,表的结构也会被删除,表中的数据和相关对象(如触发器、索引等)都将被永久删除。一旦执行drop命令,被删除的表及其所有数据将无法恢复,因此在使用时需要格外小心。drop命令的语法相对简单,通常只需要指定要删除的表名。
truncate:truncate命令用于删除表中的所有数据,但保留表的结构(即定义)。与delete命令不同,truncate操作不记录任何单独的删除操作到日志中,因此执行速度通常更快。truncate命令会重置任何自增长的字段(如自增长的ID),这意味着下次向表中插入数据时,自增长字段的值会从1开始。truncate命令不能用于带有外键约束的表,因为它不会触发与这些外键相关的级联删除操作。
delete:delete命令用于从表中删除数据。与truncate不同,delete命令可以配合where子句使用,以实现基于特定条件的数据删除。如果不使用where子句,delete将删除表中的所有数据。delete命令会记录每一行的删除操作到日志中,因此执行速度相对较慢,尤其是在处理大量数据时。delete命令不会重置自增长的字段。delete命令可以用于处理带有外键约束的表,因为它可以触发相关的级联删除操作。
这三个命令的主要区别在于它们删除数据的范围和方式,以及对表结构的影响。drop是删除整个表及其数据,truncate是删除表数据但保留结构,delete则可以根据条件删除表中的数据。在选择使用哪个命令时,需要根据具体的需求和数据处理要求来决定。
5 查询语句不同元素执行先后顺序
标准 SQL 规范中它们的顺序依次为 from --> where --> group by --> having–> select --> order by --> limit
6 左连接、右连接、内连接和全外连接的区别
左连接:返回左表中的所有记录和右表中连接字段相等的记录
右连接:返回右表中的所有记录和左表中连接字段相等的记录
内连接:只返回两个表中连接字段相等记录
全外连接:返回左右表中连接字段相等的记录和剩余所有记录
十六 MyBatis
1 在 MyBatis 中,#{} 和 ${} 的区别
#{}是预编译处理,${}是字符串替换。
MyBatis 在 处 理 #{} 时 , 会 将 SQL 中 的 #{} 替 换 为 ? 号 , 调 用PreparedStatement 的 set 方法来赋值;
MyBatis 在处理 ${} 时,就是把 ${} 替换成变量的值。使用 #{} 可以有效的防止 SQL 注入,提高系统安全性。
2 阐述 Mapper 接口原理
MyBatis通过使用JDK的动态代理技术,为开发者提供的Mapper接口自动生成了动态代理类,从而实现了对数据库操作的抽象化。这种机制让开发者能够专注于定义SQL语句和业务逻辑,而无需关心具体的数据库连接、SQL执行等底层细节。
具体来说,MyBatis在解析全局配置文件时,会解析mapper标签对应的mapper XML文件或Mapper接口注解类。在这个过程中,MyBatis会收集Mapper接口的信息,并创建MapperProxyFactory类实例。MapperProxyFactory是一个工厂类,用于创建MapperProxy实例。
MapperProxy是java.lang.reflect.InvocationHandler接口的实现类。当MyBatis调用sqlSession.getMapper()方法时,内部会调用MapperProxyFactory的newInstance方法,根据Mapper接口信息创建对应的Mapper接口动态代理类对象。这个动态代理类对象实现了开发者定义的Mapper接口,并且在其内部持有一个MapperProxy类型的引用。
当开发者调用Mapper接口动态代理类对象的任何方法时,实际上会调用到MapperProxy类型的invoke方法。在invoke方法中,MyBatis会根据方法的签名和命名空间等信息,找到对应的SQL语句并执行。执行结果会根据Mapper接口的方法签名进行转换,并返回给调用者。
通过这种方式,MyBatis将开发者定义的Mapper接口与具体的SQL语句和执行逻辑进行了绑定,从而实现了对数据库操作的抽象化和简化。开发者只需要关注Mapper接口的定义和SQL语句的编写,而无需关心具体的数据库连接、SQL执行等底层细节,提高了代码的可读性和可维护性。
3 MyBatis 是否支持延迟加载
MyBatis 仅支持 association 关联对象和 collection 关联集合对象的延迟加载,association 指的就是一对一,collection 指的就是一对多查询。在 MyBatis 配置文件中,可以配置是否启用延迟加载 lazyLoadingEnabled = true|false。
它的原理是,使用 Javasissit(默认)创建目标对象的代理对象,当调用目标方法时,进入拦截器方法,比如调用 a.getB().getName(),拦截器 invoke() 方法发现 a.getB() 是 null 值,那么就会单独发送事先保存好的查询关联 B 对象的SQL,把 B 查询上来,然后调用 a.setB(b),于是 a 的对象 b 属性就有值了,接着完成 a.getB().getName() 方法的调用。这就是延迟加载的基本原理。
4 MyBatis 中的批处理
使用 BatchExecutor 完成批处理。执行 update(没有 select,JDBC 批处理不支持 select),将所有 SQL 都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个 Statement 对象,每个 Statement 对象都是addBatch() 完毕后,等待逐一执行 executeBatch() 批处理。与 JDBC 批处理相同。
5 MyBatis-Plus 特点
1、无侵入:只做增强不做改变,约会它不会对现有工程产生影响,如丝般顺滑
2、损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
3、强大的 CRUD 操作:内置通用 Mapper,通用服务,仅通过少量配置即可实现单表大部分 CRUD 操作,足以强大的条件构造器,满足各种使用需求
4、支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写分类查询条件,无需再担心替换写错
5、支持主键自动生成:支持多达 4 种主键策略(内部含分布式唯一 ID 生成器- 序列),可自由配置,完美解决主键问题
6、支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model类即可进行强大的 CRUD 操作
7、支持自定义通用通用操作:支持通用通用方法注入(写一次,可在任何地方使用)
8、内置代码生成器:采用代码或 Maven 插件可快速生成 Mapper,Model,Service,Controller 层代码,支持模板引擎,甚至超多自定义配置等您来使用
9、内置分页插件:基于 MyBatis 物理分页,开发者无需关心特定操作,配置好插件之后,写分页等同于普通列表查询
10、分页插件支持多种数据库:支持 MySQL,MariaDB,Oracle,DB2,H2,HSQL,SQLite,Postgre,SQLServer 等多种数据库
11、内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
12、内置的拦截插件:提供全表 delete,update 操作智能分析中断,也可自定义拦截规则,预防误操作
6 MyBatis-Plus 常用注解
@TableName:这个注解用于指定实体类与数据库表之间的映射关系。当实体类的类名与数据库表名一致时,可以省略该注解。
@TableId:用于标识实体类中的主键字段,并可以指定主键的生成策略。如果实体类中的字段名与数据库表的主键列名一致,并且主键名为“id”,则可以省略该注解。此外,type属性用于设置主键的生成策略,如自增、UUID等。
@TableField:用于标识实体类中的非主键字段与数据库表的列之间的映射关系。这个注解提供了丰富的属性,如exist(指定字段是否映射到数据库表的列)、insertStrategy、updateStrategy和whereStrategy(控制字段在插入、更新或作为WHERE条件时的行为),以及fill(字段为空时的自动填充策略)。
@Version:用于实现乐观锁机制,帮助处理并发更新冲突。当多个线程或事务尝试同时更新同一行数据时,乐观锁能够确保数据的完整性和一致性。
@EnumValue:这个注解用于标识实体类中的枚举字段,并指定枚举字段与数据库存储值之间的映射关系。
@TableLogic:用于实现逻辑删除功能。逻辑删除并不真正从数据库中删除数据,而是通过标记某些字段(如删除标志位)来表示数据已被删除。这种方式保留了数据的历史信息,并允许在需要时恢复数据。
@KeySequence:这个注解用于Oracle数据库中的序列主键策略,它允许使用数据库序列来生成主键值。
@InterceptorIgnore:这个注解用于指定插件的过滤规则,使得某些操作或方法不受插件的影响。