学习总结(2022.05.17-2022.05.23)
索引 索引是一种帮助提高查询速度的数据结构,可以提高MySQL的查询语句的效率。
索引的数据结构的选择有两个指标:单值查询和范围查询。各个数据结构的查询效率如下:
数组:单值查询慢,范围查询慢。
链表:单值查询慢,范围查询慢。
二叉树:单值查询较慢,范围查询慢。
B树:单值查询较快,范围查询慢。
B+树:单值查询较快,范围查询较快(MySQL官方推荐数据结构)
Hash表:单值查询快,范围查询慢(MySQL内部使用,不开放给用户)
B+树是对B树的一种优化。叶子节点之间维护一个指针,方便了范围查找;所有非叶子节点会在叶子节点中冗余一份,并且自身不再存储数据,而只存储key值,这样就降低了树的高度。
为什么要降低树的高度?因为数据库读取数据时是按树的层序读取,而每一次读取都要经历一次磁盘IO,所以树的高度越低,磁盘IO的次数越少,效率越高。
为什么MySQL使用主键自增策略?因为无序的插入索引会导致树的结构发生较多较大的变化,而改变结构是要消耗时间的。在主键自增的策略下,每次新建的数据的主键索引就永远插入到索引树的右侧,这样就能保证树的结构不会发生较大的改变。
索引是不是声明的越多越好?首先,声明索引需要建立索引树,需要占用空间;其次,每次对数据的增删改都会影响索引树的结构,如果索引树太多势必会导致维护这些索引树的代价提升。所以索引并不是越多越好。
数据库的组成结构 连接器:校验用户名和密码,和客户端建立连接。
分析器:进行语法分析,编译SQL语句。
优化器:优化SQL语句的性能,主要操作为
选择哪个索引进行查询。
多表查询时,选择表连接的顺序。
执行器:执行SQL语句编译后的指令。
存储引擎:存储数据。
存储引擎 目前MySQL使用的引擎有MyISAM和InnoDB。
MyISAM:MyISAM有三种文件,分别是表结构定义文件、数据文件和索引文件。因为它的数据和索引是分开存储的,所以MyISAM的索引叫非聚集索引。索引分为两种类型:主键索引和非主键索引,根据索引树是否是由主键生成来区分。在MyISAM中:
主键索引的key值为主键值,data为数据对应的地址值。
非主键索引的key值为指定的队列值,data也为数据对应的地址值。
InnoDB:InnoDB有两种文件。分别是表结构定义文件、数据/索引文件。因为它的数据和索引放在一个文件中,所以InnoDB的索引叫聚集索引。在InnoDB中:
主键索引的key值为主键值,data为主键对应的数据。不同于MyISAM存储的是数据的地址,因为InnoDB存储的是数据本身,这就要求InnoDB必须有一列主键,否则就无法构建索引树。如果用户建表时不声明主键,那么InnoDB会维护一个隐藏列作为主键。MySQL推荐自定义主键的原因就是如果我们不声明主键,我们也无法通过隐藏的索引列来进行查询,这就浪费了主键索引的性能。
非主键索引的key值为指定的队列值,data为数据对应的主键值。
MyISAM与InnoDB的区别:
InnoDB支持事务,MyISAM不支持事务。
InnoDB支持外键,MyISAM不支持外键。
InnoDB支持表锁和行锁,MyISAM只支持表锁。
那么什么情况下使用MyISAM?因为MyISAM不支持事务,且只支持表锁,会导致增删改查的效率下降,并有发生隔离问题的可能,所以一般存储不需要更改的数据,比如历史数据。
索引语法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 show index from tableName;create table tableName( id int PRIMARY KEY, index idx_name(name) using BTREE )ENGINE= InnoDB character set utf8;show index from tableName;alter table tableName drop index idx_name;alter table tableName add index idx_name;
回表 回表是指在一次查询中,需要根据非主键索引查询出主键值,再根据主键值在主键索引中找到数据,这样就查询了两条索引树,导致查询的效率变低了。
如何避免回表?主要的方式有:
尽量使用主键查询。因为只有使用非主键索引才会发生回表。
尽量避免使用select * from。因为如果准确列出列名,且列名中包含非主键索引,那么该列的值会直接使用非主键索引中的值,而非去使用获得主键,并在主键索引树查询得到的值。
可以考虑使用联合索引。这样可以降低非主键索引树的高度,能更快的查到主键值。
Maven Maven是一个项目管理工具,主要功能是编译项目、将项目打包为jar包、依赖管理。
Maven有以下指令:
clean:删除编译好的项目文件夹target。
validate:验证项目中文件的合法性和权限。
compile:编译项目,生成target文件夹。
test:可以运行test包下的所有测试方法。
package:将项目打包成.jar或.war。
verify:验证打包是否有效且合法。
install:可以将打包生成的jar包复制到本地仓库中。
site:建立远程站点。
deploy:把本地仓库传输到远端部署。
依赖管理 scope作用域:在导入依赖时,可以对依赖所影响的作用域进行限制,使用标签即可。
作用域有以下几种:
test:仅在test包下有效,在main包下无效。
provide:在编译时生效,在运行时失效。主要用于对依赖的版本控制,如对旧依赖使用provide作用域。
runtime:在编译时失效,在运行时生效。主要用于面向接口编程时对编程的规范,如JDBC,在使用JDBC的方法时,由于编译时使用的是java自带的接口,运行时才使用导入的驱动包中的实现类,所以此时编译并不会报错,且可以正常运行。
compile:默认作用域,作用于全局。
依赖传递:依赖具有传递性。如A依赖引入了B依赖,B依赖引入了C依赖,那么A依赖就一定也会引入C依赖。
依赖冲突:在同一个项目中,导入同一依赖的不同版本,就会发生冲突。冲突的解决方法有:
声明优先原则:指同一依赖的不同版本中,谁先在pom.xml声明,就优先使用谁。
就近原则:指在依赖传递时,谁传递的次数比较少,就优先使用谁。就近原则的优先级高于声明优先原则。
手动排除。
提取常量。在pom.xml顶部事先提取依赖版本,这样就可以避免依赖冲突。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <properties > <spring.version > 5.3.3</spring.version > </properties > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-jdbc</artifactId > <version > ${spring.version}</version > </dependency > </dependencies >
清理Maven 在本地仓库下寻找含有lastupdated和part关键字的文件,删除之。
Junit Junit是一种测试工具,可以运行指定的方法。
Junit有以下五种注解:
@Test:单元测试。修饰的方法必须是public void,必须没有参数。
@After:在测试执行后执行。
@Before:在测试执行前执行。
@AfterClass:在测试类类加载前执行。
@BeforeClass:在测试类中的全部方法执行完毕后执行。
其中,@AfterClass、@BeforeClass必须修饰静态方法。
测试类必须放在test包下。
测试类一般命名为XxxTest,测试方法一般命名为testXxx。
Mybatis Mybatis是一种ORM框架。ORM意为对象关系映射,即是将SQL中的数据转化为Java对象,或是将Java对象转化为SQL记录。Mybatis可以帮我们在Java中更高效的操作数据库。
Mybatis需要一个主配置文件,用于获取SqlSessionFactory。SqlSessionFactory时一个工厂,用于生产SqlSession实例对象。SqlSession是一种Mybatis框架与数据库的连接,里面封装了Connection对象。
配置文件样例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <configuration > <settings > <setting name ="logImpl" value ="STDOUT_LOGGING" /> </settings > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="com.mysql.jdbc.Driver" /> <property name ="url" value ="jdbc:mysql://localhost:3306/40th?useSSL=false& characterEncoding=utf8" /> <property name ="username" value ="root" /> <property name ="password" value ="123456" /> </dataSource > </environment > </environments > <mappers > </mappers > </configuration >
其中标签用于放置SQL语句的配置文件,一般命名为XxxMapper.xml,并放置于Maven项目中的src/main/resources文件下,且要与src/main/java中的Mapper接口在相同的路径下。
Mapper配置文件样例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace ="cskaoyan" > <select id ="selectAccountById" resultType ="com.cskaoyan.bean.Account" > select * from account where id = #{id} </select > <select id ="selectAccountList" resultType ="com.cskaoyan.bean.Account" > select * from account </select > </mapper >
Mybatis的动态代理功能可以根据Mapper配置文件和由用户制定的Mapper接口,自动生成接口的代理对象,但是对Mapper接口有如下要求:
接口的全限定名需与Mapper.xml下的标签中的值保持一致。
接口的方法要与Mapper.xml下的 标签 一一对应,并且方法名要和标签的id值保持一致。
方法的返回类型必须与标签中的resultType保持一致。
接口文件名和配置文件名需保持一致,且配置文件在resources文件夹下的相对路径要与接口文件在java文件夹下的相对路径一致。
Mybatis的使用样例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void main (String[] args) throws IOException { InputStream stream = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(stream); SqlSession sqlSession = sqlSessionFactory.openSession(true ); StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class); String name = studentMapper.selectNameById(1 ); System.out.println(name); }
输入映射 在Mapper接口中的参数需要映射至Mapper配置文件中,需要遵守一些规则。
传入的参数在Mapper中需要使用#{XXX}来取值。
如果在Mapper接口中对参数进行了@Param注解修饰别名,那么在Mapper配置文件中必须使用该别名。
如果Mapper接口中传入的是对象,那么在Mapper配置文件中必须使用#{成员变量名}来取值。如果对象有注解,可以通过#{注解值.成员变量名}来取值。
传值时可以使用#{}和${}两种不同的传值方式,其中#{}表示是预编译占位,${}表示是字符串拼接。一般而言,我们都使用#{}传值,但是如果我们需要对SQL语句传入表名和列名时,则需要使用${}。
输出映射 在Mapper配置文件中执行SQL语句后的结果会转换为一个Java对象,转换时也需要遵守一些规则。
映射的类型由Mapper配置文件中SQL语句标签里的resultType或resultMap指定,且Mapper接口中的返回类型要与其一致。
如果返回有多个值,在Mapper接口中需定义数组或集合,但是resultType只需要指定单个元素的类型即可。
如果返回的有多个列,那么需要在java文件中定义一个JavaBean对象来作为返回类型。此时Mybatis会把对象中的成员变量名和查询结果的列名一一映射,如果原始表中的列名与成员变量名不一致,可以通过@Param取别名,或者是resultMap来解决。在声明JavaBean对象的成员变量时,尽量使用包装类型,方便寻找错误。
resultMap一般用于对SQL的查询结果进行再次映射,一般用于修改原始表列名和多表查询。resultMap的使用样例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <resultMap id ="studentMap" type ="com.cskaoyan.bean.Student" > <id column ="id" property ="id" /> <result column ="name" property ="name" /> <result column ="age" property ="age" /> <result column ="gender" property ="gender" /> </resultMap > <select id ="selectStudentByIdWithResultMap" resultMap ="studentMap" > select id,name,age,gender from student where id = #{id}</select >
动态SQL Mybatis可以根据传入的条件,动态的改变SQL语句。
:可以自动拼接where关键字,并去除相邻的and或or关键字;如果where中没有SQL片段需要拼接,不会拼接where关键字。
:可以判断传入的参数是否符合条件。需要注意的是,判断中的大于号和小于号为了避免与xml文件中的标签符号冲突,需要写为>和<。
和:可以提取公共的SQL语句,负责引入中的内容。为了保证SQL语句的可读性,并不会滥用这两个标签,它们一般用于提取列名。
:可以自动拼接set关键字,并去除标签中的最后一个,。
:可以在SQL中循环取值。一般用于in查询。例子如下。
Mapper.java
1 2 List<User> selectUserListByIds (List<Integer> ids) ;
Mapper.xml
1 2 3 4 5 6 7 8 9 <select id ="selectUserListByIds" resultType ="com.cskaoyan.bean.User" > select <include refid ="all_column" /> from user where id in <foreach collection ="collection" item ="id" separator ="," open ="(" close =")" > #{id} </foreach > </select >
: 可以在执行目标SQL语句之前或之后执行一条额外的SQL语句。例子如下。
1 2 3 4 5 6 7 8 9 10 11 <insert id ="insertUserWithReturnId" > insert into user values (null,#{user.name},#{user.age},#{user.nickname})<selectKey order ="AFTER" keyProperty ="user.id" resultType ="int" > select LAST_INSERT_ID()</selectKey > </insert >
useGeneratedKeys:仅在insert语句中使用,可以获取插入数据的自增主键。(注意:此时对应的接口返回值仍然是影响行数,id值则是被放在传入的keyProperty中,上面的同理)例子如下。
1 2 3 4 5 6 7 8 9 <insert id ="insertUserWithReturnIdUseGeneratedKeys" useGeneratedKeys ="true" keyProperty ="user.id" > insert into user values (null,#{user.name},#{user.age},#{user.nickname})</insert >
多表查询 多表查询的方式分为分次查询和连接查询。
首先在建立JavaBean对象时,需要在直接查询的A表所映射的对象中放入B表所映射的对象的成员变量。
分次查询:在Mybatis中,一个SQL语句标签只能执行一句SQL语句,分次查询即是使用resultMap来连接两个SQL语句,用一个SQL语句的结果作为另一个SQL语句的传入值。
分次查询的样例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <select id ="selectUserByName" resultMap ="userMap" > select id,username,nickname,gender,age from user where username = #{name}</select > <resultMap id ="userMap" type ="com.cskaoyan.bean.User" > <id column ="id" property ="id" /> <result column ="username" property ="username" /> <result column ="nickname" property ="nickname" /> <result column ="gender" property ="gender" /> <result column ="age" property ="age" /> <association property ="userDetail" javaType ="com.cskaoyan.bean.UserDetail" select ="com.cskaoyan.mapper.UserDetailMapper.selectUserDetailById" column ="id" /> <select id ="selectUserDetailById" resultType ="com.cskaoyan.bean.UserDetail" > select id,user_id as userId,height,weight,pic from user_detail where user_id = #{id} </select >
连接查询:连接查询即是利用inner join和resultMap来连接表。
连接查询的样例如下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <select id ="selectUserByNameUseCrossQuery" resultMap ="userCrossMap" > SELECT user.id, user.username, user.nickname, user.gender, user.age, user_detail.id as uid, user_detail.user_id as userId, user_detail.weight as weight, user_detail.height as height, user_detail.pic as pic FROM user left JOIN user_detail ON user.id = user_detail.user_id WHERE user.username = #{name}</select > <resultMap id ="userCrossMap" type ="com.cskaoyan.bean.User" > <id column ="id" property ="id" /> <result column ="username" property ="username" /> <result column ="nickname" property ="nickname" /> <result column ="gender" property ="gender" /> <result column ="age" property ="age" /> <association property ="userDetail" javaType ="com.cskaoyan.bean.UserDetail" > <id column ="uid" property ="id" /> <result column ="userId" property ="userId" /> <result column ="weight" property ="weight" /> <result column ="height" property ="height" /> <result column ="pic" property ="pic" /> </association > </resultMap >
懒加载和缓存 懒加载是指在Mybatis进行分次查询的时候,假如第二次查询的内容没有被使用到的话,那么就不去执行第二次查询的SQL语句,等到用到第二次查询的内容的时候再去执行第二条SQL语句。
Mybatis的默认配置是关闭懒加载,要开启懒加载需要在Mybatis的配置文件中加入如下配置。
1 2 3 4 5 6 <settings > <setting name ="lazyLoadingEnabled" value ="true" /> </settings >
缓存是指在Mybatis中,单独开辟一块内存空间(map),来存储查询的信息。后续假如再次调用了到了同样的查询,那么就直接查询缓存。
缓存分为一级缓存和二级缓存。
一级缓存是一个SqlSession级别的缓存。缓存的内容存储在SQLSession中。一级缓存是默认开启的,且无法关闭,只有在SqlSession关闭时,或者SqlSession调用commit方法会让一级缓存失效。
二级缓存是一个NameSpace级别的缓存,每一个NameSpace都有自己的缓存空间。二级缓存是默认开启的。
在实际工作中,Mybatis的缓存机制并不好用,主要是它无法解决脏数据的问题,且用户不能直接操作缓存,所以在实际工作中会使用Redis替代。