MySQL42
数据库存储引擎:定义如何存储数据、如何为存储的数据建立索引、如何更新、查询数据等技术的具体实现。
MySQL 提供了很多种存储引擎,可以直接查询
MySQL 默认的存储引擎是 InnoDB,支持事务,是其它引擎所不能比拟的
InnoDB 支持事务,支持主键自增,支持外键
XA:表示是否支持分布式事务
SQL:是一种编程语言,专门用来操作数据库的,实现对数据库的增删改查都是通过执行对应 SQL 语句来完成的。
MySQL常用知识点
- 算术运算符:在查询的过程中对结果的值进行加减乘除、比较、逻辑运算等功能
- 特殊运算符
- 判断数据是否为空
- 判断值是否在某个区间之内
- 判断值是否在某个特定值的集合内
- 模糊查询
- % 可以替换为任意字符
- _ 表示一位长度
- 日期函数
- 获取当前日期
select curdate();
- 获取当前时间
select curtime();
- 获取当前日期+时间
select now();
- 获取两个日期相隔的天数
select datediff('2022-6-23','2019-05-01');
- 查询某个日期之后多少天的日期是多少
select adddate('2022-08-22',100);
- 查询某个日期之前多少天的日期是多少
select subdate('2022-08-22',100);
- 聚合函数
- 统计记录数
select count(id) from student;
- 计算字段值之和
select sum(age) from student;
- 求字段的平均值
select avg(age) from student;
- 求字段的最大值
select max(age) from student;
- 求字段的最小值
select min(age) from student;
MySQL 的数据类型
整数类型:tinyint、smallint、mediumint、int、bigint
浮点型:float、double、decimal
日期类型:datetime、date、timestamp、time、year
字符串类型:char、varchar、text
二进制类型:bit、binary、varbinary、tinyblob、blob、mediumblob、longblob
数据库是通过主键去唯一标识某条数据的
外键是建立两张表之间的联系的,进行数据之间的约束
数据表之间的关系
- 一对一:A 表中的一条记录唯一对应 B 表中的一条记录,B 表中的一条记录唯一对应 A 表中的一条记录。比如人和身份证
- 一对多:A 表中的一条记录对应 B 表中的一条记录,B 表中的一条记录对应 A 表中的多条记录。比如学生和班级
- 多对多:A 表中的一条记录对应 B 表中的多条记录,B 表中的一条记录对应 A 表中的多条记录。比如学生和选课
MySQL43
多表关联查询
- 嵌套查询
- 连接查询
- 内连接(默认是笛卡尔积的结果)
- 外连接
- 左连接:左表所有数据和右表满足条件的数据组成的结果
- 右连接:右表所有数据和左表满足条件的数据组成的结果
多对多查询,需要通过中间表将两张业务表进行关联
索引是一种特殊的数据库结构,可以用来快速查询数据表中的特定记录,索引是提升数据库性能的重要方式,主要是提高查询的速度。
索引:
- 普通索引:不需要任何限制条件的索引,可以在任意数据类型上创建
- 唯一索引:索引的值必须唯一,比如主键索引
- 全文索引:只能创建在 text、char、varchar 数据类型上,针对文本类型数据的索引
- 单列索引:只对应一个字段的索引
- 多列索引:在一张表的多个字段上添加索引,由多个字段共同构成索引结构
- 空间索引:只能建立在空间数据类型(经纬度),InnoDB 不支持空间索引
索引的设计原则:
- 出现在 where 语句中的列可以加索引,select 后面的列不需要加索引
- 索引的值,尽量唯一,效率更高
- 不要添加过多的索引,维护成本很高
1、添加索引
create index in_name on student(name);
2、删除索引
drop index in_name on student;
事务的特性:
- 原子性:多条 SQL 是一个整体,不可再分割
- 一致性:SQL 执行前后,数据库的数据值保持一致
- 隔离性:一个事务的执行不能被其他事务所干扰
- 持久性:一个事务一旦提交,数据库中数据的改变是永久性的
视图:数据库中一张虚拟的表,允许不同用户或应用程序以不同的方式查看同一张表中的数据
创建视图
create view view_common as select id,name from user;
create view view_all as select * from user;
使用视图
select * from view_common;
select * from view_all;
删除视图
drop view view_common;
drop view view_all;
触发器:提前定义好一系列的操作,可以在对指定表进行插入、更新、删除的同时自动执行这些操作
use db01;
create table tab1(
tab1_id varchar(11)
);
create table tab2(
tab2_id varchar(11)
);
# 创建触发器
create trigger t_afterinsert_on_tab1
after insert on tab1 # 给tab1表添加后去执行触发器
for each row # 针对每一行都有效
begin
insert into tab2(tab2_id) values(new.tab1_id); # 这里是要执行的操作
end;
# 创建触发器
create trigger t_afterdelete_on_tab1
after delete on tab1
for each row
begin
delete from tab2 where tab2_id = old.tab1_id;
end;
# 删除触发器
drop trigger t_afterdelete_on_tab1;
drop trigger t_afterinsert_on_tab1;
存储过程:存储过程是提前定义好了一组 SQL 语句的集合,存储在数据库中,用户可以直接调用这些存储过程,类似于 Java 的方法调用。
use db01;
# 创建一个存储过程
create procedure add_name(in target int) # in表示传入的参数,target是参数名,int是参数类型
begin
declare name varchar(20); # 声明一个name
if target = 1 then
set name = 'MySQL';
else
set name = 'Java';
end if;
insert into user(name) values(name);
end;
# 调用存储过程
call add_name(1);
# 创建一个存储过程
create procedure count_of_user(out count_num int) # out表示返回值,count_num表示返回值名字
begin
select count(id) into count_num from user; # 把查出的结果赋给返回值count_num
end;
# 调用存储过程
call count_of_user(@count_num);
select @count_num;
# 删除存储过程
drop procedure add_name;
drop procedure count_of_user;
MySQL44
JDBC:用 Java 语言来操作数据库的一种技术,具有通用性,无论底层数据库是什么,都可以使用 JDBC 来进行操作,而不需要修改接口,JDBC本身就是接口
Java —> JDBC —> 数据库
JDBC基本的使用:
import java.sql.*;
public class Test {
public static void main(String[] args) throws Exception {
String url = "jdbc:mysql://localhost:3306/db01?useSSL=true";
String user = "root";
String password = "123456";
Connection connection = DriverManager.getConnection(url, user, password);// 获取连接对象
String sql = "select * from student";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);// 将执行结果返回给结果集
while (resultSet.next()) {
ResultSetMetaData metaData = resultSet.getMetaData();// 获取结果集的字段属性
int columnCount = metaData.getColumnCount();// 获取字段属性的个数
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);// 获取第i个字段属性的名字
System.out.println("字段["+columnName+"]的值是:"+resultSet.getObject(columnName));
}
System.out.println("*****************************************");
}
}
}
MySQL45
数据库连接池:预先创建好一定数量的数据库连接对象,放到缓冲池中,当开发者需要连接的时候直接从缓冲池中取出使用,用完再还回到缓冲池中,这样就做到了对于资源的重复利用,提高效率。
和线程池、字符串常量池、包装类常量池都是一样的原理,只是缓冲池中存放的资源不同。
下面是使用c3p0线程池的例子
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;
public class JDBCUtil {
private static ComboPooledDataSource dataSource;
static {
try {
//创建连接池对象
dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");// 不同版本的驱动这里也会有所不同
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/db01?useSSL=true");
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setInitialPoolSize(20);
dataSource.setMaxPoolSize(40);
dataSource.setMinPoolSize(2);
dataSource.setAcquireIncrement(5);
} catch (PropertyVetoException e) {
e.printStackTrace();
}
}
// 通过线程池获取连接对象
public static Connection getConnection(){
try {
Connection connection = dataSource.getConnection();
return connection;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return null;
}
}
import java.sql.*;
public class Test {
public static void main(String[] args) throws Exception {
Connection connection = JDBCUtil.getConnection();
String sql = "select * from student";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while (resultSet.next()) {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnName(i);
System.out.println("字段["+columnName+"]的值是:"+resultSet.getObject(columnName));
}
System.out.println("*****************************************");
}
}
}
上面的示例中:数据源的参数直接写到 Java 程序中,如果数据源参数有变动,则需要重新修改Java代码,此时Java文件就要重新编译,导致效率低下。所以我们一般把数据源的参数内容写到配置文件中。
DBUtils:是对 JDBC 的一套封装,可以简化 CRUD 的操作,是一个轻量级小工具。能够帮助我们把数据库得到的结果集映射成对应的实体类。
我们可以尝试手写一个DBUtils,底层实现主要使用到了反射的机制。我们可以体会一下框架背后实现的原理
关键代码如下:
package com.util;
public class Test2 {
public static void main(String[] args) throws Exception{
MyQueryRunner<User> myQueryRunner = new MyQueryRunner<>();
String sql = "select * from user where id=1";
User user = myQueryRunner.query(
JDBCUtil.getConnection(),
sql,
new MyBeanHandler(User.class)
);
System.out.println(user);
}
}
package com.util;
import org.apache.commons.dbutils.ResultSetHandler;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class MyQueryRunner<T> {
public T query(Connection connection, String sql, ResultSetHandler<T> rsh) throws SQLException {
//非空校验
if (connection == null) throw new SQLException("Null connection");
if (sql == null) throw new SQLException("Null SQL statement");
if (rsh == null) throw new SQLException("Null ResultSetHandler");
//创建statement
Statement stmt = connection.createStatement();
//执行sql
ResultSet rs = stmt.executeQuery(sql);
//将结果集转为目标对象
/*
主要思路就三步:
1、获取实体类的结构
2、获取结果集的结构
3、完成映射即可
*/
Object result = rsh.handle(rs);
//返回目标对象
return (T)result;
}
}
package com.util;
import org.apache.commons.dbutils.ResultSetHandler;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.*;
public class MyBeanHandler implements ResultSetHandler {
private Class type;
public MyBeanHandler(Class type) {
this.type = type;
}
@Override
public Object handle(ResultSet resultSet) throws SQLException {
//resultSet转为type对象
//获取实体类的结构
Map<String, Class> properties = getProperties();
//获取结果集的结构
List<String> colums = getColums(resultSet);
//完成映射并且返回即可!
return toBean(properties, colums,resultSet);
}
public Object toBean(Map<String, Class> properties,List<String> colums,ResultSet resultSet){
Object result = null;
try {
//创建对象
Constructor constructor = type.getConstructor(null);
result = constructor.newInstance(null);
//属性赋值
if(resultSet.next()){
for (String colum : colums) {
Class aClass = properties.get(colum);
String name = aClass.getName();
Object value = null;
switch (name){
case "java.lang.Integer":
value = resultSet.getInt(colum);
break;
case "java.lang.String":
value = resultSet.getString(colum);
break;
}
colum = "set"+colum.substring(0, 1).toUpperCase()+colum.substring(1);
Method declaredMethod = type.getDeclaredMethod(colum, aClass);
declaredMethod.invoke(result, value);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
public List<String> getColums(ResultSet resultSet){
List<String> list = new ArrayList<>();
try {
ResultSetMetaData metaData = resultSet.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
list.add(metaData.getColumnName(i));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return list;
}
public Map<String,Class> getProperties(){
Field[] declaredFields = type.getDeclaredFields();
Map<String,Class> map = new HashMap<>();
for (Field declaredField : declaredFields) {
String fieldName = declaredField.getName();
Class<?> fieldType = declaredField.getType();
map.put(fieldName, fieldType);
}
return map;
}
}
MySQL46
Maven 提供了一种服务,可以通过配置文件的方式从远程仓库下载相应的 jar 到本地仓库,自动从本地仓库将 jar 导入工程中。
配置文件中添加我们需要的 jar,Maven 会自动进行下载导入
MyBatis中一对多的关键代码如下:
package com.southwind.entity;
import lombok.Data;
@Data
public class Student {
private Integer id;
private String name;
private Class clazz;
}
package com.southwind.entity;
import lombok.Data;
import java.util.List;
@Data
public class Class {
private Integer id;
private String name;
private List<Student> students;
}
下面是查询学生
<?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="com.southwind.mapper.StudentMapper">
<resultMap id="studentMap" type="com.southwind.entity.Student">
<id property="id" column="sid"></id><!--结果集的字段为主键的用id标签-->
<result property="name" column="sname"></result>
<!--相当于把Class类中的id和name属性封装成Class类对象传给Student类中的clazz属性-->
<association property="clazz" javaType="com.southwind.entity.Class">
<id property="id" column="cid"></id><!--结果集的字段为主键的用id标签-->
<result property="name" column="cname"></result>
</association>
</resultMap>
<!--结果集映射成上面的studentMap-->
<select id="findById" resultMap="studentMap">
select s.id sid,s.name sname,c.id cid,c.name cname from student s,class c where s.cid = c.id and s.id = #{id}
</select>
</mapper>
下面是查询班级
<?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="com.southwind.mapper.ClassMapper">
<resultMap id="classMap" type="com.southwind.entity.Class">
<id property="id" column="cid"></id>
<result property="name" column="cname"></result>
<collection property="students" ofType="com.southwind.entity.Student">
<id property="id" column="sid"></id>
<result property="name" column="sname"></result>
</collection>
</resultMap>
<select id="findById" resultMap="classMap" parameterType="java.lang.Integer"><!--parameterType这是参数类型,可以省略不写-->
select s.id sid,s.name sname,c.id cid,c.name cname from student s,class c where s.cid = c.id and c.id = #{id}
</select>
</mapper>
关键思路就是:如何把结果集映射为对应的实体类
MySQL47
MyBatis的延迟加载:多表关联查询的时候,一种优化机制。(本质上就是把级联查询转换成多个单表的查询。根据需要决定执行的sql语句次数)
MyBatis的延迟加载关键实现代码如下:(相当于嵌套查询)
<?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="com.southwind.mapper.StudentMapper">
<resultMap id="studetnMap" type="com.southwind.entity.Student">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<association property="clazz" javaType="com.southwind.entity.Class" select="com.southwind.mapper.ClassMapper.findById" column="cid"></association>
</resultMap>
<select id="findById" resultMap="studetnMap">
select * from student where id = #{id}
</select>
</mapper>
<?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="com.southwind.mapper.ClassMapper">
<select id="findById" resultType="com.southwind.entity.Class">
select * from class where id = #{id}
</select>
</mapper>
在MyBatis核心配置文件中添加如下配置开启MyBatis的延迟加载
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" /><!--开启打印日志-->
<setting name="lazyLoadingEnabled" value="true"/><!--开启MyBatis的延迟加载-->
</settings>
一级缓存:自带,必须使用同一个 SqlSession
二级缓存:需要手动开启的
1、在MyBatis核心配置文件中开启二级缓存
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="cacheEnabled" value="true"/><!--开启二级缓存-->
</settings>
2、在对应的 Mapper.xml 中配置二级缓存
<cache></cache>
3、实体类需要实现序列化接口
package com.southwind.entity;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class Class implements Serializable {
private Integer id;
private String name;
private List<Student> students;
}
4、必须关闭 sqlsession,重新赋值
import com.southwind.entity.Class;
import com.southwind.entity.Course;
import com.southwind.entity.Student;
import com.southwind.mapper.AccountMapper;
import com.southwind.mapper.ClassMapper;
import com.southwind.mapper.CourseMapper;
import com.southwind.mapper.StudentMapper;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
public class Test {
public static void main(String[] args) {
//加载MyBatis环境
InputStream inputStream = Test.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
SqlSession sqlSession = factory.openSession();
//调用接口
ClassMapper classMapper = sqlSession.getMapper(ClassMapper.class);
Class aClass = classMapper.findById(1);
System.out.println(aClass);
sqlSession.close();
sqlSession = factory.openSession();
classMapper = sqlSession.getMapper(ClassMapper.class);
Class aClass1 = classMapper.findById(1);
System.out.println(aClass1);
}
}