大数据]Doris 数据库和表操作语法详细实践
目录
一、前言
二、数据库基本操作
2.1 修改账户密码
2.2 创建新用户
2.3 创建数据库与账户授权
2.3.1 数据库创建补充说明
2.3.2 数据库账户赋权
三、数据表基本操作
3.1 Doris 数据表介绍与使用
3.1.1 建表结构说明
3.1.2 建表语法与操作
3.1.3 建表示例 - 单分区
3.1.4 建表示例 - 多分区
3.2 单分区与多分区使用场景
3.2.1 单分区使用场景
3.2.2 多分区使用场景
四、Doris 数据导入操作实践
4.1 导入数据方式总结
4.2 insert into
4.2.1 基本语法
4.2.2 操作演示
4.3 Stream load
4.4 Broker load
4.5 Routine Load
4.5.1 准备kafka环境
4.5.2 创建一张测试表
4.5.3 发送几条消息到kafka
4.5.4 创建导入作业
五、数据删除
5.1 删除语法
5.2 操作演示
5.2.1 删除语法补充说明
六、写在文末
一、前言
对于学习任何一门数据库来说,不管是关系型数据库mysql,还是诸如hive这样的大数据相关的分析性数据库等,只有掌握了数据库操作的基本知识,才能在后续的学习中更加游刃有余,本篇将详细介绍Doris 中数据库和表相关的基础操作。
二、数据库基本操作
在日常使用mysql操作数据库时,为确保数据安全,通常不会直接使用root账户连接和操作,而是由管理员为使用者创建新的账户和密码,并赋予一定的操作权限。
2.1 修改账户密码
Drois环境安装完成之后,内置了 root 和 admin 用户,密码默认都为空,启动完 Doris 程序之后,可以通过 root 或 admin 用户连接到 Doris 集群,在上一篇的环境搭建中,进入容器之后,使用下面的命令登录:
/opt/mysql-5.7.22-linux-glibc2.12-x86_64/bin/mysql -uroot -P9030 -h127.0.0.1 --skip-ssl
如果需要修改root账户密码,执行下面的命令
SET PASSWORD FOR 'root' = PASSWORD('你设置的密码');
2.2 创建新用户
使用下面的命令创建一个新的账户
CREATE USER 'user01' IDENTIFIED BY 'user01';
下次登录的时候,就可以使用上述这个账户进行登录了
mysql -h 主机名称 -P9030 -uuser01 -puser01
注意:
新创建的普通用户默认没有任何权限,还需根据实际需要进行赋权
2.3 创建数据库与账户授权
初始创建数据库时可以通过root或admin账户进行创建,比如下面创建一个新的测试数据库
CREATE DATABASE doris_db;
2.3.1 数据库创建补充说明
与很多中间件一样,如果你不清楚具体语法,可以使用help + command的方式进行查看,比如想要了解如何创建数据库,就可以执行: HELP CREATE DATABASE; 就能看到完整的建库语法
information_schema数据库说明:
-
information_schema是为了兼容MySQL协议而存在,实际中信息可能不是很准确,所以关于具体数据库的信息建议通过直接查询相应数据库而获得。
2.3.2 数据库账户赋权
有了数据库和账户之后,就可以对账户赋权,比如将上述的doris_db赋权给user01,授权之后采用 test 账户登录就可以操作 doris_db数据库了
GRANT ALL ON doris_db TO user01;
然后使用客户端工具登录该账户就能看到数据库了
三、数据表基本操作
3.1 Doris 数据表介绍与使用
在 Doris 中,数据都以表(Table)的形式进行逻辑上的描述。Doris 支持两种类型的表:
-
OLAP 表:主要用于存储静态数据,支持更新和删除操作。
-
外部表:主要用于临时存储数据,不支持更新和删除操作,通常用于导入数据后再转换为 OLAP 表。
3.1.1 建表结构说明
在 Doris 中创建表时,需要定义表的结构,包括列的定义和约束。
列定义
列定义包括列名、数据类型、是否为空(NULL 或 NOT NULL)等属性。Doris 支持多种数据类型,包括但不限于:
-
整形类型:
TINYINT
,SMALLINT
,INT
,BIGINT
-
浮点类型:
FLOAT
,DOUBLE
-
字符串类型:
VARCHAR
,CHAR
-
日期时间类型:
DATE
,DATETIME
-
JSON 类型:
JSON
-
固定长度二进制类型:
FIXED_STRING
主键约束
Doris 支持定义主键(Primary Key),主键用于唯一标识表中的每一行数据。主键可以由单个列或多个列组合而成。定义主键有助于提高查询性能,尤其是对于需要频繁进行更新和删除操作的场景。
分区键
除了主键之外,Doris 还支持分区键(Partition Key),用于将数据逻辑上分割成多个分区,从而提高查询效率。分区键可以是单个列或多个列的组合。
分区
分区是 Doris 中的一个重要特性,用于将大数据集划分为多个较小的部分,以便更快地执行查询。Doris 支持以下几种分区策略:
-
范围分区(Range Partitioning):根据数值或日期类型的数据范围进行分区。
-
列表分区(List Partitioning):根据离散的值列表进行分区。
-
Hash 分区:根据哈希函数的结果进行分区。
副本
为了提高数据的可靠性和查询性能,Doris 支持为数据创建多个副本。副本是数据在不同 Backend (BE) 节点上的冗余拷贝,可以提高数据的可用性和容错能力。
3.1.2 建表语法与操作
使用 CREATE TABLE 命令可以建立一个表(Table),也可以通过 HELP CREATE TABLE查看完整的建表语法
HELP CREATE TABLE;
建表完整语法:
CREATE [EXTERNAL] TABLE [IF NOT EXISTS] [database.]table_name
(column_definition1[, column_definition2, ...]
[, index_definition1[, ndex_definition12,]])
[ENGINE = [olap|mysql|broker|hive]]
[key_desc]
[COMMENT "table comment"];
[partition_desc]
[distribution_desc]
[rollup_index]
[PROPERTIES ("key"="value", ...)]
[BROKER PROPERTIES ("key"="value", ...)]
说明:
-
Doris 的建表是一个同步命令,命令返回成功,即表示建表成功。
-
Doris 支持支持
单分区
和复合分区
两种建表方式。-
单分区:
只做 HASH 分布,即只分桶
。 -
复合分区:
既有分区也有分桶
-
第一级称为 Partition,即分区。用户可以指定某一维度列作为分区列(当前只支持整型和时间类型的列),并指定每个分区的取值范围。
-
第二级称为 Distribution,即分桶。用户可以指定一个或多个维度列以及桶数对数据进行 HASH 分布。
-
-
3.1.3 建表示例 - 单分区
建立一个名字为 t_user01的逻辑表。分桶列为 siteid,桶数为 10
CREATE TABLE t_user01
(
siteid INT DEFAULT '10',
citycode SMALLINT,
username VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(siteid, citycode, username)
DISTRIBUTED BY HASH(siteid) BUCKETS 10
PROPERTIES("replication_num" = "1");
参数说明:
-
AGGREGATE KEY,聚合字段,这里根据siteid, citycode, username三个字段进行聚合;
-
DISTRIBUTED,分区的字段,这里分区字段为siteid取hash之后分到10个桶中;
-
PROPERTIES,副本数量的设置,可以根据你的集群数量进行设置;
插入几条数据,通过结果不难看出,前两条数据的pv经过聚合之后存储的结果是一条
insert into t_user01 values(1,1,'user1',10);
insert into t_user01 values(1,1,'user1',10);
insert into t_user01 values(1,2,'user1',10);
也可以通过控制台,查看分区相关的详细信息
3.1.4 建表示例 - 多分区
使用下面的sql创建一个多分区的表t_user02,在这里使用event_day 列作为分区列,建立3个分区: p202106,p202107,p202108,每个分区使用 siteid 进行哈希分桶,桶数为10,副本数为1
-
p202106:范围为 [最小值, 2021-07-01)
-
p202107:范围为 [2021-07-01, 2021-08-01)
-
p202108:范围为 [2021-08-01, 2020-09-01)
-
区间为左闭右开;
CREATE TABLE t_user02
(
event_day DATE,
siteid INT DEFAULT '10',
citycode SMALLINT,
username VARCHAR(32) DEFAULT '',
pv BIGINT SUM DEFAULT '0'
)
AGGREGATE KEY(event_day, siteid, citycode, username)
PARTITION BY RANGE(event_day)
(
PARTITION p202106 VALUES LESS THAN ('2021-07-01'),
PARTITION p202107 VALUES LESS THAN ('2021-08-01'),
PARTITION p202108 VALUES LESS THAN ('2021-09-01')
)
DISTRIBUTED BY HASH(siteid) BUCKETS 10
PROPERTIES("replication_num" = "1");
插入几条数据
insert into t_user02 values ('2021-06-03',9,1,'jack',3);
insert into t_user02 values ('2021-06-10',10,2,'rose',2);
insert into t_user02 values ('2021-07-03',11,1,'jim',2);
insert into t_user02 values ('2021-07-05',12,1,'grace',2);
insert into t_user02 values ('2021-07-12',13,2,'tom',2);
insert into t_user02 values ('2021-08-15',14,3,'bush',3);
insert into t_user02 values ('2021-08-12',15,3,'helen',3);
也可以通过控制台 dbs进行详细的查看
在这里列举了表的详细信息,比如分区信息
3.2 单分区与多分区使用场景
3.2.1 单分区使用场景
虽然单分区在某些场景下可能不如多分区那样高效,但它仍然有其适用的场景。下面介绍了单分区在 Apache Doris 中的一些使用场景。
-
小型数据集
-
当数据集较小且不需要频繁进行按时间范围或其他条件的过滤查询时,可以使用单分区。这种情况下,单分区可以简化数据管理和查询逻辑。
-
-
不需要时间分割的数据
-
对于那些不需要按时间范围进行查询的数据集,单分区可以避免不必要的分区开销。例如,一些统计汇总表可能只需要定期更新数据,而不涉及历史数据的查询。
-
-
全表扫描
-
在某些情况下,查询需要对整个表进行全表扫描。此时,单分区可以简化查询逻辑,避免分区选择带来的额外复杂性。
-
-
简化数据导入
-
对于不需要按照特定维度(如时间)进行分区的数据集,单分区可以简化数据导入过程。例如,如果数据是从多个来源一次性导入的,且不需要根据时间或其他维度进行拆分,那么单分区可以减少数据导入的复杂度。
-
-
存储非时间序列数据
-
当数据不是按照时间序列组织的,而是根据其他维度(如地理位置、用户ID等)进行查询时,单分区可能更为合适。例如,一个存储用户信息的表可能不需要按时间进行分区,而是直接存储所有用户的信息。
-
-
数据量不大且增长缓慢
-
如果数据量不大且增长速度较慢,单分区可以有效减少管理成本。在这种情况下,多分区的优势并不明显,反而会增加管理复杂度。
-
-
实时查询场景
-
对于需要快速响应的实时查询场景,单分区可以减少分区选择所带来的延迟。虽然多分区可以提高查询性能,但对于非常小的数据集而言,单分区的查询速度已经足够快。
-
-
测试开发使用
-
在测试和开发环境中,为了简化数据管理和测试流程,可以使用单分区。这样可以更快地进行数据加载和查询测试,而无需担心分区逻辑的影响。
-
-
特定的聚合查询
-
如果查询主要是针对整个数据集的聚合操作(如求和、平均值等),单分区可以避免分区选择带来的额外开销。例如,一个用于计算总体统计数据的表可能只需要单分区。
-
3.2.2 多分区使用场景
复合分区(Composite Partitioning)指的是在一个表中同时使用两种或更多种不同的分区策略。这种分区方式可以更加灵活地满足不同的查询需求和数据管理需求。以下是复合分区的一些常见使用场景:
-
多维度查询优化
-
当数据集需要根据多个维度进行高效的查询时,复合分区可以显著提升查询性能。例如,数据可以按照时间维度和业务维度(如地区、用户组等)进行分区,从而支持快速的时间段和业务维度组合查询。
-
-
数据生命周期管理
-
复合分区可以帮助更好地管理不同生命周期阶段的数据。例如,数据可以根据时间进行范围分区,再根据业务标识(如用户ID、设备ID等)进行哈希分区,这样可以方便地管理每个时间段内的不同业务数据。
-
-
提高查询效率
-
通过复合分区,可以将数据分布得更加均匀,减少查询时的数据扫描范围。例如,一个表可以按照时间范围分区,然后再按照某些散列字段(如用户ID)进行哈希分区,这样可以确保数据在集群中分布均匀,并且查询时可以快速定位到相关数据。
-
-
并行处理
-
复合分区可以更好地支持并行处理,特别是在大规模数据集的情况下。通过将数据按照多个维度进行分区,可以在查询时并行处理不同的分区,从而加速查询响应时间。
-
-
数据隔离
-
复合分区还可以帮助实现数据隔离,特别是在多租户环境中。例如,数据可以按照租户ID进行哈希分区,然后在每个租户的数据基础上再按照时间范围进行分区,这样可以确保不同租户的数据不会相互干扰。
-
-
数据分布均衡
-
在分布式数据库环境中,合理的数据分布可以避免热点问题。通过复合分区,可以确保数据在各个节点之间分布得更加均匀,避免某些节点负载过高。
-
-
维度表和事实表的关联优化
-
在数据仓库设计中,复合分区可以用于优化维度表和事实表之间的关联查询。例如,事实表可以按照时间范围分区,再按照维度表中的主键进行哈希分区,从而加快事实表与维度表的连接操作。
-
-
实现细粒度访问控制
-
通过复合分区,可以实现更加细粒度的数据访问控制。例如,数据可以按照部门进行哈希分区,再按照时间范围进行分区,这样可以针对不同部门的数据实施不同的访问策略。
-
-
提升写入性能
-
在需要频繁写入数据的场景中,复合分区可以提高写入性能。通过将数据按照时间范围和业务标识进行分区,可以确保写入操作分散到多个分区,减少单一分区的压力。
-
-
支持复杂查询
-
对于复杂的分析查询,复合分区可以提供更好的支持。例如,复合分区可以帮助实现多表连接、多条件筛选等操作,使得查询更加高效。
-
四、Doris 数据导入操作实践
Doris 提供了多种数据导入的方式,在实际应用中,业务主要是基于Doris的具体的数据表数据进行数据消费,因此需要将数据提前插入到表中,而上文中演示的insert into 只是其中的一种方式,接下来介绍下相关的数据导入方式。
4.1 导入数据方式总结
为适配不同的数据导入需求,Doris 系统提供了6种不同的导入方式。每种导入方式支持不同的数据源,存在不同的使用方式(异步,同步)。所有导入方式都支持 csv 数据格式。其中 Broker load 还支持 parquet 和 orc 数据格式。
-
Stream load
-
用户通过 HTTP 协议提交请求并携带原始数据创建导入。主要用于快速将本地文件或数据流中的数据导入到 Doris,导入命令同步返回导入结果。
-
-
Insert
-
类似 MySQL 中的 Insert 语句,Doris 提供 INSERT INTO tbl SELECT ...; 的方式从 Doris 的表中读取数据并导入到另一张表。或者通过 INSERT INTO tbl VALUES(...); 插入单条数据。
-
-
Broker load
-
通过 Broker 进程访问并读取外部数据源(如 HDFS)导入到 Doris。用户通过 Mysql 协议提交导入作业后,异步执行。通过 SHOW LOAD 命令查看导入结果。
-
-
Multi load
-
用户通过 HTTP 协议提交多个导入作业。Multi Load 可以保证多个导入作业的原子生效。
-
-
Routine load
-
用户通过 MySQL 协议提交例行导入作业,生成一个常驻线程,不间断的从数据源(如 Kafka)中读取数据并导入到 Doris 中。
-
-
通过S3协议导入
-
用户通过S3协议直接导入数据,用法和Broker Load 类似
-
4.2 insert into
和操作mysql表类似,这是一种最基本的数据导入方式,doris支持mysql协议,所以可以直接使用这种方式快速插入数据。比如上文中演示的往t_user01插入数据:
insert into t_user01 values(1,1,'user1',10);
insert into t_user01 values(1,1,'user1',10);
insert into t_user01 values(1,2,'user1',10);
4.2.1 基本语法
Insert Into 语句的使用方式和 MySQL 等数据库中 Insert Into 语句的使用方式类似。但在 Doris 中,所有的数据写入都是一个独立的导入作业。
完整格式:
INSERT INTO table_name [partition_info] [WITH LABEL label名称] [col_list] [query_stmt] [values];
常用格式【开发测试使用】:
INSERT INTO tbl (col1, col2, ...) VALUES (1, 2, ...), (1,3, ...);
4.2.2 操作演示
在实际工作中,使用 INSERT INTO tbl SELECT ... 这种方式比较多,在学习mysql的时候也有类似的用法,下面演示下使用过程,首先创建一张表,该表与上一步的案例中表一模一样,只是表名称不同
create table user_kafka_copy(
id int,
name varchar(50),
age int
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 10
PROPERTIES("replication_num" = "1");
使用下面的sql插入数据到user_kafka_copy
-
with label label_copy ,即给当前的执行任务加一个标签,方便后续任务失败时可以追溯
insert into user_kafka_copy with label label_copy select * from user_kafka;
执行成功后检查下数据已经导入了
4.3 Stream load
使用下面的命令进行导入
curl --location-trusted -u root:123456 -H "label:user01_20210210" -H "column_separator:," -T user01.csv http://127.0.0.1:8030/api/doris_db/t_user01/_stream_load
参数说明:
-
-H "label:user01_20210210"
-
指定导入数据的标签;
-
-
-H "column_separator:,"
-
指定导入的数据字段之间的分隔符
-
-
-T user01.csv
-
指定导入时使用的数据文件
-
user01.csv内容如下:
执行上面的数据导入命令
可以看到,已经按照预期进行导入并做数据聚合操作
4.4 Broker load
Broker load是一个导入的异步方式,不同的数据源需要部署不同的 broker 进程。可以通过 show broker 命令查看已经部署的 broker。参考文档:按方式导入 - Broker Load - 《Apache Doris v1.2 中文文档》 - 书栈网 · BookStack
4.5 Routine Load
支持用户提交一个常驻的导入任务,通过不断的从指定的数据源读取数据,将数据导入到 Doris 中,当前仅支持kafka系统进行例行导入。
支持无认证的 Kafka 访问,以及通过 SSL 方式认证的 Kafka 集群。
4.5.1 准备kafka环境
像下面这样,提前创建一个topic,确保生产端发送的消息可以被消费端收到
4.5.2 创建一张测试表
该表用于后面从kafka中接收存储数据,建表sql如下
create table user_kafka(
id int,
name varchar(50),
age int
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 10
PROPERTIES("replication_num" = "1");
4.5.3 发送几条消息到kafka
在上述的kafka发送消息的窗口中,发送下面2条消息
{"id":1,"name":"jike","age":23}
{"id":2,"name":"tank","age":19}
4.5.4 创建导入作业
desired_concurrent_number指定并行度
CREATE ROUTINE LOAD doris_db.kafka_job2 on user_kafka
PROPERTIES
(
"desired_concurrent_number"="1",
"strict_mode"="false",
"format" = "json"
)
FROM KAFKA
(
"kafka_broker_list"= "kafka服务IP地址:9092",
"kafka_topic" = "test",
"property.group.id" = "test_group_1",
"property.kafka_default_offsets" = "OFFSET_BEGINNING",
"property.enable.auto.commit" = "false"
);
执行成功后,检查数据表,可以发现数据导入成功了,这个可能存在一定的延迟
补充:
-
查看导入作业状态
-
SHOW ALL ROUTINE LOAD
-
-
修改作业属性
-
用户可以修改已经创建的作业。具体说明可以通过 HELP ALTER ROUTINE LOAD; 命令查看
-
-
作业的启停控制
-
停止作业 : STOP ROUTINE LOAD FOR 作业名称;
-
暂停作业 : PAUSE ROUTINE LOAD FOR 作业名称;
-
恢复作业 : RESUME ROUTINE LOAD FOR 作业名称;
-
五、数据删除
在日常使用中,删除数据也是经常涉及到的操作,接下来说明如何在Doris中删除数据。
5.1 删除语法
Delete不同于其他导入方式,它是一个同步过程,与Insert into相似,所有的Delete操作在Doris中是一个独立的导入作业,一般Delete语句需要指定表和分区以及删除的条件来筛选要删除的数据,并将会同时删除base表和rollup表的数据。参考文档:操作 - DELETE - 《Apache Doris v1.2 中文文档》 - 书栈网 · BookStack
完整语法
DELETE FROM table_name [PARTITION partition_name | PARTITIONS (p1, p2)]
WHERE
column_name1 op { value | value_list } [ AND column_name2 op { value | value_list } ...];
参数说明:
-
op 的可选类型包括:=, >, <, >=, <=, !=, in, not in
-
使用聚合类的表模型(AGGREGATE、UNIQUE)只能指定 key 列上的条件。
-
当选定的 key 列不存在于某个 rollup 中时,无法进行 delete。
-
条件之间只能是“与”的关系。若希望达成“或”的关系,需要将条件分写在两个 DELETE 语句中。
-
SinceVersion 1.2 如果为分区表,需要指定分区,如果不指定,doris 会从条件中推断出分区。两种情况下,doris 无法从条件中推断出分区: 1) 条件中不包含分区列;2) 分区列的 op 为 not in。当分区表未指定分区,或者无法从条件中推断分区的时候,需要设置会话变量 delete_without_partition 为 true,此时 delete 会应用到所有分区。
5.2 操作演示
删除user_kafka 中id为1的数据
delete from user_kafka_copy where id = 1;
在上述的删除中我们并没有指定分区也可以删除成功,为什么呢?因为我们的表的分区为单分区,只有单分区才可以这么使用,如果是多分区这样操作会报错。
删除指定分区的数据,需要先找到数据所在的分区名称,可以在控制台界面上定位分区名字
DELETE FROM user_kafka_copy PARTITION user_kafka_copy where id = 2;
5.2.1 删除语法补充说明
delete from 类似标准mysql的 delete 语法和使用,具体可查看 help delete 命令帮助,下面补充几点使用过程中的注意事项:
-
语句只能针对 Partition 级别进行删除。如果一个表有多个 partition 含有需要删除的数据,则需要执行多次针对不同 Partition 的 delete 语句。而如果是没有使用 Partition 的表,partition 的名称即表名。
-
where 后面的条件谓词只能针对 Key 列,并且谓词之间,只能通过 AND 连接。如果想实现 OR 的语义,需要执行多条 delete。
-
delete 是一个同步命令,命令返回即表示执行成功。
-
从代码实现角度,delete 是一种特殊的导入操作。该命令所导入的内容,也是一个新的数据版本,只是该版本中只包含命令中指定的删除条件。在实际执行查询时,会根据这些条件进行查询时过滤。所以,不建议大量频繁使用 delete 命令,因为这可能导致查询效率降低。
-
数据的真正删除是在 BE 进行数据 Compaction 时进行的。所以执行完 delete 命令后,并不会立即释放磁盘空间。
-
delete 命令一个较强的限制条件是,在执行该命令时,对应的表,不能有正在进行的导入任务(包括 PENDING、ETL、LOADING)。而如果有 QUORUM_FINISHED 状态的导入任务,则可能可以执行。
-
delete 也有一个隐含的类似 QUORUM_FINISHED 的状态。即如果 delete 只在多数副本上完成了,也会返回用户成功。但是会在后台生成一个异步的 delete job(Async Delete Job),来继续完成对剩余副本的删除操作。如果此时通过 show delete 命令,可以看到这种任务在 state 一栏会显示 QUORUM_FINISHED。
六、写在文末
本文通过案例操作详细演示了Doris与表操作相关的语法,对于学习Doris的同学来说,掌握基础的语法对于后续深入学习和掌握Doris的高级语法有着重要的意义,本篇到此结束感谢观看。
上一篇: 服务器]服务器虚拟化概述 - 结论
下一篇: 【笔记】平面
推荐阅读
-
大数据]Doris 数据库和表操作语法详细实践
-
【2022新手指南】Java编程进阶之路 - 六、技术架构篇 ### MySQL索引底层解析与优化实战 - 你会讲解MySQL索引的数据结构吗?性能调优技巧知多少? - Redis深度揭秘:你知道多少?从基础到哨兵、主从复制全梳理 - Redis持久化及哨兵模式详解,还有集群搭建和Leader选举黑箱打开 - Zookeeper是个啥?特性和应用场景大公开 - ZooKeeper集群搭建攻略及 Leader选举、读写一致性、共享锁实现细节 - 探究ZooKeeper中的Leader选举机制及其在分布式环境中的作用 - Zab协议深入剖析:原理、功能与在Zookeeper中的核心地位 - RabbitMQ全方位解读:工作模式、消费限流、可靠投递与配置策略 - 设计者视角:RabbitMQ过期时间、死信队列与延时队列实践指南 - RocketMQ特性和应用场景揭示:理解其精髓与差异化优势 - Kafka详细介绍:特性及广泛应用于实时数据处理的场景解析 - ElasticSearch实力揭秘:特性概述与作为搜索引擎的广泛应用 - MongoDB认知升级:非关系型数据库的优势阐述,安装与使用实战教学 - BIO/NIO/AIO网络模型对比:掌握它们的区别与在网络编程中的实际应用 - Netty带你飞:理解其超快速度背后的秘密,包括线程模型分析 - 网络通信黑科技:Netty编解码原理与常用编解码器的应用,Protostuff实战演示 - 解密Netty粘包与拆包现象,怎样有效应对这一常见问题 - 自定义Netty心跳检测机制,轻松调整检测间隔时间的艺术 - Dubbo轻骑兵介绍:核心特性概览,服务降级实战与其实现益处 - Dubbo三大神器解读:本地存根与本地伪装的实战运用与优势呈现 ----------------------- 七、结语与回顾
-
SSM三大框架基础面试题-一、Spring篇 什么是Spring框架? Spring是一种轻量级框架,提高开发人员的开发效率以及系统的可维护性。 我们一般说的Spring框架就是Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。比如Core Container中的Core组件是Spring所有组件的核心,Beans组件和Context组件是实现IOC和DI的基础,AOP组件用来实现面向切面编程。 Spring的6个特征: 核心技术:依赖注入(DI),AOP,事件(Events),资源,i18n,验证,数据绑定,类型转换,SpEL。 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。 数据访问:事务,DAO支持,JDBC,ORM,编组XML。 Web支持:Spring MVC和Spring WebFlux Web框架。 集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。 语言:Kotlin,Groovy,动态语言。 列举一些重要的Spring模块? Spring Core:核心,可以说Spring其他所有的功能都依赖于该类库。主要提供IOC和DI功能。 Spring Aspects:该模块为与AspectJ的集成提供支持。 Spring AOP:提供面向切面的编程实现。 Spring JDBC:Java数据库连接。 Spring JMS:Java消息服务。 Spring ORM:用于支持Hibernate等ORM工具。 Spring Web:为创建Web应用程序提供支持。 Spring Test:提供了对JUnit和TestNG测试的支持。 谈谈自己对于Spring IOC和AOP的理解 IOC(Inversion Of Controll,控制反转)是一种设计思想: 在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC在其他语言中也有应用,并非Spring特有。IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。 将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。在实际项目中一个Service类可能由几百甚至上千个类作为它的底层,假如我们需要实例化这个Service,可能要每次都搞清楚这个Service所有底层类的构造函数,这可能会把人逼疯。如果利用IOC的话,你只需要配置好,然后在需要的地方引用就行了,大大增加了项目的可维护性且降低了开发难度。 Spring中的bean的作用域有哪些? 1.singleton:该bean实例为单例 2.prototype:每次请求都会创建一个新的bean实例(多例)。 3.request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。 4.session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。 5.global-session:全局session作用域,仅仅在基于Portlet的Web应用中才有意义,Spring5中已经没有了。Portlet是能够生成语义代码(例如HTML)片段的小型Java Web插件。它们基于Portlet容器,可以像Servlet一样处理HTTP请求。但是与Servlet不同,每个Portlet都有不同的会话。 Spring中的单例bean的线程安全问题了解吗? 概念用于理解:大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。 有两种常见的解决方案(用于回答的点): 1.在bean对象中尽量避免定义可变的成员变量(不太现实)。 2.在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal(线程本地化对象)中(推荐的一种方式)。 ThreadLocal解决多线程变量共享问题(参考博客):https://segmentfault.com/a/1190000009236777 Spring中Bean的生命周期: 1.Bean容器找到配置文件中Spring Bean的定义。 2.Bean容器利用Java Reflection API创建一个Bean的实例。 3.如果涉及到一些属性值,利用set方法设置一些属性值。 4.如果Bean实现了BeanNameAware接口,调用setBeanName方法,传入Bean的名字。 5.如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader方法,传入ClassLoader对象的实例。 6.如果Bean实现了BeanFactoryAware接口,调用setBeanClassFacotory方法,传入ClassLoader对象的实例。 7.与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。 8.如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执postProcessBeforeInitialization方法。 9.如果Bean实现了InitializingBean接口,执行afeterPropertiesSet方法。 10.如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。 11.如果有和加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization方法。 12.当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy方法。 13.当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。 Spring框架中用到了哪些设计模式? 1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。 2.代理设计模式:Spring AOP功能的实现。 3.单例设计模式:Spring中的bean默认都是单例的。 4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。 5.包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。 6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。 7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。 还有很多。。。。。。。 @Component和@Bean的区别是什么 1.作用对象不同。@Component注解作用于类,而@Bean注解作用于方法。 2.@Component注解通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用@ComponentScan注解定义要扫描的路径)。@Bean注解通常是在标有该注解的方法中定义产生这个bean,告诉Spring这是某个类的实例,当我需要用它的时候还给我。 3.@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解来实现。 @Configuration public class AppConfig { @Bean public TransferService transferService { return new TransferServiceImpl; } } <beans> <bean id="transferService" class="com.kk.TransferServiceImpl"/> </beans> @Bean public OneService getService(status) { case (status) { when 1: return new serviceImpl1; when 2: return new serviceImpl2; when 3: return new serviceImpl3; } } 将一个类声明为Spring的bean的注解有哪些? 声明bean的注解: @Component 组件,没有明确的角色 @Service 在业务逻辑层使用(service层) @Repository 在数据访问层使用(dao层) @Controller 在展现层使用,控制器的声明 注入bean的注解: @Autowired:由Spring提供 @Inject:由JSR-330提供 @Resource:由JSR-250提供 *扩:JSR 是 java 规范标准 Spring事务管理的方式有几种? 1.编程式事务:在代码中硬编码(不推荐使用)。 2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。 Spring事务中的隔离级别有哪几种? 在TransactionDefinition接口中定义了五个表示隔离级别的常量:ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 Spring事务中有哪几种事务传播行为? 在TransactionDefinition接口中定义了八个表示事务传播行为的常量。 支持当前事务的情况:PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。 不支持当前事务的情况:PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。 其他情况:PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。 二、SpringMVC篇 什么是Spring MVC ?简单介绍下你对springMVC的理解? Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。 Spring MVC的工作原理了解嘛? image.png Springmvc的优点: (1)可以支持各种视图技术,而不仅仅局限于JSP; (2)与Spring框架集成(如IoC容器、AOP等); (3)清晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。 (4) 支持各种请求资源的映射策略。 Spring MVC的主要组件? (1)前端控制器 DispatcherServlet(不需要程序员开发) 作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。 (2)处理器映射器HandlerMapping(不需要程序员开发) 作用:根据请求的URL来查找Handler (3)处理器适配器HandlerAdapter 注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。 (4)处理器Handler(需要程序员开发) (5)视图解析器 ViewResolver(不需要程序员开发) 作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view) (6)视图View(需要程序员开发jsp) View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等) springMVC和struts2的区别有哪些? (1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。 (2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。 (3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。 SpringMVC怎么样设定重定向和转发的? (1)转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4" (2)重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com" SpringMvc怎么和AJAX相互调用的? 通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 : (1)加入Jackson.jar (2)在配置文件中配置json的映射 (3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。 如何解决POST请求中文乱码问题,GET的又如何处理呢? (1)解决post请求乱码问题: 在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8; <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> (2)get请求中文参数出现乱码解决方法有两个: ①修改tomcat配置文件添加编码与工程编码一致,如下: <ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/> ②另外一种方法对参数进行重新编码: String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8") ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。 Spring MVC的异常处理 ? 统一异常处理: Spring MVC处理异常有3种方式: (1)使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver; (2)实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器; (3)使用@ExceptionHandler注解实现异常处理; 统一异常处理的博客:https://blog.csdn.net/ctwy291314/article/details/81983103 SpringMVC的控制器是不是单例模式,如果是,有什么问题,怎么解决? 是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写成员变量。(此题目类似于上面Spring 中 第5题 有两种解决方案) SpringMVC常用的注解有哪些? @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。 @RequestBody:注解实现接收http请求的json数据,将json转换为java对象。 @ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。 SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代? 一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody + @Controller,表示是表现层,除此之外,一般不用别的注解代替。 如果在拦截请求中,我想拦截get方式提交的方法,怎么配置? 可以在@RequestMapping注解里面加上method=RequestMethod.GET。 怎样在方法里面得到Request,或者Session? 直接在方法的形参中声明request,SpringMVC就自动把request对象传入。 如果想在拦截的方法里面得到从前台传入的参数,怎么得到? 直接在形参里面声明这个参数就可以,但必须名字和传过来的参数一样。 如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象? 直接在方法中声明这个对象,SpringMVC就自动会把属性赋值到这个对象里面。 SpringMVC中函数的返回值是什么? 返回值可以有很多类型,有String, ModelAndView。ModelAndView类把视图和数据都合并的一起的。 SpringMVC用什么对象从后台向前台传递数据的? 通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前台就可以拿到数据。 怎么样把ModelMap里面的数据放入Session里面? 可以在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。 SpringMvc里面拦截器是怎么写的: 有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在SpringMvc的配置文件中配置拦截器即可: <!-- 配置SpringMvc的拦截器 --> <mvc:interceptors> <!-- 配置一个拦截器的Bean就可以了 默认是对所有请求都拦截 --> <bean id="myInterceptor" class="com.zwp.action.MyHandlerInterceptor"></bean> <!-- 只针对部分请求拦截 --> <mvc:interceptor> <mvc:mapping path="/modelMap.do" /> <bean class="com.zwp.action.MyHandlerInterceptorAdapter" /> </mvc:interceptor> </mvc:interceptors> 注解原理: 注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池 三、Mybatis篇 什么是MyBatis? MyBatis是一个可以自定义SQL、存储过程和高级映射的持久层框架。 讲下MyBatis的缓存 MyBatis的缓存分为一级缓存和二级缓存,一级缓存放在session里面,默认就有, 二级缓存放在它的命名空间里,默认是不打开的,使用二级缓存属性类需要实现Serializable序列化接口, 可在它的映射文件中配置<cache/> Mybatis是如何进行分页的?分页插件的原理是什么? 1)Mybatis使用RowBounds对象进行分页,也可以直接编写sql实现分页,也可以使用Mybatis的分页插件。 2)分页插件的原理:实现Mybatis提供的接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql。 举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10 简述Mybatis的插件运行原理,以及如何编写一个插件? 1)Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、 Executor这4种接口的插件,Mybatis通过动态代理, 为需要拦截的接口生成代理对象以实现接口方法拦截功能, 每当执行这4种接口对象的方法时,就会进入拦截方法, 具体就是InvocationHandler的invoke方法,当然, 只会拦截那些你指定需要拦截的方法。 2)实现Mybatis的Interceptor接口并复写intercept方法, 然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可, 记住,别忘了在配置文件中配置你编写的插件。 Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不? 1)Mybatis动态sql可以让我们在Xml映射文件内, 以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。 2)Mybatis提供了9种动态sql标签:trim|where|set|foreach|if|choose|when|otherwise|bind。 3)其执行原理为,使用OGNL从sql参数对象中计算表达式的值, 根据表达式的值动态拼接sql,以此来完成动态sql的功能。 #{}和${}的区别是什么? 1)#{}是预编译处理,${}是字符串替换。 2)Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值(有效的防止SQL注入); 3)Mybatis在处理${}时,就是把${}替换成变量的值。 为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里? Hibernate属于全自动ORM映射工具, 使用Hibernate查询关联对象或者关联集合对象时, 可以根据对象关系模型直接获取,所以它是全自动的。 而Mybatis在查询关联对象或关联集合对象时, 需要手动编写sql来完成,所以,称之为半自动ORM映射工具。 Mybatis是否支持延迟加载?如果支持,它的实现原理是什么? 1)Mybatis仅支持association关联对象和collection关联集合对象的延迟加载, association指的就是一对一,collection指的就是一对多查询。 在Mybatis配置文件中, 可以配置是否启用延迟加载lazyLoadingEnabled=true|false。 2)它的原理是,使用CGLIB创建目标对象的代理对象, 当调用目标方法时,进入拦截器方法, 比如调用a.getB.getName, 拦截器invoke方法发现a.getB是null值, 那么就会单独发送事先保存好的查询关联B对象的sql, 把B查询上来,然后调用a.setB(b), 于是a的对象b属性就有值了, 接着完成a.getB.getName方法的调用。 这就是延迟加载的基本原理。 MyBatis与Hibernate有哪些不同? 1)Mybatis和hibernate不同,它不完全是一个ORM框架, 因为MyBatis需要程序员自己编写Sql语句, 不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句, 并将java对象和sql语句映射生成最终执行的sql, 最后将sql执行的结果再映射生成java对象。 2)Mybatis学习门槛低,简单易学,程序员直接编写原生态sql, 可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发, 例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁, 一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性, 如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。 3)Hibernate对象/关系映射能力强,数据库无关性好, 对于关系模型要求高的软件(例如需求固定的定制化软件) 如果用hibernate开发可以节省很多代码,提高效率。 但是Hibernate的缺点是学习门槛高,要精通门槛更高, 而且怎么设计O/R映射,在性能和对象模型之间如何权衡, 以及怎样用好Hibernate需要具有很强的经验和能力才行。 总之,按照用户的需求在有限的资源环境下只要能做出维护性、 扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。 MyBatis的好处是什么? 1)MyBatis把sql语句从Java源程序中独立出来,放在单独的XML文件中编写, 给程序的维护带来了很大便利。 2)MyBatis封装了底层JDBC API的调用细节,并能自动将结果集转换成Java Bean对象, 大大简化了Java数据库编程的重复工作。 3)因为MyBatis需要程序员自己去编写sql语句, 程序员可以结合数据库自身的特点灵活控制sql语句, 因此能够实现比Hibernate等全自动orm框架更高的查询效率,能够完成复杂查询。 简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系? Mybatis将所有Xml配置信息都封装到All-In-One重量级对象Configuration内部。 在Xml映射文件中,<parameterMap>标签会被解析为ParameterMap对象, 其每个子元素会被解析为ParameterMapping对象。 <resultMap>标签会被解析为ResultMap对象, 其每个子元素会被解析为ResultMapping对象。 每一个<select>、<insert>、<update>、<delete> 标签均会被解析为MappedStatement对象, 标签内的sql会被解析为BoundSql对象。 什么是MyBatis的接口绑定,有什么好处? 接口映射就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置. 接口绑定有几种实现方式,分别是怎么实现的? 接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加 上@Select@Update等注解里面包含Sql语句来绑定, 另外一种就是通过xml里面写SQL来绑定,在这种情况下, 要指定xml映射文件里面的namespace必须为接口的全路径名. 什么情况下用注解绑定,什么情况下用xml绑定? 当Sql语句比较简单时候,用注解绑定;当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多 MyBatis实现一对一有几种方式?具体怎么操作的? 有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成; 嵌套查询是先查一个表,根据这个表里面的结果的外键id, 去再另外一个表里面查询数据,也是通过association配置, 但另外一个表的查询通过select属性配置。 Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别? 能,Mybatis不仅可以执行一对一、一对多的关联查询, 还可以执行多对一,多对多的关联查询,多对一查询, 其实就是一对一查询,只需要把selectOne修改为selectList即可; 多对多查询,其实就是一对多查询,只需要把selectOne修改为selectList即可。 关联对象查询,有两种实现方式,一种是单独发送一个sql去查询关联对象, 赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用join查询, 一部分列是A对象的属性值,另外一部分列是关联对象B的属性值, 好处是只发一个sql查询,就可以把主对象和其关联对象查出来。 MyBatis里面的动态Sql是怎么设定的?用什么语法? MyBatis里面的动态Sql一般是通过if节点来实现,通过OGNL语法来实现, 但是如果要写的完整,必须配合where,trim节点,where节点是判断包含节点有 内容就插入where,否则不插入,trim节点是用来判断如果动态语句是以and 或or 开始,那么会自动把这个and或者or取掉。 Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式? 第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。 第二种是使用sql列的别名功能,将列别名书写为对象属性名, 比如T_NAME AS NAME,对象属性名一般是name,小写, 但是列名不区分大小写,Mybatis会忽略列名大小写,
-
南邮OJ Web任务大揭秘:层层挑战剖析 1. 挑战一:迷宫般的目录探索 题目作者似乎穷举了所有可能的目录组合,最终在404.php中的