目录
1.2 从SqlSessionFactory获取SqlSession
一、部署MySQL
(1)下载MySQL安装包
(2)将安装包解压到F盘MySQL文件夹
(3)复制一份后缀为.ini的文件,更名为“my.ini”,修改basedir与datadir并保存。
# basedir =F:\MySQL\MySQL 5.6.36 //安装的目录
# datadir =F:\MySQL\MySQL 5.6.36\data //MySQL数据库中数据的存放
(4)右键“我的电脑”----->属性------>高级系统设置------>环境变量------>打开系统变量“Path”,在最后加上“;F:\MySQL\MySQL 5.6.36”------>保存
(5)以管理员身份运行cmd------>f:------->cd MySQL------>cd MySQL 5.6.36------>cd bin------>mysqld -install------>安装成功
(6)打开MySQL:以管理员身份运行cmd------>f:------->cd MySQL------>cd MySQL 5.6.36------>cd bin------>mysql -u root -p
二、数据库
1. SQL语句
(1)查询语句
①select 列名称 from 表名
②select * from 表名
①insert into 表名 values (值1, 值2,....)
②insert into 表名 (列1, 列2,...) values (值1, 值2,....)
(3)修改语句
update 表名称set 列名=新值 where 列名=某值
delete from 表名where 列名=值
(5)消除重复值:关键词distinct用于返回唯一不同的值。
select distinct 列名 from 表名
select 列名 from 表名 where 列 运算符 值
若值为文本值,需使用单引号来环绕文本值;如果是数值,要使用引号。
and和or可在where子语句中把两个或多个条件结合起来。
如果第一个条件和第二个条件都成立,则and运算符显示一条记录;如果第一个条件和第二个条件中只要有一个成立,则or运算符显示一条记录。
order by语句用于根据指定的列对结果集进行排序。order by语句默认按照升序对记录进行排序。
按照降序对记录进行排序,可以使用desc关键字;按照升序对记录进行排序,可以使用esc关键字。
select top 50 percent * from persons // 从persons表中选取50%的记录
(10)like操作符:like操作符用于在where子句中搜索列中的指定模式。
select * from persons where city like 'n%' // 从persons表中选取n 开始的城市的人,"%"可用于定义通配符(模式中缺少的字母),'%g':以g结尾,'%lon%':包含lon,not like:不包含
(11)in操作符:in操作符允许我们在where子句中规定多个值。
select 列名 from 表名 where 列名 in(value1,value2,...)
(12)between操作符:between操作符在where子句中使用,作用是选取介于两个值之间的数据范围。
select 列名 from 表名 where 列名 between value1 and value2
①select 列名 from 表名 as 表的别名
②select 列名 as 列的别名 from 表名
(14)join:用于根据两个或多个表中的列之间的关系,从这些表中查询数据。
数据库中的表可通过键将彼此联系起来。主键(primary key)是一个列,在这个列中的每一行的值都是唯一的。在表中,每个主键的值都是唯一的。这样做的目的是在不重复每个表中的所有数据的情况下,把表间的数据交叉捆绑在一起。
"id_p" 列是persons 表中的的主键,"id_o" 列是 orders 表中的的主键
①selectpersons.lastname,persons.firstname,orders.ordernofrompersons, orderswhere persons.id_p = orders.id_p
②selectpersons.lastname,persons.firstname,orders.orderno from persons inner join orders on persons.id_p =orders.id_p order by persons.lastname
(15)inner join :在表中存在至少一个匹配时,inner join关键字返回行。
select 列名 from 表1 inner join 表2 on 表1.列名=表2.列名
(16)left join:left join关键字会从左表(表1) 那里返回所有的行,即使在右表 (表2)中没有匹配的行。
select 列名 from 表1 left join 表2 on 表1.列名=表2.列名
(17)right join:right join 关键字会右表 (表2) 那里返回所有的行,即使在左表(表1) 中没有匹配的行。
select 列名 from 表1 right join 表2 on 表1.列名=表2.列名
(18)full join:只要其中某个表存在匹配,full join关键字就会返回行。
select 列名 from 表1 full join 表2 on 表1.列名=表2.列名
(19)union:union 操作符用于合并两个或多个 select语句的结果集。union内部的 select语句必须拥有相同数量的列,列也必须拥有相似的数据类型。同时,每条select语句中的列的顺序必须相同。union结果集中的列名总是等于union中第一个select语句中的列名。
①select 列名 from 表1 union select 列名 from 表2
②select 列名 from 表1 union all select 列名 from 表2
(20)select into:select into语句从一个表中选取数据,然后把数据插入另一个表中,常用于创建表的备份复件或者用于对记录进行存档。
①将所有列加入新表
select * into 新表 [in externaldatabase] from 旧表
②只把某些列加入新表
select 列名 into 新表 [in externaldatabase] from 旧表
create database 数据库名
create table 表名
(
列名称1 数据类型,
列名称2 数据类型,
列名称3 数据类型,
....
)
(23)drop:通过使用drop语句,可以删除索引、表和数据库。
①droptable语句用于删除表(表的结构、属性以及索引也会被删除)
drop table 表名称
②dropdatabase 语句用于删除数据库
drop database 数据库名称
③仅需除去表内的数据,但并不删除表本身
truncate table 表名称
④删除表格中的索引
alter table 表名 drop index 索引名
(24)alter:alter table语句用于在已有的表中添加、修改或删除列。
①在表中添加列
alter table 表名 add 列名 datatype
②删除表中的列
alter table 表名 drop column 列名
③改变表中列的数据类型
alter table 表名 alter column 列名 datatype
select * from 表名 limit arg1,arg2
arg1指定查询记录的起始位置,arg2用于指定查询数据所返回的记录数。
(26)多表查询
①左连接SQL语句:左外连接包含left join左表所有行,如果左表中某行在右表没有匹配,则结果中对应行右表的部分全部为空(NULL)
SELECT * FROM one LEFT JOIN two ON one.id =two.id
②右连接SQL语句:右外连接包含right join右表所有行,如果左表中某行在右表没有匹配,则结果中对应左表的部分全部为空(NULL)。
SELECT * FROM one RIGHT JOIN two ON one.id =two.id
③内连接SQL语句:inner join 是比较运算符,只返回符合条件的行。
SELECT * FROM one INNER JOIN two ON one.id=two.id
相当于
SELECT * FROM one,two WHERE one.id=two.id
④交叉连接
SELECT * FROM one CROSS JOIN two WHERE one.id=two.id
⑤USING (column_list):其作用是为了方便书写连接的多对应关系,大部分情况下USING语句可以用ON语句来代替。
SELECT * FROM one JOIN two USING(id)
相当于
SELECT * FROM one LEFT JOIN two ON one.id =two.id
2. 索引
2.1 索引概念
索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度。索引包含由表或视图中的一列或多列生成的键。
2.2 如何使用
(1)直接创建
使用create index语句或者使用创建索引向导来创建索引,可以定制创建出符合自己需要的索引。在使用这种方式创建索引时,可以使用许多选项,例如指定数据页的充满度、进行排序、整理统计信息等,这样可以优化索引。使用这种方法,可以指定索引的类型、唯一性和复合性,也就是说,既可以创建聚簇索引,也可以创建非聚簇索引,既可以在一个列上创建索引,也可以在两个或者两个以上的列上创建索引。
创建普通索引:CREATE INDEX usernameON LOGIN_INFO(username)
创建唯一索引:CREATE UNIQUE INDEX id ON login_info(id)
删除索引:DROP INDEX username ON login_info
(2)间接创建
通过定义主键约束或者唯一性键约束,也可以间接创建索引。
主键约束是一种保持数据完整性的逻辑,它限制表中的记录有相同的主键记录。在创建主键约束时,系统自动创建了一个唯一性的聚簇索引。在逻辑上,主键约束是一种重要的结构,在物理结构上,与主键约束相对应的结构是唯一性的聚簇索引。换句话说,在物理实现上,不存在主键约束,而只存在唯一性的聚簇索引。
在创建唯一性键约束时,也同时创建了索引,这种索引则是唯一性的非聚簇索引。因此,当使用约束创建索引时,索引的类型和特征基本上都已经确定了,由用户定制的余地比较小。
当在表上定义主键或者唯一性键约束时,如果表中已经有了使用create index语句创建的标准索引时,那么主键约束或者唯一性键约束创建的索引覆盖以前创建的标准索引。也就是说,主键约束或者唯一性键约束创建的索引的优先级高于使用create index语句创建的索引。
2.3 优缺点
(1)优点(作用)
①通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
②可以大大加快数据的检索速度,这也是创建索引的最主要的原因。
③可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
④在使用分组和排序 子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。
⑤通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能。
(2)缺点
①创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
②索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
③当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
2.4 需要创建索引的情况
(1)在经常需要搜索的列上,可以加快搜索的速度;
(2)在作为主键的列上,强制该列的唯一性和组织表中数据的排列结构;
(3)在经常用在连接的列上,这些列主要是一些外键,可以加快连接的速度;
(4)在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,其指定的范围是连续的;
(5)在经常需要排序的列上创建索引,因为索引已经排序,这样查询可以利用索引的排序,加快排序查询时间;
(6)在经常使用在where子句中的列上面创建索引,加快条件的判断速度。
2.5 不应该使用索引的情况
(1)对于那些在查询中很少使用或者参考的列不应该创建索引。既然这些列很少使用到,因此有索引或者无索引,并不能提高查询速度。相反,由于增加了索引,反而降低了系统的维护速度和增大了空间需求。
(2)对于那些只有很少数据值的列也不应该增加索引。由于这些列的取值很少,例如人事表的性别列,在查询的结果中,结果集的数据行占了表中数据行的很大比例,即需要在表中搜索的数据行的比例很大。增加索引,并不能明显加快检索速度。
(3)对于那些定义为text, image和bit数据类型的列不应该增加索引。这些列的数据量要么相当大,要么取值很少。
(4)当修改性能远远大于检索性能时,不应该创建索引。修改性能和检索性能是互相矛盾的。当增加索引时,会提高检索性能,但是会降低修改性能。当减少索引时,会提高修改性能,降低检索性能。
2.6 索引的特征 (唯一性索引和复合索引)
(1)唯一性索引
唯一性索引保证在索引列中的全部数据是唯一的,不会包含冗余数据。如果表中已经有一个主键约束或者唯一性键约束,那么当创建表或者修改表时,SQL Server自动创建一个唯一性索引。然而,如果必须保证唯一性,那么应该创建主键约束或者唯一性键约束,而不是创建一个唯一性索引。
当创建唯一性索引时,应该考虑这些规则:
①当在表中创建主键约束或者唯一性键约束时,SQL Server自动创建一个唯一性索引;
②如果表中已经包含有数据,那么当创建索引时,SQL Server检查表中已有数据的冗余性;
③每当使用插入语句插入数据或者使用修改语句修改数据时,SQL Server检查数据的冗余性:如果有冗余值,那么SQL Server取消该语句的执行,并且返回一个错误消息;
④确保表中的每一行数据都有一个唯一值,这样可以确保每一个实体都可以唯一确认;
⑤只能在可以保证实体完整性的列上创建唯一性索引。
(2)复合索引
复合索引就是一个索引创建在两个列或者多个列上。在搜索时,当两个或者多个列作为一个关键值时,最好在这些列上创建复合索引。
当创建复合索引时,应该考虑这些规则:
①最多可以把16个列合并成一个单独的复合索引,构成复合索引的列的总长度不能超过900字节,也就是说复合列的长度不能太长;
②在复合索引中,所有的列必须来自同一个表中,不能跨表建立复合列;
③在复合索引中,列的排列顺序是非常重要的,因此要认真排列列的顺序,原则上,应该首先定义最唯一的列,例如在(col1,col2)上的索引与在(col2,col1)上的索引是不相同的,因为两个索引的列的顺序不同;
④为了使查询优化器使用复合索引,查询语句中的where子句必须参考复合索引中第一个列;
⑤当表中有多个关键列时,复合索引是非常有用的;
⑥使用复合索引可以提高查询性能,减少在一个表中所创建的索引数量。
2.7 mysql索引何时生效
查询的时候生效。
3. 常用函数
(1)内建SQL函数
select function(列) from 表
在SQL中,基本的函数类型和种类有若干种。函数的基本类型是:aggregate函数和scalar函数。
合计函数(aggregate functions):aggregate函数的操作面向一系列的值,并返回一个单一的值。
scalar函数:scalar函数的操作面向某个单一的值,并返回基于输入值的一个单一的值。
(2)avg()函数:avg函数返回数值列的平均值。null值不包括在计算中。
select avg(列名) from 表名
(3)count()函数:count() 函数返回匹配指定条件的行数。
①count(列名)函数返回指定列的值的数目(null不计入)
select count(列名) from 表名
②count(*)函数返回表中的记录数
select count(*) from 表名
③count(distinct列名)函数返回指定列的不同值的数目
select count (distinct 列名) from 表名
(4)first()函数:first()函数返回指定的字段中第一个记录的值。可使用order by语句对记录进行排序。
select first(列名) from 表名
(5)last()函数:last()函数返回指定的字段中最后一个记录的值。可使用order by语句对记录进行排序。
select last(列名) from 表名
(6)max()函数:max函数返回一列中的最大值。null值不包括在计算中min和max也可用于文本列,以获得按字母顺序排列的最高或最低值。
select max(列名) from 表名
(7)min()函数:min函数返回一列中的最小值。null值不包括在计算中。min和max也可用于文本列,以获得按字母顺序排列的最高或最低值。
select min(列名) from 表名
select sum(列名) from 表名
(9)group by语句:group by语句用于结合合计函数,根据一个或多个列对结果集进行分组。
select 列名,aggregate_function(列名) from 表名 where 列名 operator value group by 列名
(10)round()函数:round函数用于把数值字段舍入为指定的小数位数。
select round(列名,decimals) from 表名
(11)concat()函数:将两个或多个字符串组合成一个字符串。
concat(string1,string2, ... );
(12)length()函数、char_length()函数:以字节和字符获取字符串的长度。
length(str);
(13)left()函数:获取指定长度的字符串的左边部分。
left(str,length);
①str是要提取子字符串的字符串。
②length是一个正整数,指定将从左边返回的字符数。
left()函数返回str字符串中最左边的长度字符。如果str或length参数为null,则返回null值。如果length为0或为负,则left函数返回一个空字符串;如果length大于str字符串的长度,则left函数返回整个str字符串。
(14)replace()函数:搜索并替换字符串中的子字符串。
replace(str,old_string,new_string);
将string中的old_string替换为new_string字符串。
(15)substring()函数:从具有特定长度的位置开始提取一个子字符串。
substring(string,position);
①string参数是要提取子字符串的字符串。
②position参数是一个整数,用于指定子串的起始字符,position可以是正或负整数。
(16)trim()函数:从字符串中删除不需要的字符。返回一个字符串,删除不需要的字符。
trim([{both|leading|trailing} [removed_str]] from str);
①可以使用leading,trailing或both选项明确指示trim()函数从字符串中删除前导,尾随或前导和尾随的不必要的字符。没有指定任何内容,trim()函数默认使用both选项。
②[removed_str]是要删除的字符串。默认情况下,它是一个空格。这意味着如果不指定特定的字符串,则trim()函数仅删除空格。
③str是要删除子字符removed_str的字符串。
(17)format()函数:格式化具有特定区域设置的数字,舍入到小数位数。
fomrat(N,D,locale);
①N是要格式化的数字。
②D是要舍入的小数位数。
③locale是一个可选参数,用于确定千个分隔符和分隔符之间的分组。如果省略locale操作符,MySQL将默认使用en_US。
(18)datediff()函数:计算两个DATE值之间的天数。
datediff(date_expression_1,date_expression_2);
datediff函数接受两个任何有效日期或日期时间值的参数。如果传递DATETIME或TIMESTAMP值,则datediff函数仅将日期部分用于计算,并忽略时间部分。
(19)date_format()函数:根据指定的日期格式格式化日期值。
date_format(date,format);
①date:是要格式化的有效日期值
②format:是由预定义的说明符组成的格式字符串,每个说明符前面都有一个百分比字符(%)。
(20)extract()函数:提取日期的一部分。
extract(unit FROM date)
①unit是要从日期中提取的间隔。
(21)now()函数 - 返回当前日期和时间。
(22)str_to_date()函数:将字符串转换为基于指定格式的日期和时间值。
str_to_date(str,fmt);
①str_to_date()根据fmt格式字符串将str字符串转换为日期值。str_to_date()函数可能会根据输入和格式字符串返回date,time或datetime值。如果输入字符串是非法的,则str_to_date()函数返回null。
②str_to_date()函数扫描输入字符串来匹配格式字符串。格式字符串可能包含以百分比(%)字符开头的文字字符和格式说明符。
(23)sysdate()函数 - 返回当前日期。
sysdate(fsp);
如果函数用于字符串上下文或YYYYMMDDHHMMSS格式,则sysdate()函数将返回当前日期时间,格式为“YYYY-MM-DD HH:MM:SS”的值,以防在函数用于数字上下文。
sysdate()函数接受一个可选参数fsp,它确定结果是否应该包含从0到6的小数秒精度。
4. 死锁
4.1 概念
指多个进程在运行过程中因争夺资源而造成的一种僵局。若无外力作用,这些进程都将永远不能再向前推进。死锁一般是事务互相等待对方资源,最后形成环路造成的。
4.2 产生死锁的原因
(1)竞争资源:当系统中多个进程使用共享资源,并且资源不足以满足需要,会引起进程对资源的竞争而产生死锁。
(2)进程间推进的顺序非法:请求和释放资源的顺序不当,也同样会导致产生进程死锁。
4.3 避免死锁的方法
(1)以固定的顺序访问表。
(2)大事务拆小事务。大事务更倾向于死锁,如果业务允许,将事务拆小。
(3)在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
(4)降低隔离级别。如果业务允许,比如将隔离级别从RR调整为RC,可以避免很多因为gap锁造成的死锁。
(5)为表添加合理的索引。如果不使用索引将会为表的每一行记录添加上锁,死锁的概率大大增大。
5. SQL注入
5.1 什么是SQL注入
SQL注入就是把SQL命令插入到Web表单然后提交到所在页面请求(查询字符串),从而达到欺骗服务器执行恶意的SQL命令。它是利用现在已有的应用程序,将SQL语句插入到数据库中执行,执行一些并非按照设计者意图的SQL语句。
5.2 产生原因
程序没有细致过滤用户输入的数据,从而导致非法数据进入系统。
(1)执行外部数拼接的SQL语句。
(2)执行外部传入的整条SQL语句
(3)在配置文件中的SQL语句没有使用预编译方式占位符。
(4)校验函数有缺陷或占位符使用错误
5.3 如何避免SQL注入
三、数据库事务
1. 查看隔离级别
select @@tx_isolation
select @@global.tx_isolation
set session transaction isolatin level repeatable read
set global transaction isolation level repeatable read
2. MySQL事务命令
取消自动事务提交 :set autocommit=0;
手动开启事务:start transaction;
commit;
3. 事务的4个特性
(1)原子性:原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。
(2)一致性:一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
(3)隔离性:隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
(4)持久性:持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
4. 七种事务传播界别
(1) propagation_required :默认的spring事务传播级别。如果上下文中已经存在事务,那么就加入到事务中执行;如果当前上下文中不存在事务,则新建事务执行。这个级别通常能满足处理大多数的业务场景。
(2)propagation_supports :如果上下文存在事务,则支持事务加入事务;如果没有事务,则使用非事务的方式执行。并非所有的包在transactionTemplate.execute中的代码都会有事务支持。通常是用来处理那些并非原子性的非核心业务逻辑操作。
(3)propagation_mandatory:该级别的事务要求上下文中必须要存在事务,否则就会抛出异常。配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
(4)propagation_requires_new :,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
(5)propagation_not_supported :上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。可以帮助你将事务极可能的缩小。
(6)propagation_never :要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行。
(7)propagation_nested :嵌套级别事务。如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
5. 事务隔离级别
(1)Serializable (序列化):最高的事务隔离级别,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
(2)Repeatable Read(可重复读):重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。
(3)Read Committed(读提交):一个事务要等另一个事务提交后才能读取数据。
(4)Read Uncommitted(读未提交(脏读)):一个事务可以读取另一个未提交事务的数据。
MySQL的默认事务隔离级别是Repeatable Read
Oracle的默认事务隔离级别是Read Committed
更新丢失:两事务同时更新,一个失败回滚覆盖另一个事务的更新。
脏读:事务T1读取到事务T2修改了但是还未提交的数据,之后事务T2又回滚其更新操作,导致事务T1读到的是脏数据。
不可重复读:事务T1读取某个数据后,事务T2对其做了修改,当事务T1再次读该数据时得到与前一次不同的值。
虚读(幻读):事务T1读取在读取某范围数据时,事务T2又插入一条数据,当事务T1再次数据这个范围数据时发现不一样了,出现了一些“幻影行”。
不可重复读和脏读的区别:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。
幻读和不可重复读的异同:都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。不可重复读的重点是修改,幻读的重点是新增、修改。
6. MySQL查看执行计划
explain sql语句
例:EXPLAIN SELECT * FROM login_info
(1)id:包含一组数字,表示查询中执行select子句或操作表的顺序; 执行顺序从大到小执行;当id值一样的时候,执行顺序由上往下。
(2)select_type:表示查询中每个select子句的类型。
simple:查询中不包含子查询或者union。
primary:查询中若包含任何复杂的子部分,最外层查询则被标记为primary。
subquery:在select或where列表中包含了子查询,该子查询被标记为subquery。
derived:在from列表中包含的子查询被标记为derived(衍生)。
若第二个select出现在union之后,则被标记为union。
若union包含在from子句的子查询中,外层select将被标记为:derived。
从union表获取结果的select被标记为:union result。
(3)table:显示这一行的数据是关于哪张表的。
(4)type:表示MySQL在表中找到所需行的方式,又称“访问类型”。
ALL:Full Table Scan, MySQL将进行全表扫描。
index:Full Index Scan,index与ALL区别为index类型只遍历索引树。
range:range Index Scan,对索引的扫描开始于某一点,返回匹配值域的行,常见于between、<、>等的查询。
ref:非唯一性索引扫描,返回匹配摸个单独值的所有行。常见于使用非唯一索引或唯一索引的非唯一前缀进行的查找。
eq_ref:唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描。
const、system:当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。
NULL:MySQL在优化过程中分解语句,执行时甚至不用访问表或索引。
(5)possible_keys:指出MySQL能使用哪个索引在表中找到行,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用;
(6)key:显示MySQL在查询中实际使用的索引,若没有使用索引,显示为NULL。当查询中若使用了覆盖索引,则该索引仅出现在key列表中。
(7)key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。
(8)ref:表示上述表的连接匹配条件,即那些列或常量被用于查找索引列上的值。
(9)rows:表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数。
(10)Extra:包含不适合在其他列中显示但十分重要的额外信息。
Using where:表示MySQL服务器在存储引擎受到记录后进行“后过滤”(Post-filter),如果查询未能使用索引,Using where的作用只是提醒我们MySQL将用where子句来过滤结果集。
Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询。
Using filesort:MySQL中无法利用索引完成的排序操作称为“文件排序”。
非select语句查看执行计划
不能直接通过explain来进行查看,而需要通过改写语句进行查看执行计划。
四、存储过程
存储过程(procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过指定存储过程的名字并给定参数(如果该存储过程带有参数)来调用执行它。一个存储过程是一个可编程的函数,它在数据库中创建并保存。它可以有SQL语句和一些特殊的控制结构组成。当希望在不同的应用程序或平台上执行相同的函数,或者封装特定功能时,存储过程是非常有用的。数据库中的存储过程可以看做是对编程中面向对象方法的模拟。它允许控制数据的访问方式。
1. 存储过程格式
create procedure 存储过程名称 (参数列表)
begin
SQL语句
end
//begin、end代表SQL语句的开始和结束
2. 分隔符
MySQL默认以“;”语句结束符(分隔符),如果没有声明分隔符,则编译器会把存储过程当成SQL语句进行处理,因此编译过程会报错,所以要事先用“DELIMITER &&”声明当前段分隔符,让编译器把两个“&&”之间的内容当做存储过程的代码,不会执行这些码;“DELIMITER ;”的意为把分隔符还原。
3. 存储过程操作
(1)简单的存储过程
delimiter &&
create procedure p1()
begin
select * from course;
end &&
(2)调用存储过程p1
call p1 &&
show procedure status \G //\G横向显示
(4)带一个参数的存储过程
create procedure p3(i int)
begin
select * from course where cno>i;
end &&
(5)调用带一个参数的存储过程
call p1(3) &&
(6)带两个参数的存储过程(if...else语句)
create procedure p2(i int,j int)
begin
if j='k' then
select * from course where cno>i;
else
select * from course where cno<i;
end if;
end &&
(7)查看带两个个参数的存储过程
call p2(4,3) &&
(8)删除存储过程
call p2(4,3) &&
(9)定义变量
declare i int;
(10)给存储变量赋值
set i=i+1;
4. 求和1~n(while语句)
create procedure p3(m smallint)
begin
declare i int;
declare j int;
set i=1;
set j=0;
while i<=m do
set j=j+i;
set i=i+1;
end while;
select j;
end &&
计算1~100的和
4.5 JDBC调用存储过程
JDBC调用存储过程,通过CallableStatement对象进行操作。CallableStatement对象位于java.sql包中,它继承于Statement对象,主要用于执行数据库中定义的存储过程,其调用方法如下:
{call 存储过程名称(?)} //?:占位符,个数根据存储过程参数来定
(1)调用带一个参数的存储过程p3
String sql="call p3(?)";
(2)创建CallableStatement对象
CallableStatement cs=null;
cs=(CallableStatement) conn.prepareCall(sql);
(3)给存储过程参数赋值
cs.setInt(1, 100);
(4)执行存储过程
rs=cs.executeQuery();
(5)调用存储过程p3(求和1~n),求1~100的和。
import com.mysql.jdbc.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
public class StoredProcedure {
public static void main(String[] args) {
Connection conn = null;
CallableStatement cs = null;
ResultSet rs = null;
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/test";
String username = "root";
String password = null;
conn = DriverManager.getConnection(url, username, password);
String sql = "call p3(?)";
cs = (CallableStatement) conn.prepareCall(sql);
cs.setInt(1, 100);
rs = cs.executeQuery();
if (rs.next()) {
System.out.println(rs.getInt(1));
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (rs != null) {
rs.close();
}
if (cs != null) {
cs.close();
}
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
五、JDBC
1.知识要点
JDBC(Java Data Base Connectivity,java数据库链接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。
JDBC库包含的API为每个通常与数据库使用相关联的任务:连接到数据库、创建SQL或MySQL语句、执行QL或MySQL查询数据库、查看和修改结果记录。
1.1 JDBC架构
JDBC API支持两层和三层处理模式进行数据库访问,但在一般的JDBC体系结构由两层组成:JDBC API:提供了应用程序对JDBC的管理链接,JDBC API的使用驱动程序管理器和数据库特定的驱动程序提供透明的连接到异构数据库;JDBC Driver API:支持JDBC管理到驱动器连接;JDBC 驱动程序管理器可确保正确的驱动程序来访问每个数据源,该驱动程序管理器能够支持连接到多个异构数据库的多个并发的驱动程序。
1.2 常用接口
(1)Driver接口:此接口处理与数据库服务器通信。
Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。
例:装载MySQL驱动:Class.forName(“com.mysql.jdbc.Driver”);
装载Oracle驱动:Class.forName(“oracle.jdbc.Driver.OracleDriver”);
(2)Connecttion接口:与数据库中的所有的通信是通过唯一的连接对象。
Connecttion与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url,user,password)方法建立在JDBC URL中定义的数据库Connecttion连接上。
例:连接MySQL数据库:
Connection conn=DriverManager.getConnection(“jdbc:mysql://host:port/database”,“user”,“password”);
连接Oracle数据库:
Connection conn=DriverManager.getConnection(“jdbc:oracle:thin:@host:port/database”,“user”,“password”);
连接SQLServer数据库:
Connection conn=DriverManager.getConnection(“jdbc:microsoft:sqlserver://host:port;DatabaseName=database”,“user”,“password”);
常用方法:
createStatement():创建向数据库发送sql的statement对象。
prepareStatement(sql):创建向数据库发送预编译sql的PrepareStatement对象。
prepareCall(sql):创建执行存储过程的callableStatement对象。
setAutoCommit(boolean autoCommit):设置事务是否自动提交。
commit():在链接上提交事务。
rollback():在此链接上回滚事务。
(3)Statement接口:可以使用这个接口创建的对象的SQL语句提交到数据库。一些派生的接口接受执行存储过程的参数。
Statement:由createStatement创建,用于发送简单的sql语句(不带参数)。
PrepareStatement:继承自Statement接口,有PrepareStatement创建,用于发送含有一个或多个参数的sql语句。PrepareStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以一般使用PrepareStatement。
CallableStatement:继承自PrepareStatement接口,由方法prepareCall创建,用于调用存储过程。
常用Statement方法:
execute(String sql):运行语句,返回是否有结果集。
executeQuery(String sql):运行select语句,返回ResultSet结果集。
executeUpdata(String sql):运行insert/updata/delete操作,返回更新的行数。
addBatch(String sql):把多条sql语句放到一个批处理中。
executeBatch():向数据库发送一批sql语句执行。
(4)ResultSet接口:这些对象保存在数据库后,执行使用Statement对象的SQL查询中检索数据。
ResultSet提供检索不同类型字段的方法,常用的有:
getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。
getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
getData(int index)、getData(String columnName):获得在数据库里是Data类型的数据。
getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据。
getObject(int index)、getObject(String columnName):获得在数据库里任意类型的数据。
ResultSet还提供了对结果集进行滚动的方法:
next():移动到下一行。
Previous():移动到前一行。
absolute(int row):移动到指定行。
beforeLast():移动到ResultSet的最前面。
afterLast():移动到ResultSet的最后面。
(5)使用后依次关闭对象及连接:ResultSet----->Statement----->Connection
(6)SQLException:这个类处理发生在一个数据库应用程序的任何错误。
2. 操作流程
2.1 连接数据库
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test";
String username = "root";
String password = null;
2.2 初始化驱动程序
Class.forName("com.mysql.jdbc.Driver");
2.3 创建一个Connection对象,建立连接
conn = (Connection) DriverManager.getConnection(url, username, password);
2.4 如何发送SQL语句
(1)创建Statement对象
PreparedStatement ps;
ps=(PreparedStatement)conn.prepareStatement(sql);
(2)使用Statement对象执行语句
方法1:executeUpdate()方法
int i=ps.executeUpdate();
方法2:executeQuery()方法
ResultSet r=ps.executeQuery();
方法3:execute()方法,仅在语句能返回多个ResultSet对象、多个更新计数或ResultSet对象与更新计数的组合时使用。
(3)语句完成。
(4)关闭Statement对象。
3. 增删改查代码实例
import com.mysql.jdbc.PreparedStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
class Course {
private int cno;
private String cname;
private int cpno;
private int ccredit;
Course(int cno, String cname, int cpno, int ccridte) {
this.cno = cno;
this.cname = cname;
this.cpno = cpno;
this.ccredit = ccridte;
}
public int getCno() {
return cno;
}
public void setCno(int cno) {
this.cno = cno;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public int getCpno() {
return cpno;
}
public void setCpno(int cpno) {
this.cpno = cpno;
}
public int getCcredit() {
return ccredit;
}
public void setCcredit(int ccredit) {
this.ccredit = ccredit;
}
}
public class JDBCDemo01 {
private static Connection getConn() {
String driver = "com.mysql.jdbc.Driver";
String url = "jdbc:mysql://localhost:3306/test";
String username = "root";
String password = null;
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url, username, password);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
private static int insert(Course course) {
Connection conn = getConn();
int i = 0;
String sql = "insert into course(cno,cname,cpno,ccredit) values(?,?,?,?)";
PreparedStatement ps;
try {
ps = (PreparedStatement) conn.prepareStatement(sql);
ps.setInt(1, course.getCno());
ps.setString(2, course.getCname());
ps.setInt(3, course.getCpno());
ps.setInt(4, course.getCcredit());
i = ps.executeUpdate();
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
}
private static int update(Course course) {
Connection conn = getConn();
int i = 0;
String sql = "update course set cno='" + course.getCno() + "'where cname='" + course.getCname() + "'";
PreparedStatement ps;
try {
ps = (PreparedStatement) conn.prepareStatement(sql);
i = ps.executeUpdate();
System.out.println(i);
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
}
private static int delete(int cno) {
Connection conn = getConn();
int i = 0;
String sql = "delete from course where cno=" + cno;
PreparedStatement ps;
try {
ps = (PreparedStatement) conn.prepareStatement(sql);
i = ps.executeUpdate();
System.out.println(i);
ps.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
return i;
}
private static Integer getAll() {
Connection conn = getConn();
String sql = "select * from course";
PreparedStatement ps;
try {
ps = (PreparedStatement) conn.prepareStatement(sql);
ResultSet r = ps.executeQuery();
int c = r.getMetaData().getColumnCount();
System.out.println("-------------------");
while (r.next()) {
for (int i = 1; i < c; i++) {
System.out.print(r.getString(i) + "\t");
if ((i == 4) && (r.getString(i).length() < 8)) {
System.out.println("");
}
}
System.out.print("\n");
}
System.out.println("-------------------");
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static void main(String[] args) {
JDBCDemo01.getAll();
JDBCDemo01.insert(new Course(5, "linux", 5, 4));
JDBCDemo01.getAll();
JDBCDemo01.update(new Course(2, "unix", 6, 4));
JDBCDemo01.getAll();
JDBCDemo01.delete(1);
JDBCDemo01.getAll();
}
}
六、ORM
1. Mybatis
MyBatis 是一款持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
1.1 从XML创建SqlSessionFactory实例
每个mybatis应用程序主要是使用SqlSessionFactory实例的,一个SqlSessionFactory实例可以通过SqlSessionFactoryBuilder获得,SqlSessionFactoryBuilder可以从一个XML配置文件或一个预定义的配置类的实例获得。
可使用类资源路径来加载配置文件,也可使用包括文本文件路径或者以file:// 开头URL 的方式。MyBatis 包括一个叫做Resources 的工具类,其中包含了一系列方法,使之能简单地从classpath 或其它地方加载配置文件。
String resource = "org/mybatis/example/Configuration.xml";
Reader reader = Resources.getResourceAsReader(resource);
sqlMapper = new SqlSessionFactoryBuilder().build(reader);
可通过java 代码而不是通过XML 创建配置选项,或者创建自己的配置生成器。MyBatis 提供了一个完整的配置类,它提供了与XML 文件相同的配置选项。
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment =new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory =new SqlSessionFactoryBuilder().build(configuration);
这种方式下的配置添加一个映射类。映射类是包含SQL 映射注解的Java 类,从而避免了使用XML。但是,由于注解的一些局限性以及MyBatis 映射的复杂性,XML 仍然是一些高级的映射功能(如嵌套连接映射,Nested Join Mapping)所必须的方式。基于这个原因,如果存在XML 文件,MyBatis 自动寻找并加载这个XML 文件。在这种情况下BlogMapper.xml 将会被类路径下名称为BlogMapper.class 的类加载。
XML 配置文件包含MyBatis 框架的核心设置,包括获取数据库连接的DataSource 实例和包括决定事务作用域范围和控制的事务管理等。
XML 配置文件的头部,会使用DTD 验证文档来验证该XML 配置文件。body 部分的environment元素,包含了事务管理和连接池配置。mappers 元素指定了映射配置文件----包含SQL 语句和映射定义的XML文件。
1.2 从SqlSessionFactory获取SqlSession
(1)SqlSession 包含了所有执行数据库SQL 语句的方法,能够直接地通过SqlSession 实例执行映射SQL 语句。
SqlSession session = sqlMapper.openSession();
try {
Blog blog = (Blog) session.selectOne(
"org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
session.close();
}
(2)可以对给定的映射语句,使用一个正确描述参数与返回值的接口(如BlogMapper.class),就能更清晰地执行类型安全的代码,从而避免错误和异常。
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
} finally {
session.close();
}
1.3 映射SQL语句
(1)所有MyBatis 提供的功能特性都可以通过基于XML 映射配置文件配置来实现。
<?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="org.mybatis.example.BlogMapper"> //namespace:命名空间
<select id="selectBlog" parameterType="int" resultType="Blog"> //id:映射语句的名称
select * from Blog where id = #{id}
</select>
</mapper>
可替换为:
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
(2)第一种方法
通过指定完整类名“org.mybatis.example.BlogMapper”来访问:直接映射到一个具在相同命名空间的映射类,这个映射类有一个方法的名称、参数及返回类型都与select映射语句相匹配。
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
(2)第二种方法
第一它不依赖于字符串,所以更安全;第二,如果IDE 有自动完成功能,可以利用这功能很快导航到映射SQL 语句;第三,不需要关注返回类型,不需要进行强制转换,因为使用BlogMapper 接口已经限定了返回类型,它会安全地返回。
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
1.4 命名空间
(1)命名空间是必须的,并且不仅仅是简单地使用完整类名来隔离区分语句。命名空间能够进行接口绑定,即使现在不会使用到它。一旦使用了命名空间并且放入适当的java 包命名空间中将会使代码清晰,并提高MyBatis 的长期可用性。
(2)名称解析: 为了减少大量地输入, MyBatis 对所有的配置元素,包括statements、resultmaps、caches 等使用下面的名称解析规则:
完整类名:(例如: “com.mypackage.MyMapper.selectAllThings”)可用来直接查找并使用。
短名称: (例如: “selectAllThings”)可用来引用明确的实体对象。但是,如果出现有两个或更多(例如“com.foo.selectAllThings 和com.bar.selectAllThings”)实体对象,就必须使用完整类名。
1.5 作用域和声明周期
(1)SqlSessionFactoryBuilder
这个类可以在任何时候被实例化、使用和销毁。一旦创造了SqlSessionFactory 就不需要再保留它了。所以SqlSessionFactoryBuilder 实例的最好的作用域是方法体内(即一个本地方法变量)。可以重用SqlSessionFactoryBuilder 创建多个SqlSessionFactory 实例,但最好不要把时间、资源放在解析XML 文件上,而是要从中解放出来做最重要事情。
(2)SqlSessionFactory
一旦创建,SqlSessionFactory 将会存在于应用程序整个运行生命周期中。很少或根本没有理由去销毁它或重新创建它。最佳实践是不要在一个应用中多次创建SqlSessionFactory。SqlSessionFactory 最好的作用域范围是一个应用的生命周期范围。这可以由多种方式来实现,最简单的方式是使用Singleton 模式或静态Singleton 模式。也可使用像Google Guice 或Spring 的依赖注入方式。这些框架允许创造一个管理器,用于管理SqlSessionFactory 的生命周期。
(3)SqlSession
每个线程都有一个SqlSession 实例,SqlSession 实例是不被共享的,并且不是线程安全的。因此最好的作用域是request 或者method。决不要用一个静态字段或者一个类的实例字段来保存SqlSession 实例引用。也不要用任何一个管理作用域,如Servlet 框架中的HttpSession,来保存SqlSession 的引用。如果正在用一个WEB 框架,可以把SqlSession 的作用域看作类似于HTTP 的请求范围。也就是说,在收到一个HTTP 请求,可以打开一个SqlSession,当把
response 返回时,就可以把SqlSession 关闭。要确保会话在一个finally 块中被关闭。
SqlSession session = sqlSessionFactory.openSession();
try {
......
} finally {
session.close();
}
(4)Mapper实例
Mappers 是创建来绑定映射语句的接口,该Mapper 实例是从SqlSession 得到的。因此,所有mapper 实例的作用域跟创建它的SqlSession 一样。但是,mapper 实例最好的作用域是method,也就是它们应该在方法内被调用,使用完即被销毁。并且mapper 实例不用显式地被关闭。虽然把mapper 实例保持在一个request 范围(与SqlSession 相似)不会产生太大的问题,在这个层次上管理太多资源可能会失控。保持简单,就是让Mappers 保持在一个方法内。
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
......
} finally {
session.close();
}
1.6 Mapper XML配置
(1)MyBatis 的XML 配置文件包含了设置和影响MyBatis 行为的属性。
(2)XML 配置文件的层次结构如下:configuration、properties、settings、 typeAliases、typeHandlers、objectFactory、plugins、environments、environment、transactionManager、dataSource、mappers
①properties元素:它们都是外部化,可替代的属性。
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
在整个配置文件中,这些属性能够被可动态替换(即使用占位符)的属性值引用:username 和password 将会被替换为配置在properties 元素中的相应值。driver 和url 属性则会被config.properties 文件中的相应值替换。
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
这些属性也可以传递给sqlSessionFactoryBuilder.build()方法。
SqlSessionFactory factory =sqlSessionFactoryBuilder.build(reader, props);
......
SqlSessionFactory factory =sqlSessionFactoryBuilder.build(reader, environment, props);
如果一个属性存在于多个地方,MyBatis 将使用下面的顺序加载: 首先读入properties 元素主体中指定的属性。然后会加载类路径或者properties 元素中指定的url 的资源文件属性。它会覆盖前面已 经读入的重复属性。通过方法参数来传递的属性将最后读取(即通过sqlSessionFactoryBuilder.build),同样也会覆盖从properties 元素指定的和resource/url 指定的重复属性。因此最优先的属性是通过方法参数来传递的属性,然后是通过resource/url 配置的属性,最 后是在MyBatis 的Mapper 配置文件中,properties 元素主体中指定的属性。
②settings元素:用于设置和改变MyBatis 运行中的行为。
cacheEnabled :全局性地启用或禁用所有在mapper 配置文件中配置的缓存。默认值为true 。
lazyLoadingEnabled: 全局性地启用或禁用延迟加载。当禁用时,所有关联的配置都会立即加载。默认值为true 。
aggressiveLazyLoading: 当启用后,一个有延迟加载属性的对象的任何一个延迟属性被加载时,该对象的所有的属性都会被加载。否则,所有属性都是按需加载。默认值为true 。
multipleResultSetsEnabled :允许或禁止从单一的语句返回多个结果集(需要驱动程序兼容)。默认值为true 。
useColumnLabel: 使用列的标签而不是列的名称。在这方面,不同的驱动程序可能有不同的实现。默认值为true 。
useGeneratedKeys :允许JDBC 自动生成主键。需要驱动程序兼容。如果设置为true 则会强行自动生成主键,然而有些则不会自动生成主键(驱动程序不兼容),但依旧会工
作(如Derby)。默认值为true 。
autoMappingBehavior :指定MyBatis 是否以及如何自动将列映射到字段/属性。
defaultExecutorType: 配置默认的执行器(executor)。
defaultStatementTimeout :设置查询数据库超时时间。任何正整数。
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="enhancementEnabled" value="false"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25000"/>
</settings>
③typeAliases元素:与XML 配置文件相关联,减少输入多余的完整类名,用于定义一个 JavaBean 类的别名。
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
④typeHandlers元素:每当MyBatis 设置参数到PreparedStatement 或者从ResultSet 结果集中取得值时,就会使用TypeHandler 来处理数据库类型与java 类型之间转换。
1.7 mybatis接口注解
使用合理描述参数和SQL语句返回值的接口,代码更安全,没有容易发生的字符串文字和转换的错误。
@Select("select * from user where id= #{id}")
1.8 mybatis动态SQL语句
mybatis 的动态sql语句是基于OGNL表达式的。可以方便的在 sql 语句中实现某些逻辑。
(1)if 语句 (简单的条件判断)
(2)choose (when、otherwize) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似
(3)trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
用来去处多余关键字的标签,它可以用来实现 where 和 set 的效果。prefixOverrides 属性会忽略通过管道分隔的文本序列。它带来的结果就是所有在 prefixOverrides 属性中指定的内容将被移除,并且插入 prefix 属性中指定的内容。
(4)where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)
where 元素知道只有在一个以上的if条件有值的情况下才去插入“WHERE”子句。where 主要是用来简化 sql 语句中 where 条件判断,自动地处理 AND/OR 条件。若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。
(5)set (主要用于更新时)
set 元素可以被用于动态包含需要更新的列,而舍去其他的。set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号。
(6)foreach (在实现 mybatis in 语句查询时特别有用)
对一个集合进行遍历,通常是在构建 IN 条件语句的时候。它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量,允许指定开闭匹配的字符串以及在迭代中间放置分隔符。
(7)bind
bind 元素可以从 OGNL 表达式中创建一个变量并将其绑定到上下文。
1.9 执行流程
- Mybatis配置文件,包括Mybatis全局配置文件和Mybatis映射文件,其中全局配置文件1.1配置了数据源、事务等信息;映射文件配置了SQL执行相关的信息。
- Mybatis通过读取配置文件信息(全局配置文件和映射文件),构造出SqlSessionFactory,即会话工厂。
- 通过SqlSessionFactory,可以创建SqlSession即会话。Mybatis是通过SqlSession来操作数据库的。
- SqlSession本身不能直接操作数据库,它是通过底层的Executor执行器接口来操作数据库的。Executor接口有两个实现类,一个是普通执行器,一个是缓存执行器(默认)。
- Executor执行器要处理的SQL信息是封装到一个底层对象MappedStatement中。该对象包括:SQL语句、输入参数映射信息、输出结果集映射信息。其中输入参数和输出结果的映射类型包括java的简单类型、HashMap集合对象、POJO对象类型。
2. Mybatis、Hibernate、JDBC三者之间的比较
(1)Hibernate
Hibernate的查询会将表中的所有字段查询出来,这一点会有性能消耗。Hibernate也可以自己写SQL来指定需要查询的字段,但这样就破坏了Hibernate开发的简洁性。
Hibernate与具体数据库的关联只需在XML文件中配置即可,所有的HQL语句与具体使用的数据库无关,移植性很好。
Hibernate在JDBC的基础上进行了封装使其更加方便程序员对持久层的操作。
(2)Mybatis
Mybatis动态SQL,主要用于解决查询条件不确定的情况:在程序运行期间,根据提交的查询条件进行查询。MyBatis 的强大特性之一便是它的动态SQL。根据不同条件拼接SQL 语句,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
Mybatis的SQL是手动编写的,所以可以按需求指定查询的字段。Mybatis的SQL会更灵活、可控性更好、更优化。
MyBatis项目中所有的SQL语句都是依赖所用的数据库的,所以不同数据库类型的支持不好。
(3)JDBC
JDBC是较底层的持久层操作方式,JDBC就是简单的建立数据库连接,然后创建statement,将sql语句传给statement去执行,如果是有返回结果的查询语句,会将查询结果放到ResultSet对象中,通过对ResultSet对象的遍历操作来获取数据;
从使用上看,如果进行底层编程,而且对性能要求极高的话,应该采用JDBC的方式;如果要对数据库进行完整性控制的话建议使用Hibernate;如果要灵活使用sql语句的话建议采用MyBatis框架。
七、Spring
1.Spring @Transactional实现原理
Spring默认使用AOP代理,在代码运行时生成一个代理对象,根据 @Transactional的属性配置信息,这个代理对象决定该声明 @Transactional的目标方法是否由拦截器TransactionInterceptor来使用拦截,在TransactionInterceptor拦截时,会在目标方法开始执行之前创建并加入事务,并执行目标方法的逻辑,然后根据执行情况是否出现异常,利用抽象事务管理器AbstractPlatformTransactionManager操作数据源DataSource提交或回滚事务。
2. Spring事务的实现方式
(1)编程式事务
使用TransactionManager对象构造TransactionTemplate,设置事务隔离级别、事务传播行为等, 将数据库操作全部包到execute方法中,失败回滚。
(2)声明式事务
应用程序启动时,自动扫描所有Spring Bean,将带有@Transactional注解的方法自动代理(AOP)动态代理,放入缓存。调用接口时,自动启用AOP代理,在方法执行前和方法执行后做相应处理。
3. Spring事务处理过程
(1)读取和处理在Spring IoC容器中配置的事务处理属性,并转化为Spring事务处理所需要的内部数据结构。
(2)Spring事务处理模块实现的统一的事务处理过程。这个通用的事务处理过程包括:处理事务配置属性、事务配置属性与线程绑定等。
(3)底层事务处理实现。Spring中底层事务处理的实现交由PlatformTransactionManager的具体实现类来实现,如DataSourceTransactionManager和HibernateTransactionManager等。
4. Spring事务与jdbc关系
Spring 对 JDBC 的支持是通过JdbcTemplate来完成的。JdbcTemplate是Spring框架自带的对JDBC操作的封装,目的是提供统一的模板方法使对数据库的操作更加方便,大大简化了数据库的操作。JdbcTempate能直接数据对象映射成实体类,不再需要获取ResultSet去获取值/赋值等操作,提高开发效率。
Spring IoC容器将管理数据库连接的数据源当作普通Java Bean一样管理,然后将数据源注入封装类JdbcTemplate中,JdbcTemplate的dataSource属性就是注入配置的数据源。JdbcTemplate必须与Spring框架结合在一起使用、不支持数据库跨平台、默认没有缓存。
注:
JDBC与JdbcTemplate的区别
(1)JDBC需要每次进行数据库连接, 然后处理SQL语句、传值、关闭数据库。甚至有时还可能会出现数据库忘记关闭导致连接被占用。如果需求改变,就导致经常会改动数据库内容。
(2)通过JdbcTemplate我们只需更改需要更改的那一部分内容就可以了,不需要进行全局修改,Spring将替我们完成所有的JDBC底层细节处理工作。