Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

简介: Mybatis学习第四天:Mybatis延迟加载懒加载,一级缓存,二级缓存,注解开发

文章目录

Mybatis中的延迟加载

image.png

一对一进行延迟加载

一对一的情况下一般是采用立即加载,但为了好理解延迟加载在这里就进行一下演示

在之前 账户和用户的表中,我们一个账户只能是归一个用户所有,所以我们采用查询账户的方式来演示延时加载

首先进行准备工作:

  1. 实现根据id来查询用户的功能<select id="findById"  resultType="com.gegege.domain.User"  parameterType="java.lang.Integer"> select * from user where id =  #{uid}; </select>
  2. 在Account中添加私有成员变量 private User user2;并提供get和set方法
  3. Account的Test方法

首先我们在IAccountDao.xml添加一个resultMap

<resultMap id="accountusermap" type="com.gegege.domain.Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <association property="user2" column="uid" javaType="com.gegege.domain.User" select="com.gegege.dao.IUserDao.findById"></association>
</resultMap>

这里要注意的是<association property="user2" column="uid"  javaType="com.gegege.domain.User"  select="com.gegege.dao.IUserDao.findById"></association>

其中property 对应的我们在准备工作第二步中创建的变量

column对应的是要传递给select映射的参数

select 对应要调用的select映射的ID

javaType对应延迟加载的javabean

这时更改findall对应的select语句

    <select id="FindAll" resultMap="accountusermap">
        select * from account
    </select>

在这时运行会发现 语句还是一次性执行完

image.png

https://mybatis.net.cn/configuration.html#settings网站中找到

要更改这两个属性值才可以实现延迟加载

image.png

于是我们更改sqlmapconfig.xml

在里添加以下内容即可实现延迟加载

<settings>
        <!--开启mybatis全局进行延迟加载的开关-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

image.png

那么我们删掉遍历输出的语句试一下

    @Test
    public void FindAllTest() {
        List<Account> list=dao.FindAll();
    }

发现只执行了单表上的内容

image.png

此时一对一的延迟加载便完成了

一对多的延迟加载

user的javabean中要有private List<Account> list;以便实现一对多

将IUserDao.xml改为

    <resultMap id="selectUserAccount" type="com.gegege.domain.User">
        <id property="id" column="id"></id>
        <result property="username" column="username"></result>
        <result property="birthday" column="birthday"></result>
        <result property="sex" column="sex"></result>
        <result property="address" column="address"></result>
        <collection property="list" column="id"  ofType="com.gegege.domain.Account" select="com.gegege.dao.IAccountDao.findByUid">
        </collection>
    </resultMap>
    <sql id="select*"> select * from user</sql>
    <select id="findAll" resultMap="selectUserAccount">
        SELECT * FROM user
    </select>

注意其中select="com.gegege.dao.IAccountDao.findByUid"

那么我们下一步要做的就是在IAccountDao.java和IAccountDao.xml添加findByUid方法

<mapper namespace="com.gegege.dao.IAccountDao">
    <sql id="select_account"> select * from account</sql>
    <resultMap id="accountusermap" type="com.gegege.domain.Account">
        <id property="id" column="id"></id>
        <result property="uid" column="uid"></result>
        <result property="money" column="money"></result>
        <association property="user2" column="uid" javaType="com.gegege.domain.User" select="com.gegege.dao.IUserDao.findById"></association>
    </resultMap>
    <select id="FindAll" resultMap="accountusermap">
        select * from account
    </select>
    <select id="findByUid" resultType="com.gegege.domain.Account">
        select * from account where uid = #{uid}
    </select>
</mapper>
List<Account> findByUid(int uid);

最后别忘了更改sqlmapconfig.xml

在里添加以下内容即可实现延迟加载

<settings>
        <!--开启mybatis全局进行延迟加载的开关-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

缓存机制:

就是将用户经常查询的数据的结果的一个保存,保存到一个内存中(缓存就是内存中的一个对象),用户在查询的时候就不用到数据库文件中查询(磁盘),从而减少与数据库的交付次数提高了响应速度,解决了并发系统的西能问题

image.png

一级缓存

我们用以下代码测试一下一级缓存

    /**
     * 测试一级缓存
     */
    @Test
    public void testFirstLevelCache() {
        User user1 = dao.findById(41);
        System.out.println(user1);
        User user2 = dao.findById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

image.png

发现只执行了一次sql语句

还有user1和user2两个对象的地址是相同的,那么证明这个这个对象是从缓存中拿出来的

那么对于重要数据是不能保存在缓存里的

所以我们要清空缓存,清空缓存有两种方式

    /**
     * 测试一级缓存清空
     * 已知在关闭session会清空一级缓存
     */
    @Test
    public void testFirstLevelCacheClean() {
        User user1 = dao.findById(41);
        System.out.println(user1);
        session.close();
        session=sqlSessionFactory.openSession();
        dao=session.getMapper(IUserDao.class);
        User user2 = dao.findById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }
    /**
     * 测试一级缓存清空
     * 已知在关闭session会清空一级缓存
     */
    @Test
    public void testFirstLevelCacheClean() {
        User user1 = dao.findById(41);
        System.out.println(user1);
        //清空cache
        session.clearCache();
        User user2 = dao.findById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

这两个程序执行的结果是这样的

image.png

这样执行的就是两遍sql语句

image.png

    /**
     * 测试一级缓存清空
     * 已知在关闭session会清空一级缓存
     */
    @Test
    public void testFirstLevelCacheClean() {
        User user1 = dao.findById(41);
        System.out.println(user1);
        //清空cache
        User user=new User();
        int num=new Random().nextInt();
        user.setId(42);
        user.setUsername("dd"+ num);
        user.setAddress("s" + num);
        user.setBirthday(new Date());
        user.setSex("s");
        dao.upDateUser(user);
        User user2 = dao.findById(41);
        System.out.println(user2);
        System.out.println(user1==user2);
    }

在两个查询中间插入一个更新操作,这时执行程序就会有这样的结果

image.png

二级缓存

image.png

先写测试类

public class cache2_test {
    private InputStream in;
    private SqlSessionFactoryBuilder sessionFactoryBuilder;
    private SqlSessionFactory factory1;
    @Before
    public void before() throws IOException {
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //创建工具
        sessionFactoryBuilder=new SqlSessionFactoryBuilder();
        factory1 = sessionFactoryBuilder.build(in);
    }
    @After
    public void after() throws IOException {
        in.close();
    }
    /**
     * 测试二级缓存
     */
    @Test
    public void testTwoLevelCache() {
        SqlSession session1 = factory1.openSession();
        IUserDao dao1 = session1.getMapper(IUserDao.class);
        User user1 = dao1.findById(41);
        System.out.println(user1);
        session1.close();
        SqlSession session2 = factory1.openSession();
        IUserDao dao2 =session2.getMapper(IUserDao.class);
        User user2 = dao2.findById(41);
        System.out.println(user2);
        session2.close();
        System.out.println(session1==session2);
    }
}

运行发现仍然执行了两遍sql语句,这表示两次数据都是从数据库里存取的

这是因为二级缓存默认是不开启的

首先在sqlmapconfig.xml配置一下setting (因为此配置默认是true此配置可以省略)

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

然后在IUserDao.xml里添加这两个标签

image.png

此时运行时结果:

image.png

我们可以发现这时只执行了一次sql语句,证明二级缓存生效了

但我们还可以发现,两个对象的地址不一样,所以对比结果为false,这是因为mybatis中二级缓存的存取方式是吧数据散装进行存储,而不是直接存储对象

注解开发

注解开发比较方便简单,在公司项目中也用的越来越多了,所以有必要学习注解开发

注解开发——单表的增删改查

创建maven项目

创建javabean的类和Iuserdao的接口

创建SqlMapConfig.xml,jdbcConfig.properties,log4j.properties

要注意对应dao接口的资源库路径下不能有对应的xml文件,否则会报错

在SqlMapConfig.xml添加如下内容

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="jdbcConfig.properties"></properties>
    <!--配置别名-->
    <typeAliases>
        <package name="com.mybatis.domain"/>
    </typeAliases>
    <!--配置环境-->
    <environments default="mysql">
        <environment id="mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    <!--指定带有注解的接口所在位置-->
    <mappers>
        <package name="com.mybatis.dao"/>
    </mappers>
</configuration>

在IUserDao中插入以下内容:

package com.mybatis.dao;
import com.mybatis.domain.User;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
/**
 * mybatis的四种注解
 * @Select @Install @Update @Delete
 */
public interface IUserDao {
    /**
     *查询所有用户
     * @return
     */
    @Select(value = "select * from user")
    List<User> selectAll();
    /**
     * 插入用户
     * @param user
     */
    @Insert(value = "insert into user (username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday})")
    void install(User user);
    /**
     * 修改用户
     * @param user
     */
    @Update(value = "update user set username=#{username}, sex=#{sex}, address=#{sex}, birthday=#{birthday} where id=#{id}")
    void updateUser(User user);
    /**
     * 删除用户
     * @param id
     */
    @Delete(value = "delete from user where id=#{id}")
    void deleteUser(int id);
    /**
     * 获取数据个数
     * @return
     */
    @Select(value = "select count(*) from user ")
    int getNumber();
    /**
     * 根据ID查询
     * @return
     */
    @Select(value = "select * from user where id=#{id}")
    User selectById(int id);
    /**
     * 模糊查询
     * @param username
     * @return
     */
    @Select(value = "select * from user where username Like #{username}")
    List<User> findUserByName(String username);
}

创建测试类进行测试

public class mybatisAnno_test {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession sqlSession;
    private IUserDao dao;
    @Before
    public void init() throws IOException {
        //获取字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //根据字节构建sqlsessionfactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //根据sqlsessionfactory生产session
        sqlSession = factory.openSession(true);
        //根据sqlsession获取Dao代理对象
        dao = sqlSession.getMapper(IUserDao.class);
    }
    @After
    public void end() throws IOException {
        //释放资源
        in.close();
        sqlSession.close();
    }
    /**
     * 测试查询
     */
    @Test
    public void testfind() {
        //用代理对象执行方法
        List<User> users = dao.selectAll();
        users.forEach(System.out::println);
    }
    /**
     * 测试插入
     */
    @Test
    public void testinsert() {
        User user=new User();
        user.setAddress("jijijijij");
        user.setBirthday(new Date());
        user.setUsername("吉良吉影");
        user.setSex("男");
        dao.install(user);
    }
    /**
     * 测试更新
     */
    @Test
    public void testUpdate() {
        User user=new User();
        user.setId(55);
        user.setAddress("aaaaa");
        user.setBirthday(new Date());
        user.setUsername("空条承太郎");
        user.setSex("男");
        dao.updateUser(user);
    }
    /**
     * 测试删除
     */
    @Test
    public void testdelete() {
        dao.deleteUser(56);
    }
    /**
     * 测试获取个数
     */
    @Test
    public void testGetNumber() {
        System.out.println(dao.getNumber());
    }
    /**
     * 测试根据id查询
     */
    @Test
    public void testFindById() {
        User user = dao.selectById(55);
        System.out.println(user);
    }
    /**
     * 测试模糊查询
     */
    @Test
    public void testFindByName() {
        //用代理对象执行方法
        List<User> users = dao.findUserByName("%王%");
        users.forEach(System.out::println);
    }
}

此时所有测试便都可以正常执行

注解开发——建立表与实体类的对应关系

先删除除了查询之外的函数及抽象类

重新更改javabean中的参数,使其与数据库表中不对应

public class User implements Serializable {
    private int userid;
    private String username;
    private String useraddress;
    private String usersex;
    private Date userbirthday;
    public int getUserid() {
        return userid;
    }
    public void setUserid(int userid) {
        this.userid = userid;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getUseraddress() {
        return useraddress;
    }
    public void setUseraddress(String useraddress) {
        this.useraddress = useraddress;
    }
    public String getUsersex() {
        return usersex;
    }
    public void setUsersex(String usersex) {
        this.usersex = usersex;
    }
    public Date getUserbirthday() {
        return userbirthday;
    }
    public void setUserbirthday(Date userbirthday) {
        this.userbirthday = userbirthday;
    }
    @Override
    public String toString() {
        return "User{" +
                "userid=" + userid +
                ", username='" + username + '\'' +
                ", useraddress='" + useraddress + '\'' +
                ", usersex='" + usersex + '\'' +
                ", userbirthday=" + userbirthday +
                '}';
    }
}

在IUserDao中添加@Results, @Result,@ResultMap

/**
 * mybatis的四种注解
 * @Select @Install @Update @Delete
 */
public interface IUserDao {
    /**
     *查询所有用户
     * @return
     */
    @Select(value = "select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,column = "ID" ,property = "userid"),
            @Result(column = "username" ,property = "username"),
            @Result(column = "birthday", property = "userbirthday"),
            @Result(column = "sex",property = "usersex"),
            @Result(column = "address",property = "useraddress")
    })
    List<User> selectAll();
    /**
     * 根据ID查询
     * @return
     */
    @Select(value = "select * from user where id=#{id}")
    @ResultMap(value = { "userMap"})
    User selectById(int id);
    /**
     * 模糊查询
     * @param username
     * @return
     */
    @Select(value = "select * from user where username Like #{username}")
    @ResultMap(value = { "userMap"})
    List<User> findUserByName(String username);
}

注解开发——多表查询:

一对一查询或者叫多对一 (一个账户只属于一个用户)

创建account的javabean

注意其中有user变量

public class Account implements Serializable {
    private int id;
    private int uid;
    private double money;
    private User user;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public int getUid() {
        return uid;
    }
    public void setUid(int uid) {
        this.uid = uid;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", uid=" + uid +
                ", money=" + money +
                '}'+user;
    }
}

再创建IAccountDao.java

public interface IAccountDao {
    @Select(value = "select * from Account")
    @Results(id = "accountMap",value = {
            @Result(id = true,column = "id",property = "id"),
            @Result(column = "uid",property = "uid"),
            @Result(column = "money",property = "money"),
            @Result(column = "uid",property = "user",one = @One(select = "com.mybatis.dao.IUserDao.selectById",fetchType = FetchType.EAGER))
    })
    List<Account> findAll();
}

需要注意@Result(column = "uid",property = "user",one = @One(select = "com.mybatis.dao.IUserDao.selectById",fetchType = FetchType.EAGER))

这句代码:

property 为javabean里的容器名

column的参数为要传递的形参参数

one是指mybatis里一对一(多对一)的查找

one注解里的 select是要填写全限定类名及方法名

fetchType 里 EAGER代表立即加载,LAZY是延迟加载或者叫懒加载

这时创建一个test运行即可看见结果

public class MybatisAccount_test {
    private InputStream in;
    private SqlSessionFactory factory;
    private SqlSession sqlSession;
    private IAccountDao dao;
    @Before
    public void init() throws IOException {
        //获取字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //根据字节构建sqlsessionfactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //根据sqlsessionfactory生产session
        sqlSession = factory.openSession(true);
        //根据sqlsession获取Dao代理对象
        dao = sqlSession.getMapper(IAccountDao.class);
    }
    @After
    public void end() throws IOException {
        //释放资源
        in.close();
        sqlSession.close();
    }
    /**
     * 测试查询
     */
    @Test
    public void testfind() {
        //用代理对象执行方法
        List<Account> users = dao.findAll();
        users.forEach(System.out::println);
    }
}

运行结果:

image.png

一对多查询——一个用户对应多个账户

为IAccountDao添加抽象类

    /**
     * 根据用户id查询账户
     * @param uid
     * @return
     */
    @Select(value = "select * from Account where uid=#{uid}")
    List<Account> findByUid(int uid);

更改User的javabean,增加账户的list

private List<Account> accounts = new ArrayList<Account>();
    public List<Account> getAccounts() {
        return accounts;
    }
    public void setAccounts(List<Account> accounts) {
        this.accounts = accounts;
    }

增加下面这句参数

image.png

    /**
     *查询所有用户
     * @return
     */
    @Select(value = "select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,column = "ID" ,property = "userid"),
            @Result(column = "username" ,property = "username"),
            @Result(column = "birthday", property = "userbirthday"),
            @Result(column = "sex",property = "usersex"),
            @Result(column = "address",property = "useraddress"),
            @Result(column = "id",property = "accounts",many = @Many(select = "com.mybatis.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))
    })
    List<User> selectAll();

这句注解的意思与上面一对一的意思类似,只是把one换成了more(一对一换成了多对一)

立即加载换成了懒加载

然后运行,即可执行成功

image.png

注解开发——缓存配置

使用下面的代码进行测试:

public class mybatisAnno_test {
    private InputStream in;
    private SqlSessionFactory factory;
    @Before
    public void init() throws IOException {
        //获取字节输入流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //根据字节构建sqlsessionfactory
        factory = new SqlSessionFactoryBuilder().build(in);
        //根据sqlsessionfactory生产session
    }
    @After
    public void end() throws IOException {
        //释放资源
        in.close();
    }
    /**
     * 测试根据id查询
     */
    @Test
    public void testFindById() {
        SqlSession sqlSession = factory.openSession(true);
        IUserDao dao = sqlSession.getMapper(IUserDao.class);
        User user = dao.selectById(42);
        System.out.println(user);
        sqlSession.close();
        SqlSession sqlSession2 = factory.openSession(true);
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.selectById(42);
        System.out.println(user2);
        sqlSession2.close();
        System.out.println(user==user2);
    }
}

image.png

可以看到他其实是进行了两次查询,这证明此时并没有用到二级缓存

这时我们修改sqlmapconfig.xml此配置可以省略,因为默认值也为true

 <!--配置缓存-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

然后在IUserDao类中添加注解@CacheNamespace(blocking = true)

@CacheNamespace(blocking = true)
public interface IUserDao {
    /**
     *查询所有用户
     * @return
     */
    @Select(value = "select * from user")
    @Results(id = "userMap",value = {
            @Result(id = true,column = "ID" ,property = "userid"),
            @Result(column = "username" ,property = "username"),
            @Result(column = "birthday", property = "userbirthday"),
            @Result(column = "sex",property = "usersex"),
            @Result(column = "address",property = "useraddress"),
            @Result(column = "id",property = "accounts",many = @Many(select = "com.mybatis.dao.IAccountDao.findByUid",fetchType = FetchType.LAZY))
    })
    List<User> selectAll();
}

此时再次运行原来的方法,就可以看到开启了二级缓存的效果:

image.png


相关文章
|
2月前
|
存储 缓存 芯片
让星星⭐月亮告诉你,当我们在说CPU一级缓存二级缓存三级缓存的时候,我们到底在说什么?
本文介绍了CPU缓存的基本概念和作用,以及不同级别的缓存(L1、L2、L3)的特点和工作原理。CPU缓存是CPU内部的存储器,用于存储RAM中的数据和指令副本,以提高数据访问速度,减少CPU与RAM之间的速度差异。L1缓存位于处理器内部,速度最快;L2缓存容量更大,但速度稍慢;L3缓存容量最大,由所有CPU内核共享。文章还对比了DRAM和SRAM两种内存类型,解释了它们在计算机系统中的应用。
104 1
|
3月前
|
缓存 Java 数据库连接
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
文章介绍了MyBatis的缓存机制,包括一级缓存和二级缓存的配置和使用,以及如何整合第三方缓存EHCache。详细解释了一级缓存的生命周期、二级缓存的开启条件和配置属性,以及如何通过ehcache.xml配置文件和logback.xml日志配置文件来实现EHCache的整合。
mybatis复习05,mybatis的缓存机制(一级缓存和二级缓存及第三方缓存)
|
21天前
|
存储 缓存 监控
后端开发中的缓存机制:深度解析与最佳实践####
本文深入探讨了后端开发中不可或缺的一环——缓存机制,旨在为读者提供一份详尽的指南,涵盖缓存的基本原理、常见类型(如内存缓存、磁盘缓存、分布式缓存等)、主流技术选型(Redis、Memcached、Ehcache等),以及在实际项目中如何根据业务需求设计并实施高效的缓存策略。不同于常规摘要的概述性质,本摘要直接点明文章将围绕“深度解析”与“最佳实践”两大核心展开,既适合初学者构建基础认知框架,也为有经验的开发者提供优化建议与实战技巧。 ####
|
1月前
|
SQL 缓存 Java
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
本文详细介绍了MyBatis的各种常见用法MyBatis多级缓存、逆向工程、分页插件 包括获取参数值和结果的各种情况、自定义映射resultMap、动态SQL
【详细实用のMyBatis教程】获取参数值和结果的各种情况、自定义映射、动态SQL、多级缓存、逆向工程、分页插件
|
1月前
|
SQL 缓存 Java
MyBatis如何关闭一级缓存(分注解和xml两种方式)
MyBatis如何关闭一级缓存(分注解和xml两种方式)
69 5
|
1月前
|
存储 缓存 Java
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
Spring缓存注解【@Cacheable、@CachePut、@CacheEvict、@Caching、@CacheConfig】使用及注意事项
199 2
|
2月前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
|
2月前
|
SQL 缓存 Java
JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
这篇文章详细介绍了JVM中类文件的初始化过程、硬件层面的数据一致性问题、缓存行和伪共享、指令乱序执行问题,以及如何通过`volatile`关键字和`synchronized`关键字来保证数据的有序性和可见性。
35 3
|
2月前
|
前端开发 Java 数据库连接
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
本文是一份全面的表白墙/留言墙项目教程,使用SpringBoot + MyBatis技术栈和MySQL数据库开发,涵盖了项目前后端开发、数据库配置、代码实现和运行的详细步骤。
73 0
表白墙/留言墙 —— 中级SpringBoot项目,MyBatis技术栈MySQL数据库开发,练手项目前后端开发(带完整源码) 全方位全步骤手把手教学
|
2月前
|
缓存 NoSQL Java
Springboot自定义注解+aop实现redis自动清除缓存功能
通过上述步骤,我们不仅实现了一个高度灵活的缓存管理机制,还保证了代码的整洁与可维护性。自定义注解与AOP的结合,让缓存清除逻辑与业务逻辑分离,便于未来的扩展和修改。这种设计模式非常适合需要频繁更新缓存的应用场景,大大提高了开发效率和系统的响应速度。
83 2
下一篇
DataWorks