零、 复习昨日
一、缓存
二、单例设计模式
多表联查的时候
- 扩展类
- 写接口设计方法
- 写sql语句
- 不能直接映射成实体类
- resultMap
- 一对一 axxxxxxx
- 一对多 collection
使用#
使用$
总结:
#{} 相当于是预处理语句,会将#
换成占位符?
,字符串等数据赋值时自动拼接引号,可以避免SQL注入${} 相当于是处理语句,
直接原样将数据取出
,直接拼接
使用模糊查询再验证一遍
使用#{} 写模糊查询
<select id="findByKeyword" resultType="User">
select * from tb_user where username like '%#{keyword}%'
select>
以上写法会把sql变成这样,#符号会给字符串拼接单引号
select * from tb_user where username like ‘%‘张’%’
改成${}写模糊查询
<select id="findByKeyword" resultType="User">
select * from tb_user where username like '%${keyword}%'
select>
以上写法会把sql变成这样,$符号是直接取值的
select * from tb_user where username like ‘%张%’
函数concat解决#{} 拼接单引号问题
<select id="findByKeyword" resultType="User">
select * from tb_user where username like concat('%',#{keyword},'%')
select>
缓存主要目的是为了
提高查询效率
.缓存其实就是一个内存空间,存储在程序的某个地方,存储数据.mybatis支持缓存的,且有两级缓存
- 一级缓存
- 二级缓存
无缓存:用户在访问相同数据时,需要发起多次对数据库的直接访问,导致产生大量IO、读写硬盘的操作,效率低下 |
---|
有缓存:首次访问时,查询数据库,将数据存储到缓存中;再次访问时,直接访问缓存,减少IO、硬盘读写次数、提高效率 |
---|
MyBatis的
一级缓存是默认的
.无需配置,自动实现.默认的
一级缓存是SqlSession级别
,是指同一个SqlSession发起的多次查询同一条数据,会使用缓存.
ps: Mybatis内部存储缓存使用的是一个HashMap对象,key为 hashCode + sqlId + sql 语句。而value值就是从查询出来映射生成的java对象。
/**
* 测试一级缓存
* -----------------------
* 如何确定使用了缓存而不是查询数据库? 通过sql语句来确定
* 只有发出sql,就说明查询数据库,即没有用缓存
*/
@Test
public void testOneLevelCache() {
UserMapper mapper1 = sqlSession.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
System.out.println("user1 = " + user1);
System.out.println("-----------------------");
// 命中缓存的前提:1) 同一SqlSession,2) 同一条件
UserMapper mapper2 = sqlSession.getMapper(UserMapper.class);
User user2 = mapper2.findUserById(1);
System.out.println("user2 = " + user2);
}
不使用缓存的情况1: 条件不一致
不使用缓存的情况2: sqlSession不一样
@Test
public void testOneLevelCache2() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得会话1
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
System.out.println("user1 = " + user1);
System.out.println("---------------------------------" );
// 获得一个会话2
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.findUserById(1);
System.out.println("user2 = " + user2);
}
虽然是同一条数据,但是还是没有使用缓存!为什么?
一级缓存是SqlSession级别,即缓存数据存储在SqlSession中,这次都不是同一个SqlSession,那就无法使用缓存
在更新(更新,删除,插入)数据后,会清空缓存,下次重新查最新的数据.避免脏读
/**
* 演示: 增删改都会清空缓存
*/
@Test
public void testOneLevelCache3() {
UserMapper mapper1 = sqlSession.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
System.out.println("user1 = " + user1);
System.out.println("---------------------------------" );
// 只要有增删改都会清空缓存
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = new User( );
user.setId(2);
user.setUsername("QF");
user.setPassword("qf888");
int i = mapper.updateUser(user);
System.out.println(i > 0?"OK":"ERR" );
// 增删改要提交
sqlSession.commit();
System.out.println("---------------------------------" );
// 清空缓存后,再查就要从数据库查
UserMapper mapper2 = sqlSession.getMapper(UserMapper.class);
User user2 = mapper2.findUserById(1);
System.out.println("user2 = " + user2);
}
一级缓存也叫本地缓存(LocalCache),Mybatis的一级缓存是会话级别(SqlSession)层面进行缓存的。Mybatis的一级缓存是默认开启的。开发项目中不需要做任何配置,但是如果想关闭一级缓存,可以在mybatis配置文件的settings下面使用localCacheScopde=statement来关闭。
<settings>
<setting name="localCacheScope" value="STATEMENT"/>
settings>
二级缓存是Mapper级别
,比SqlSession级别范围更大.使用时需要手动设置
1)需要在全局配置文件中开启缓存(默认开着的)
<settings> <setting name="cacheEnabled" value="true"/> settings>
2)需要在mapper中设置caceh即可
<cache/>
- 映射语句文件中的所有 select 语句的结果将会被缓存。
- 映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
- 1)需要在全局配置文件中开启缓存(默认开着的)
- 2)需要在mapper中设置caceh即可
- 3)每个会话执行完,要关流close,才会将查询是数据放入缓存
- 4)实体类需要系列化,实现Serializable接口
/**
* 二级缓存
* 演示不同SqlSession,会命中缓存
*/
@Test
public void testTwoLevelCache1() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得会话1
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
System.out.println("user1 = " + user1);
// 关闭会话,才会将这次查询的结果放入缓存!!!
sqlSession1.close();
System.out.println("---------------------------------" );
// 获得一个会话2
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.findUserById(1);
System.out.println("user2 = " + user2);
sqlSession2.close();
System.out.println("---------------------------------" );
// 获得一个会话3
SqlSession sqlSession3 = sqlSessionFactory.openSession();
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
User user3 = mapper3.findUserById(1);
System.out.println("user3 = " + user3);
sqlSession3.close();
}
/**
* 二级缓存
* 演示增删改会清空缓存
*/
@Test
public void testTwoLevelCache2() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获得会话1
SqlSession sqlSession1 = sqlSessionFactory.openSession();
UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
User user1 = mapper1.findUserById(1);
System.out.println("user1 = " + user1);
// 关闭会话,才会将这次查询的结果放入缓存!!!
sqlSession1.close();
System.out.println("---------------------------------" );
// 获得一个会话2
SqlSession sqlSession2 = sqlSessionFactory.openSession();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
User user2 = mapper2.findUserById(1);
System.out.println("user2 = " + user2);
System.out.println("---------------------------------" );
// 只要有增删改都会清空缓存
UserMapper mapper = sqlSession2.getMapper(UserMapper.class);
User user = new User( );
user.setId(2);
user.setUsername("QF");
user.setPassword("qf888");
int i = mapper.updateUser(user);
System.out.println(i > 0?"OK":"ERR" );
// 增删改要提交
sqlSession2.commit();
sqlSession2.close();
System.out.println("---------------------------------" );
// 获得一个会话3
SqlSession sqlSession3 = sqlSessionFactory.openSession();
UserMapper mapper3 = sqlSession3.getMapper(UserMapper.class);
User user3 = mapper3.findUserById(1);
System.out.println("user3 = " + user3);
sqlSession3.close();
}
引入依赖
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.2.8version>
dependency>
配置db.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/database_name?useSSL=false
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=5
jdbc.maxActive=20
jdbc.minIdle=3
jdbc.maxWait=0
jdbc.timeBetweenEvictionRunsMillis=0
jdbc.minEvictableIdleTimeMillis=0
替换原有的连接池类型
需要创建一个类,继承Mybatis提供一个数据库连接池的工厂类
package com.qf.util;
import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.datasource.pooled.PooledDataSourceFactory;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class MyDruidDataSourceFactory extends PooledDataSourceFactory {
// 构造方法
public MyDruidDataSourceFactory(){
// 创建alibaba的连接池
this.dataSource = new DruidDataSource();
}
}
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="com.qf.util.MyDruidDataSourceFactory">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
dataSource>
environment>
environments>
后续正常使用CRUD,可以减少查询时间.
只有给定数据库表,然后逆向工程可以直接根据表自动生成实体类,接口文件,CRUD方法以及对应的映射文件,以及文件中写好SQL语句.
1 加入逆向工程的依赖
<dependencies>
<dependency>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-coreartifactId>
<version>1.3.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
dependencies>
2 pom中加入逆向工程的插件
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generatorgroupId>
<artifactId>mybatis-generator-maven-pluginartifactId>
<version>1.3.5version>
<configuration>
<configurationFile>src\main\resources\generator.xmlconfigurationFile>
<verbose>trueverbose>
<overwrite>trueoverwrite>
configuration>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.47version>
dependency>
dependencies>
plugin>
plugins>
build>
3 编写逆向所需配置文件
DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="testTables" targetRuntime="MyBatis3">
<plugin type="org.mybatis.generator.plugins.ToStringPlugin"/>
<commentGenerator>
<property name="suppressAllComments" value="true"/>
commentGenerator>
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
connectionURL="jdbc:mysql://localhost:3306/java2212"
userId="root"
password="123456">
jdbcConnection>
<javaTypeResolver>
<property name="forceBigDecimals" value="false"/>
javaTypeResolver>
<javaModelGenerator targetPackage="com.qf.model"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="false"/>
<property name="trimStrings" value="true"/>
javaModelGenerator>
<sqlMapGenerator targetPackage="com.qf.mapper"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="false"/>
sqlMapGenerator>
<javaClientGenerator type="XMLMAPPER"
targetPackage="com.qf.mapper"
targetProject=".\src\main\java">
<property name="enableSubPackages" value="false"/>
javaClientGenerator>
<table tableName="admin"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
<table tableName="user"
enableCountByExample="false"
enableUpdateByExample="false"
enableDeleteByExample="false"
enableSelectByExample="false"
selectByExampleQueryId="false"/>
context>
generatorConfiguration>
4 运行
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。
单例模式是一种对象创建型模式。
Servlet默认是单例的。
单例模式有三个要点:
- 某个类只能有一个实例
- 它必须自行创建这个实例
- 它必须自行向整个系统提供这个实例。
(1)由于每次使用new关键字来实例化Singleton 类时都将产生一个新对象,为了确保Singleton 实例的唯一性,我们需要禁止类的外部直接使用new来创建对象,因此需要将Singleton 的构造函数的可见性为private
(3)为了保证成员变量的封装性,我们将Singleton 类型的single的可见性设置为private,但外界该如何使用该成员变量并何时实例化该成员变量呢?答案是增加一个公有的静态方法Singleton getInstance(),
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
//1 私有的构造器
private Singleton() {}
//2 私有 静态 Singleton类型 成员变量
private static Singleton single=null;
//3 公有静态方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
不管有么有被被人使用,先把对象创建出来
//饿汉式单例类.在类初始化时,已经自行实例化
public class Singleton1 {
//1 私有构造器
private Singleton1() {}
//2 直接创建私有 静态(可以不是final)对象
private static final Singleton1 single = new Singleton1();
//3 静态方法
public static Singleton1 getInstance() {
return single;
}
}