Mysql
索引:
索引分类:
1.普通索引:是最基本的索引,它没有任何限制;
2.主键索引:是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引
3.唯一索引:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一
组合索引:指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合
4.全局索引:有char、varchar,text 列上可以创建全文索引,更像是一个搜索引擎
索引的注意事项:
索引不会包含有null值的列:只要列中包含有null值都将不会被包含在索引中,复合索引中只要有一列含有null值,那么这一列对于此复合索引就是无效的。所以我们在数据库设计时不要让字段的默认值为null。
不要使用like 如果使用: “%abc%” 不会使用索引而like “abc%”可以使用索引
不要做运算
不使用not in和<>操作
事务传播行为
@Transactional(propagation=Propagation.REQUIRED)
public void test() {
//todo
}
required :以事务运行,没有事务就新建一个(Spring默认)
support:以事务运行,没有事务就以非事务运行
mandatory:以事务运行,没有事务就抛出异常
requireds new : 新建一个事务,已有事务挂起
not supports:以非事务运行,有事务就挂起
never :以非事务运行,有事务就抛出异常
nsted:以事务运行,有事务就嵌套,没有则与required一样
只读操作:
@Transactional(readOnly=true)
事务隔离机制
read uncommit 读未提交 脏读
read commit 读已提交 解决脏读,未解决可重复读
read repeat 可重复读 未解决幻读 next-key lock机制 为读取的数据增加了标记
serilisable 串行化
事务 transaction
ACID
原子性 不可再拆分,要么都做要么都不做
一致性 事务内的数据库操作应当从一个一致状态变换到另一个一致状态
隔离性 事务与事务之间相互独立不干扰
持久化 一旦提交就会保存在硬盘上
保证ACID的解决方式
持久化解决:redo log(预写式日志) 事务在进行前通过Buffer Pool,再定期刷新到磁盘(脏刷)
原子性、一致性解决:undo log
隔离性:mvcc多版本的并发控制协议 锁机制(表锁与行锁) 为读取的数据增加了标记
客户端连接池 处理客户端连接、授权认证等
服务层 它负责处理连接 优化缓存、内置函数实现、存储过程
存储引擎 数据引擎 innoDB 服务层不支持事务,主要由数据层的引擎负责
Mybatis框架
功能架构
1.API接口层
提供给外部使用的接口API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
2.数据处理层
负责具体的SQL查找、SQL解析、SQL执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
3.基础支撑层
负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑
核心方法:
SqlSessionFactoryBuilder
- 就是SqlSessionFactory的建造器,典型的工厂模式。
- 创建后就不再需要了,因为就是为了创造SqlSessionFactory的工具类。
- 作用域:局部变量
SqlSessionFactory
- 类似DBCP的连接池。创建就应该一直存在,没有任何理由丢弃它或重新创建另一个实例。因此应该使用单例模式或静态单例模式来创建。
- 作用域:应用作用域(Application)程序运行就创建,程序关闭才释放。
- 生命周期:等同于MyBatis的应用周期。
SqlSession
- 类似JDBC的一个数据库连接(
Connection
对象)。因此每次使用完就应该要关闭,这样才能回收到SqlSessionFactory
中继续利用,而且SqlSession
不是线程安全的,不能被共享。 - 作用:在一个事务里面执行多条SQL,然后通过它的
commit
、rollback
等方法,提交或者回滚事务。 - 生命周期:它应该存活在一个业务请求中
- 作用域:请求或方法。
- 处理完整个请求后,应该关闭这条连接,让它归还给
SqlSessionFactory
,否则数据库资源就很快被消耗精光,系统应付瘫痪,所以用try…catch…fanally语句来保证其正确关闭 - 使用
MyBatis-Spring
之后, 你不再需要直接使用 SqlSessionFactory 了,因为你的bean
可以通过一个线程安全的 SqlSession 来注入,基于Spring
的事务配置来自动提交、回滚、关闭session
。
Mapper
- Mapper是一个接口,它由SqlSession所创建,所以它的最大生命周期至多和SqlSession保持一致
- 尽管很好用,但是由于SqlSession关闭,它的数据库连接资源也会消失,所以它的生命周期应该小于等于SqlSession的生命周期。
- Mapper代表是一个请求中的业务处理,所以它应该在一个请求中,一旦处理完了相关的业务,就应该废弃它。
SqlSessionTemplate
-
SqlSessionTemplate
是MyBatis-Spring
的核心。 这个类负责管理MyBatis
的SqlSession
, 调用MyBatis
的 SQL 方法, 翻译异常。SqlSessionTemplate
是线程安全的, 可以被多个 DAO 所共享使用。 -
当调用 SQL 方法时, 包含从映射器 getMapper()方法返回的方法,
SqlSessionTemplate
将会保证使用的SqlSession
是和当前Spring
的事务相关的。此外,它管理session
的生命周期,包含必要的关闭,提交或回滚操作。 -
SqlSessionTemplate
实现了SqlSession
接口,在代码中无需对MyBatis
的SqlSession
进行替换。SqlSessionTemplate
通常是被用来替代默认的 MyBatis 实现的DefaultSqlSession
, 因为模板可以参与到Spring
的事务中并且被多个注入的映射器类所使 用时也是线程安全的。相同应用程序中两个类之间的转换可能会引起数据一致性的问题。 -
SqlSessionTemplate
对象可以使用SqlSessionFactory
作为构造方法的参数来创建。SqlSessionTemplate
只有构造方法注入
,没有setter
方法
Mybatis插件-通用Mapper
作用:为了解决单表增删改查,基于Mybatis的插件。开发人员不需要编写SQL,不需要在DAO中增加方法,只要写好实体类,就能支持相应的增删改查方法。
<!-- mybatis的启动器 省略了版本号-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- 通用mapper启动器 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
使用时,使用tk.mybatis.spring.mapper.MapperScannerConfigure
替换原来Mybatis的org.mybatis.spring.mapper.MapperScannerConfigurer
实体类的写法
原则:实体类的字段数量 >= 数据库表中需要操作的字段数量。默认情况下,实体类中的所有字段都会作为表中的字段来操作,如果有额外的字段,必须加上@Transient
注解。
@Table(name = "test_table")
public class TestTableVO implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(generator = "JDBC")
private Long id;
@Transient
private String userId;
private String name;
// get/set...
}
说明:
-
表名默认使用类名,驼峰转下划线(只对大写字母进行处理),如
UserInfo
默认对应的表名为user_info
。 -
表名可以使用
@Table(name = "tableName")
进行指定,对不符合第一条默认规则的可以通过这种方式指定表名. -
字段默认和
@Column
一样,都会作为表字段,表字段默认为Java对象的Field名字驼峰转下划线形式. -
可以使用
@Column(name = "fieldName")
指定不符合第3条规则的字段名 -
使用
@Transient
注解可以忽略字段,添加该注解的字段不会作为表字段使用. -
建议一定是有一个
@Id
注解作为主键的字段,可以有多个@Id
注解的字段作为联合主键. -
如果是MySQL的自增字段,加上
@GeneratedValue(generator = "JDBC")
即可。
Dao写法:
在传统的Mybatis写法中,DAO
接口需要与Mapper
文件关联,即需要编写SQL
来实现DAO
接口中的方法。而在通用Mapper中,DAO
只需要继承一个通用接口,即可拥有丰富的方法:
配置文件
mybatis:
type-aliases-package: com.leyou.item.pojo #扫描对应实体类的包
启动类增加扫描
@MapperScan("com.leyou.item.mapper") // mapper接口的包扫描,里面跟包路径
继承通用的Mapper,必须指定泛型
public interface TestTableDao extends Mapper<TestTableVO> {
}
一旦继承了Mapper,继承的Mapper就拥有了Mapper所有的通用方法:
Select
方法:List<T> select(T record);
说明:根据实体中的属性值进行查询,查询条件使用等号
方法:T selectByPrimaryKey(Object key);
说明:根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
方法:List<T> selectAll();
说明:查询全部结果,select(null)方法能达到同样的效果
方法:T selectOne(T record);
说明:根据实体中的属性进行查询,只能有一个返回值,有多个结果是抛出异常,查询条件使用等号
方法:int selectCount(T record);
说明:根据实体中的属性查询总数,查询条件使用等号
Insert
方法:int insert(T record);
说明:保存一个实体,null的属性也会保存,不会使用数据库默认值
方法:int insertSelective(T record);
说明:保存一个实体,null的属性不会保存,会使用数据库默认值
Update
方法:int updateByPrimaryKey(T record);
说明:根据主键更新实体全部字段,null值会被更新
方法:int updateByPrimaryKeySelective(T record);
说明:根据主键更新属性不为null的值
Delete
方法:int delete(T record);
说明:根据实体属性作为条件进行删除,查询条件使用等号
方法:int deleteByPrimaryKey(Object key);
说明:根据主键字段进行删除,方法参数必须包含完整的主键属性
Example方法
方法:List<T> selectByExample(Object example);
说明:根据Example条件进行查询
重点:这个查询支持通过Example
类指定查询列,通过selectProperties
方法指定查询列
方法:int selectCountByExample(Object example);
说明:根据Example条件进行查询总数
方法:int updateByExample(@Param("record") T record, @Param("example") Object example);
说明:根据Example条件更新实体record
包含的全部属性,null值会被更新
方法:int updateByExampleSelective(@Param("record") T record, @Param("example") Object example);
说明:根据Example条件更新实体record
包含的不是null的属性值
方法:int deleteByExample(Object example);
说明:根据Example条件删除数据
代码中使用
在service
中注入dao
,即可使用。
@Autowired
private TestTableDao testTableDao;
自定义查询写法(例)
public interface BrandMapper extends Mapper<Brand> {
/**
* 新增商品分类和品牌中间表数据
* @param cid 商品分类id
* @param bid 品牌id
* @return
*/
@Insert("INSERT INTO tb_category_brand(category_id, brand_id) VALUES (#{cid},#{bid})")
int insertBrandAndCategory(@Param("cid") Long cid, @Param("bid") Long bid);
@Select("SELECT b.* from tb_brand b INNER JOIN tb_category_brand cb on b.id=cb.brand_id where cb.category_id=#{cid}")
List<Brand> selectBrandByCid(Long cid);
}
@Select("SELECT b.* from tb_brand b INNER JOIN tb_category_brand cb on b.id=cb.brand_id where cb.category_id=#{cid}")
List<Brand> selectBrandByCid(Long cid);
使用时与通用mapper同理