一、完成连接池的配置和使用

1、连接池

创建一个java.sql.Connection对象的代价是如此巨大,是因为创建一个Connection对象的过程,在底层就相当于和数据库建立的通信连接,在建立通信连接的过程,消耗了这么多的时间,而往往我们建立连接后(即创建Connection对象后),就执行一个简单的SQL语句,然后就要抛弃掉,对于一个复杂的数据库应用,情况就完全不同了。频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。

对于需要频繁地跟数据库交互的应用程序,可以在创建了Connection对象,并操作完数据库后,可以不释放掉资源,而是将它放到内存中,当下次需要操作数据库时,可以直接从内存中取出Connection对象,不需要再创建了,这样就极大地节省了创建Connection对象的资源消耗。由于内存也是有限和宝贵的,这又对我们对内存中的Connection对象怎么有效地维护提出了很高的要求。

我们将在内存中存放Connection对象的容器称之为 连接池(Connection Pool)

总结:

1、连接池是面向数据库连接的

2、连接池是为了优化数据库的连接资源

2、mybatis中的连接池

在 Mybatis 中我们将它的数据源 dataSource 分为以下几类

Mybatis 将它自己的数据源分为三类:

①、UNPOOLED 不使用连接池的数据源

会为每一个数据库操作创建一个新的连接,并关闭它。该方式使用于只有小规模数量并发用

户的简单应用程序上。

②、POOLED 使用连接池的数据源

会创建一个数据库连接池,连接池中的一个连接会被用做数据库操作。一旦数据库操作完

成,Mybatis会将此连接返回给连接池。在开发或测试环境中,常用此方式。

③、JNDI 使用 JNDI 实现的数据源

Mybatis从在应用服务器向配置好的JNDI数据源dataSource获取数据库连接。在生产环境

中优先考虑这种方式。

3、源码分析

3.1、三类数据源的关系在Mybatis内部定义了一个接口DataSourceFactory以及它的三个实现类

UnpooledDataSourceFactory 、PooledDataSourceFactory 、JndiDataSourceFactorypublic interface DataSourceFactory {void setProperties(Properties props);DataSource getDataSource();}public class UnpooledDataSourceFactory implements DataSourceFactory {....}public class PooledDataSourceFactory extends UnpooledDataSourceFactory{....}public class JndiDataSourceFactory implements DataSourceFactory{...}

具体结构如下:

与这些数据源工厂类相对应的也定义了相应的数据源对象,其中UnpooledDataSourceFactory和PooledDataSourceFactory工厂返回的分别是UnpooledDataSource和PooledDataSource,而JndiDataSourceFactory则是根据配置返回相应的数据源。

public class PooledDataSource implements DataSource{...}

public class UnpooledDataSource implements DataSource {...}

3.2、mybatis中数据源的创建过程

1、先从配置文件开始

2、在mybatis初始化的时候,在解析到节点时,会根据相应的type类型设置来创建相应的数据源工厂类实例,如下所示:

在上面代码里,根据type类型去寻找相应的数据源工厂类并实例化一个。具体每一个配置对应什么类,在Configuration类中已经进行了声明,如下所示:

2.1、之后,从数据源工厂类实例中通过getDataSource()方法获取一个DataSource对象;

2.2、MyBatis创建了DataSource实例后,会将其放到Configuration对象内的Environment对象中, 供以后使用。如下所示:

2.3、数据源DataSource对象什么时候创建数据库连接

当我们需要创建SqlSession对象并需要执行SQL语句时,这时候MyBatis才会去调用dataSource对象来创建java.sql.Connection对象。也就是说,java.sql.Connection对象的创建一直延迟到执行SQL语句的时候。

对于上面这段代码,我们通过调试会发现,在前两句的时候其实是没有创建数据库连接的,而是在执行openSession.selectOne()方法的时候才触发了数据库连接的创建。

3、非池化的数据源UnpooledDataSource

代码分析

从上面的代码可以知道UnpooledDataSource创建数据库连接的主要流程,具体时序图如下所示:

(a)调用initializeDriver()方法进行驱动的初始化;

判断driver驱动是否已经加载到内存中,如果还没有加载,则会动态地加载driver类,并实例化一个Driver对象,使用DriverManager.registerDriver()方法将其注册到内存中,以供后续使用。

(b)调用DriverManager.getConnection()获取数据库连接;

(c)对数据库连接进行一些设置,并返回数据库连接Connection;

username和password是什么传递给数据源的呢?

这个问题其实上面已经提到过了,在mybatis初始化的时候,就已经解析了元素,并将其下相关的配置作为数据源的配置初始化进去了。也就是下面这段逻辑:

至此,对于UnpooledDataSource数据源算是有比较清楚的了解了。

4、带连接池的PooledDataSource

在mybatis中,定义了一个数据库连接池状态的类PoolState,在这个类里,除维护了数据源实例,还维护着数据库连接。数据库连接被分成了两种状态类型并存放在两个列表中:idleConnections和activeConnections。

idleConnections:

空闲(idle)状态PooledConnection对象被放置到此集合中,表示当前闲置的没有被使用的PooledConnection集合,调用PooledDataSource的getConnection()方法时,会优先从此集合中取PooledConnection对象。当用完一个java.sql.Connection对象时,MyBatis会将其包裹成PooledConnection对象放到此集合中。

activeConnections:

活动(active)状态的PooledConnection对象被放置到名为activeConnections的ArrayList中,表示当前正在被使用的PooledConnection集合,调用PooledDataSource的getConnection()方法时,会优先从idleConnections集合中取PooledConnection对象,如果没有,则看此集合是否已满,如果未满,PooledDataSource会创建出一个PooledConnection,添加到此集合中,并返回。

下面我们看看怎么从连接池中获取一个数据库连接,还是从PooledDataSource类开始看起。

这里都是调用了popConnection()方法,然后返回其代理对象。

private PooledConnection popConnection(String username, String password) throws SQLException { boolean countedWait = false; PooledConnection conn = null; long t = System.currentTimeMillis(); int localBadConnectionCount = 0; while (conn == null) { synchronized (state) { if (!state.idleConnections.isEmpty()) { // Pool has available connection conn = state.idleConnections.remove(0); if (log.isDebugEnabled()) { log.debug("Checked out connection " + conn.getRealHashCode() + " from pool."); } } else { // Pool does not have available connection if (state.activeConnections.size() < poolMaximumActiveConnections) { // Can create new connection conn = new PooledConnection(dataSource.getConnection(), this); if (log.isDebugEnabled()) { log.debug("Created connection " + conn.getRealHashCode() + "."); } } else { // Cannot create new connection PooledConnection oldestActiveConnection = state.activeConnections.get(0); long longestCheckoutTime = oldestActiveConnection.getCheckoutTime(); if (longestCheckoutTime > poolMaximumCheckoutTime) { // Can claim overdue connection state.claimedOverdueConnectionCount++; state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime; state.accumulatedCheckoutTime += longestCheckoutTime; state.activeConnections.remove(oldestActiveConnection); if (!oldestActiveConnection.getRealConnection().getAutoCommit()) { try { oldestActiveConnection.getRealConnection().rollback(); } catch (SQLException e) { log.debug("Bad connection. Could not roll back"); } } conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this); conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp()); conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp()); oldestActiveConnection.invalidate(); if (log.isDebugEnabled()) { log.debug("Claimed overdue connection " + conn.getRealHashCode() + "."); } } else { // Must wait try { if (!countedWait) { state.hadToWaitCount++; countedWait = true; } if (log.isDebugEnabled()) { log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection."); } long wt = System.currentTimeMillis(); state.wait(poolTimeToWait); state.accumulatedWaitTime += System.currentTimeMillis() - wt; } catch (InterruptedException e) { break; } } } } if (conn != null) { // ping to server and check the connection is valid or not if (conn.isValid()) { if (!conn.getRealConnection().getAutoCommit()) { conn.getRealConnection().rollback(); } conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password)); conn.setCheckoutTimestamp(System.currentTimeMillis()); conn.setLastUsedTimestamp(System.currentTimeMillis()); state.activeConnections.add(conn); state.requestCount++; state.accumulatedRequestTime += System.currentTimeMillis() - t; } else { if (log.isDebugEnabled()) { log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection."); } state.badConnectionCount++; localBadConnectionCount++; conn = null; if (localBadConnectionCount > (poolMaximumIdleConnections + poolMaximumLocalBadConnectionTolerance)) { if (log.isDebugEnabled()) { log.debug("PooledDataSource: Could not get a good connection to the database."); } throw new SQLException("PooledDataSource: Could not get a good connection to the database."); } } } } } if (conn == null) { if (log.isDebugEnabled()) { log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection."); } throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection."); } return conn; }

我们看下上面的方法都做了什么:

1. 先看是否有空闲(idle)状态下的PooledConnection对象,如果有,就直接返回一个可用的PooledConnection对象;否则进行第2步。

2. 查看活动状态的PooledConnection池activeConnections是否已满;如果没有满,则创建一个新的PooledConnection对象,然后放到activeConnections池中,然后返回此PooledConnection对象;否则进行第三步;

3. 看最先进入activeConnections池中的PooledConnection对象是否已经过期:如果已经过期,从activeConnections池中移除此对象,然后创建一个新的PooledConnection对象,添加到activeConnections中,然后将此对象返回;否则进行第4步;

4. 线程等待,循环2步。

流程图如下:

当我们拿到数据库连接PooledConnection后,我们在使用完之后一般来说就要关闭这个数据库连接,但是,对于池化来说,我们关闭了一个数据库连接并不是真正意义上想关闭这个连接,而是想把它放回到数据库连接池中。

怎么实现呢?mybatis中使用了代理模式有效的解决了该问题。就是返回给外部使用的数据库连接其实是一个代理对象(通过调用getProxyConnection()返回的对象)。这个代理对象是在真实数据库连接创建的时候被创建的,如下所示:

而在调用这个代理对象的各个方法的时候,都是通过反射的方式,从invoke()方法进入,我们来看看:

我们可以看到,这里做了一个特殊处理,那就是判断调用的方法名是否是close()方法,如果是的话,就调用数据源对象的pushConnection()方法将数据库连接放回到连接池中,如下所示:

简单的说下上面这个方法的逻辑:

1. 首先将当前数据库连接从活动数据库连接集合activeConnections中移除;

2. 判断当前数据库连接是否有效,如果无效,则跳转到第4步;如果有效,则继续下面

的判断;

3. 判断当前idleConnections集合中的闲置数据库连接数量是否没超过设置的阈值且是

当前数据库连接池的创建出来的链接,如果是,则将该数据库连接放回到

idleConnections集合中并且通知在此据库连接池上等待的请求对象线程,如果不

是,则将数据库连接关闭;

4. 将连接池中的坏数据库连接数+1,并返回;

总结:

通过分析,大家应该对数据库的连接池技术有个大概的了解,需要知道mybatis中数据库连接池的大致实现流程,也对我们之后的程序开发很有帮助。同时,大家也要养成看源码的习惯,通过对源码进行分析,也可以提升自己的编程功底。

-------------------------------------------------------------------------------------------------------------------------------------

二、Mybatis的事务

1、 JDBC中的事务

在 JDBC 中我们可以通过手动方式将事务的提交改为手动方式,通过 setAutoCommit()方法就可以调整。通过 JDK 文档,我们找到该方法如下:

那么我们的 Mybatis 框架因为是对 JDBC 的封装,所以 Mybatis 框架的事务控制方式,本身也是用 JDBC 的setAutoCommit()方法来设置事务提交方式的。

2、 Mybatis中的事务提交方式

Mybatis 中事务的提交方式,本质上就是调用 JDBC 的 setAutoCommit()来实现事务控制。 我们运行之前所写的代码:

private SqlSession session = null; @Before public void init() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); session = sessionFactory.openSession(true); } @After public void destory() { //session.commit(); session.close(); } @Test public void test() { User user = new User("安娜", "123", "jack", 0); //System.out.println(user); session.insert("com.tledu.mjw.pojo.User.add", user); }

这里我们设置了自动提交事务之后,就不需要在进行commit操作了

3、 事务的回滚

对于我们开发过程,也会遇到报错的情况,这个时候为了保证数据的一致性我们就需要进行事务的回滚,比如我们有一个操作需要同时更新用户表和地址表,这个时候更新用户表的时候成功了,但是更新地址表的时候出现了一个特别长的字段,导致更新失败,这个时候,我们需要将数据进行回滚。

private SqlSession session = null; @Before public void init() throws IOException { String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream); session = sessionFactory.openSession(); } @After public void destory() { //session.commit(); session.close(); } @Test public void test() { try { User user = new User("安东尼", "123", "jack", 0); session.insert("com.tledu.mjw.pojo.User.add", user); throw new Exception(); } catch (Exception e) { e.printStackTrace(); session.rollback(); } }

-------------------------------------------------------------------------------------------------------------------------------------

三、 事务的隔离级别

上面的事务在单个情况下一般不会出现什么问题,但是如果同时运行多个,就会出现问题了。我们知道并发操作总是会出现各种各样的问题,对于事务来说就会出现下面三个典型的问题:

(1)脏读

有俩事务T1,T2。如果T1读了一条数据,这条数据是T2更新的但是还没提交,突然T2觉得不合适进行事务回滚了,也就是不提交了。此时T1读的数据就是无效的数据。

(2)不可重复读

有俩事务T1,T2。如果T1读了一条数据,之后T2更新了这条数据,T1再次读取就发现值变了。

(3)幻读

有俩事务T1,T2。如果T1读了一条数据,之后T2插入了一些新的数据,T1再次读取就会多出现一些数据。

如何去解决这些问题呢?既然多个事务同时运行不好,那就把他们隔离开来。这时候就用到了事务的隔离性。

√: 可能出现    ×: 不会出现

脏读

不可重复读

幻读

说明

Read uncommitted

直译就是"读未提交",意思就是即使一个更新语句没有提交,但是别 的事务可以读到这个改变.

这是很不安全的。允许任务读取数据库中未提交的数据更改,也称为脏读。

Read committed

×

直译就是"读提交",可防止脏读,意思就是语句提交以后即执行了COMMIT以后,别的事务才

能读到这个改变. 只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别

Repeatable read

×

×

直译就是"可以重复读",这是说在同一个事务里面先后执行同一个查询语句的时候,得到的结果

是一样的.在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,

该隔离级别消除了不可重复读,但是还存在幻象读。

Serializable

×

×

×

直译就是"序列化",意思是说这个事务执行的时候不允许别的事务并发执行. 完全串行化的读,

每次读都需要获得表级共享锁,读写相互都会阻塞

mybatis中也可以设置隔离级别,只不过增加了一个没有事务的属性

session.openSession(这里可以写级别);

public enum TransactionIsolationLevel { NONE(Connection.TRANSACTION_NONE), READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED), READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED), REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ), SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE); private final int level; TransactionIsolationLevel(int level) { this.level = level; } public int getLevel() { return level; } }

-------------------------------------------------------------------------------------------------------------------------------------

四、 延迟加载策略

4.1 什么是延迟加载

延迟加载:

就是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。延迟加载也称懒加载.

好处:

先从单表查询,需要时再从关联表去关联查询,大大提高数据库性能,因为查询单表要比关

联查询多张表速度要快。

坏处:

因为只有当需要用到数据时,才会进行数据库查询,这样在大批量数据查询时,因为查询工

作也要消耗时间,所以可能造成用户等待时间变长,造成用户体验下降。

4.2、需求

需求:

查询产品类型(Category)信息并且关联查询产品(Product)信息。

如果先查询产品类型(Category)信息即可满足要求,当我们需要查询产品(Product)信息时再查询

产品(Product)信息。把对产品(Product)信息按需去查询就是延迟加载。

实现多表操作时,我们使用了resultMap来实现一对一,一对多关系的操作。主要是通过

association、collection 实现一对一及一对多映射。

association、collection 具备延迟加载功能。

4.3、collection 实现懒加载

4.3.1、未实现延迟加载的时候

我们之前未实现延迟加载的时候,每次操作,都会直接查询出我们需要的数据

可以看到控制台打印了两条sql

4.3.2、开启延迟加载

找到对应设置

mybatis – MyBatis 3 | 配置

通过lazyLoadingEnabled、aggressiveLazyLoading可以对延迟加载进行配置

<properties resource="jdbc.properties"></properties> <settings> <setting name="lazyLoadingEnabled" value="true"/> <setting name="aggressiveLazyLoading" value="false"/> </settings>

注意:settings标签一定要放在properties下面

添加了懒加载配置之后,我们在进行列表查询的过程中就不会再做大量的关联查询了,可以

提升列表查询的效率,在我们用到具体字段之后才会进行关联查询。

在CategoryMapper.xml中添加如下代码:

<mapper namespace="com.tledu.mjw.dao"> <resultMap type="Category" id="categoryBean" autoMapping="true"> <id column="id" property="id"/> <result column="name" property="name"/> <collection property="products" ofType="Product" column="id" select="query" autoMapping="true" fetchType="lazy"/> </resultMap> <select id="lazyListCategory" resultMap="categoryBean" parameterType="int"> select * from t_category where id = #{id} </select> <select id="query" resultType="Product" parameterType="int"> select * from t_product where cid = #{id} </select> </mapper>

懒加载效果如下图:

4.3.3、aggressiveLazyLoading

属性开启之后,我们获取到变量的任意属性,就会触发懒加载,而关闭之后,我们

只有触发到关联属性时,才会触发懒加载

4.3.4、配置每个关联字段的加载方式

<resultMap type="Category" id="categoryBean" autoMapping="true"> <id column="id" property="id"/> <result column="name" property="name"/> <collection property="products" ofType="Product" column="id" select="query" fetchType="lazy"/> </resultMap>

4.3.5、association实现懒加载

association的懒加载实现和collection基本类似

-------------------------------------------------------------------------------------------------------------------------------------

五、 使用注解开发

5.1、mybatis常用注解说明

@Insert:实现新增

@Update:实现更新

@Delete:实现删除

@Select:实现查询

@Result:实现结果集封装

@Results:可以与@Result 一起使用,封装多个结果集

@ResultMap:实现引用@Results 定义的封装

@One:实现一对一结果集封装

@Many:实现一对多结果集封装

5.1、实现基本的CRUD

新增加接口GradeMapper ,并在接口中声明的方法上,加上注解

对比配置文件GradeMapper.xml,其实就是把SQL语句从XML挪到了注解上来

public interface GradeMapper {        @Insert(" insert into t_class ( cname ) values (#{cname}) ")      public int add(Grade grade);               @Delete(" delete from t_class where cid= #{cid} ")      public void delete(int id);               @Select("select * from t_class where cid= #{cid} ")      public Grade get(int id);             @Update("update t_class set cname=#{cname} where cid=#{cid} ")      public int update(Grade grade);                @Select(" select * from t_class ")      public List<Grade> list();  }

5.2、关联查询

5.2.1、一对多

创建CategoryMapper接口

新增加CategoryMapper接口,查询所有Category

@Select注解获取Category类本身

@Select(" select * from t_category ")

@Results 通过@Result和@Many中调用ProductMapper.listByCategory()方法相结合,来获

取一对多关系

@Results({@Result(property = "products", javaType = List.class, column = "id",

many = @Many(select = "com.test.mapper.ProductMapper.listByCategory"))})

public interface CategoryMapper {     @Select(" select * from t_category ")     @Results({          @Result(property = "id", column = "id"),         @Result(property = "products",javaType = List.class,column = "id", many =@Many(select = "com.test.mapper.ProductMapper.listByCategory") )             })     public List<Category> list(); }

创建ProductMapperr接口

新增接口ProductMapper

注解@Select用于根据分类id获取产品集合

@Select(" select * from product_ where cid = #{cid}")

public interface ProductMapper { @Select(" select * from t_product where cid = #{cid}") public List<Product> listByCategory(int cid); }

5.2.2、多对一

创建ategoCryMapper接口

public interface CategoryMapper { @Select(" select * from t_category where id = #{id} ") public Category getCateGory(int id); }

创建ProductMapperr接口

public interface ProductMapper { @Select(" select * from t_product ") @Results({ @Result(property = "category" ,column = "cid",one = @One( select = "com.test.mapper.CategoryMapper.getCateGory") ) }) public List<Product> getProductList(); }

-------------------------------------------------------------------------------------------------------------------------------------

六、 mybatis中的缓存

像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提高性能。Mybatis 中缓存分为一级缓存,二级缓存

6.1、一级缓存

一级缓存是 SqlSession 级别的缓存,只要 SqlSession 没有 flush 或 close,它就存在。

6.2、测试

public static void main(String[] args) throws IOException {         String resource = "mybatis-config.xml";         InputStream inputStream = Resources.getResourceAsStream(resource);         SqlSessionFactory sqlSessionFactory = newSqlSessionFactoryBuilder().build(inputStream);         SqlSession session1 = sqlSessionFactory.openSession();           Category c1 = session1.selectOne("getCategory", 1);         System.out.println(c1);         Category c2 = session1.selectOne("getCategory", 1);         System.out.println(c2);           session1.commit();         session1.close();     }

只要通过session查过的数据,都会放在session上,下一次再查询相同id的数据,都直接从缓存中取出来,而不用到数据库里去取了。

在一个Session里查相同id的数据在session1中查询两次id=1的Category对象。

第一次会去数据库中取数据,但是第二次就不会访问数据库了,而是直接从session中取出来。

6.3、总结

一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改,添加,删除,

commit(),close()等方法时,就会清空一级缓存。

第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。得到用户信息,将用户信息存储到一级缓存中。如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息(但是再查询时需要重新执行SQL去数据库查询),避免脏读。

第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。缓存中没有,重新执行SQL去数据库查找,然后再放入缓存中

6.4、二级缓存

Mybatis二级缓存是SessionFactory,如果两次查询基于同一个SessionFactory,那么就从二级缓存中取数据,而不用到数据库里去取了。

6.4.1、启动二级缓存

开启二级缓存

<setting name="cacheEnabled" value="true"/>

6.4.2、在CategoryMapper.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="com.how2java.pojo">         <cache/>         <insert id="addCategory" parameterType="Category" >             insert into category_ ( name ) values (#{name})            </insert>                   <delete id="deleteCategory" parameterType="Category" >             delete from category_ where id= #{id}           </delete>                   <select id="getCategory" parameterType="_int" resultType="Category">             select * from   category_  where id= #{id}            </select>           <update id="updateCategory" parameterType="Category" >             update category_ set name=#{name} where id=#{id}            </update>         <select id="listCategory" resultType="Category">             select * from   category_                 <if test="start!=null and count!=null">                     limit #{start},#{count}                 </if>         </select>         </mapper>

6.4.3、序列化Category

让Category 实现序列化接口

public class Category implements Serializable{     private int id;     private String name;     List<Product> products;     .......        }

6.4.4、运行测试

再次运行TestMybatis,如图所示,在同一个SessionFactory下查询id=2的数据,只有第一次需要执行sql语句,以后都是从缓存中取出

SqlSession session = sqlSessionFactory.openSession(); Category c = new Category(); c.setCid(2); Category c= session.selectOne("listCategory",c); session.commit(); session.close(); SqlSession session2 = sqlSessionFactory.openSession(); Category c2 = session2.selectOne("listCategory", c); session2.commit(); session2.close(); System.out.println(c+"\t"+c2);

6.4.5、总结

  • 在使用二级缓存的时候,需要注意配置mybatis-config.xml中 开启二级缓存

setting name="cacheEnabled" value="true"/>

  • 然后再mapper映射文件中使用cache标签标注开启,并对需要缓存的语句添加

useCache=”true”

  • 在mapper的映射文件中使用,代表当前mapper是开启二级缓存的

在需要二级缓存的查询上增加useCache = true,代表当前查询是需要缓存的

并且对应封装数据的实体类需要实现Serializable 接口

  • 对待缓存的数据,实现Serialization接口,代表这个数据是可序列化

只有当sqlSession close之后,二级缓存才能生效

  • 当执行增删改操作的时候,必须执行commit()才能持久化到数据库中,同时二级缓存清空
  • session.clearCache()无法清除二级缓存,如果需要清除二级缓存,可以通过

sqlSessionFactory.getConfiguration().getCache("缓存id").clear();

  • 但是当我们查询语句中,执行commit() 或者是close()关闭session,都不会清空二级缓存

day_03_连接池、事务、一对多和多对多配置相关推荐

  1. mybatis学习(四)连接池、事务、动态SQL、多表查询

    目录 连接池 事务 动态SQL 1.if标签 2.where标签 3.foreach标签 4.sql标签 多表操作 (一)一对多.多对一 .一对一 1.查询所有账户,在账户信息后显示所属的用户的用户名 ...

  2. 大数据笔记16—java基础篇12(JDBC 、连接池、事务)

    目录 JDBC jdbc概述 jdbc入门案例 API详解 jdbc工具类 预编译执行平台 1.SQL注入问题(安全问题) 2API详解:预处理对象(PreparedStatement) 使用连接池重 ...

  3. Mybatis第三天动态Sql语句、XML中一对多、多对一、多对多该怎么写

    Mybatis第三天 Mybatis中使用unpooled配置连接池原理分析 Mybatis中使用pooled配置连接的原理分析 Mybatis中的事务原理和自动提交设置 Mybatis中的动态sql ...

  4. Hibernate【查询、连接池、逆向工程】

    2019独角兽企业重金招聘Python工程师标准>>> 前言 在Hibernate的第二篇中只是简单地说了Hibernate的几种查询方式....到目前为止,我们都是使用一些简单的主 ...

  5. 修改 连接层_Mybatis连接池_动态sql语句_多表查询实现

    Mybatis连接池 Mybatis中的连接池Mybatis连接池提供了3种方式的配置:配置的位置:主配置文件SqlMapConfig.xml中的dataSource标签,type属性就是表示采用何种 ...

  6. spring-boot中使用druid连接池

      最近因为项目的要求,需要在spring-boot中配置druid连接池,数据库是Oracle,并且是多数据源的连接池,特地写下我的配置经历.   用的框架是spring-boot,数据库是orac ...

  7. 16---Day03-JDBC连接池-笔记++

    typora-root-url: img typora-copy-images-to: img JDBC 回顾 数据库的三大范式 第一范式: 表中的字段不能再拆分(原子性) 第二范式:一张表只描述一件 ...

  8. JDBCC3P0连接池Druid连接池

    typora-root-url: img typora-copy-images-to: img JDBC&连接池 回顾 会使用mysql字符串函数 CONCAT: 连接字符串 CHAR_LEN ...

  9. mysql连接池泄露_一次线上故障:数据库连接池泄露后的思考

    作者:陈朗,普兰金融科技能效工程部开发工程师 一:初步排查 早上作为能效平台系统的使用高峰期,系统负载通常比其它时间段更大一些,某个时间段会有大量用户登录.当天系统开始有用户报障,发布系统线上无法构建 ...

最新文章

  1. Redhat下的yum更改为Centos的免费yum源
  2. 解决Windows Installer的错误
  3. 3,maven使用入门
  4. Kafka消息序列化和反序列化(下)
  5. C++——常用取整方法
  6. Spring Boot学习总结(13)——Spring Boot加载application.properties配置文件顺序规则
  7. IntelliJ Idea中使用Java8新特性lambda表达式
  8. java16进制取前几位_16位16进制数怎么取前8位和后8位
  9. 数学建模基本模型(一) 优化模型
  10. 基于python及图像识别的围棋棋盘棋子识别3——耗时优化(一行代码速度提高600倍)
  11. 菱形c语言思路,c语言打印菱形(c语言打印菱形思路)
  12. 输入一系列整数,建立二叉排序树,并进行前序,中序,后序遍历。
  13. 大学计算机高海波目录,《大学计算机基础》TOC课程教学大纲(定稿3ok).doc
  14. Greenplum常用SQL——通过表名查找shema名
  15. SpringBoot 缓存之 @Cacheable介绍
  16. 论文笔记:Controlling Decoding for More Abstractive Summaries with Copy-Based Networks
  17. 移动硬盘新加卷变为本地磁盘且不能打开磁盘查看文件
  18. CSS网页设计教程:表单Button的Outl…
  19. webRTC基础入门
  20. Python|为什么列表推导式会更快

热门文章

  1. Android 手机采集摄像头视频 socket 视频传输实时传播
  2. 看我小穷仔和富家MM的经典图聊!!
  3. 你偷看的小簧片,其实全都被监视了…
  4. 坐标反算c语言程序,测量坐标计算程序
  5. 在uniapp里面使用阿里矢量图标(iconfont)
  6. 2018年Android源码下载记录
  7. 基于java实现bilibili视频爬虫
  8. 使用Python创建excel文件成功后报错打不开
  9. 错误1:Archive for required library: XXXXXXXXcannot be read or is not a valid ZIP file
  10. Groovy on Grails(Java笨狗)系列---前言(二)