MyBatis 高级使用

文章目录

动态SQL语句

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

if

需要判断的时候,条件写在test中

xml 复制代码
    <select id="selectListIf" parameterType="user" resultMap="BaseResultMap" >
        select
            <include refid="baseSQL"></include>
        from t_user
        <where>
            <if test="id != null">
                and id = #{id}
            </if>
            <if test="userName != null">
                and user_name = #{userName}
            </if>
        </where>
    </select>

choose

需要选择一个条件的时候

xml 复制代码
    <!-- choose 的使用 -->
    <select id="selectListChoose" parameterType="user" resultMap="BaseResultMap" >
        select
        <include refid="baseSQL"></include>
        from t_user
        <where>
            <choose>
                <when test="id != null">
                    id = #{id}
                </when>
                <when test="userName != null and userName != ''">
                    and user_name like CONCAT(CONCAT('%',#{userName,jdbcType=VARCHAR}),'%')
                </when>
                <otherwise>

                </otherwise>
            </choose>
        </where>
    </select>

trim

需要去掉where、and、逗号之类的符号的时候

xml 复制代码
    <!--
        trim 的使用
        替代where标签的使用
    -->
    <select id="selectListTrim" resultMap="BaseResultMap" 
            parameterType="user">
        select <include refid="baseSQL"></include>
        <trim prefix="where" prefixOverrides="AND |OR ">
            <if test="userName!=null">
                and user_name = #{userName}
            </if>
            <if test="age != 0">
                and age = #{age}
            </if>
        </trim>
    </select>

foreach

需要遍历集合的时候

xml 复制代码
    <delete id="deleteByList" parameterType="java.util.List">
         delete from t_user 
         where id in
        <foreach collection="list" item="item" open="(" separator="," close=")">
             #{item.id,jdbcType=INTEGER}
        </foreach>
    </delete>

批量操作

在项目中会有一些批量操作的场景,比如导入文件批量处理数据的情况,当数据量非常大,比如超过几万条的时候,在Java代码中循环发送SQL到数据库执行肯定是不现实的,因为这个意味着要跟数据库创建几万次会话。即使在同一个连接中,也有重复编译和执行SQL的开销。

例如循环插入10000条(大约耗时3秒钟):

java 复制代码
public class Test03Batch {

    public SqlSession session;

    @Before
    public void init() throws IOException {
        // 1.获取配置文件
        InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
        // 2.加载解析配置文件并获取SqlSessionFactory对象
        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
        // 3.根据SqlSessionFactory对象获取SqlSession对象
        session = factory.openSession();
    }

    /**
     * 循环插入10000
     */
    @Test
    public void test1(){
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        for (int i=2000; i< count; i++) {
            User user = new User();
            user.setUserName("a"+i);
            mapper.insertUser(user);
         }
         session.commit();
         session.close();
         long end = System.currentTimeMillis();
         System.out.println("循环批量插入"+count+"条,耗时:" + (end -start )+"毫秒");
    }
}

在MyBatis里面是支持批量的操作的,包括批量的插入、更新、删除。我们可以直接传入一个List、Set、Map或者数组,配合动态SQL的标签,MyBatis会自动帮我们生成语法正确的SQL语句。

批量插入

批量插入只要在values后面增加插入的值就可以了。

sql 复制代码
insert into tbl_emp (emp_id, emp_name, gender,email, d_id) values ( ?,?,?,?,? ),( ?,?,?,?,? ),( ?,?,?,?,? )

在Mapper文件里面,我们使用foreach标签拼接 values部分的语句:

xml 复制代码
    <!-- 批量插入 -->
    <insert id="insertUserList" parameterType="java.util.List" >
        insert into t_user(user_name,real_name)
        values
        <foreach collection="list" item="user" separator=",">
            (#{user.userName},#{user.realName})
        </foreach>

    </insert>

Java代码里面,直接传入一个List类型的参数。

java 复制代码
    /**
     * 批量插入
     */
    @Test
    public void test2(){
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        List<User> list = new ArrayList<>();
        for (int i=2000; i< count; i++) {
            User user = new User();
            user.setUserName("a"+i);
            list.add(user);
        }
        mapper.insertUserList(list);
        session.commit();
        session.close();
        long end = System.currentTimeMillis();
        System.out.println("循环批量插入"+count+"条,耗时:" + (end -start )+"毫秒");
    }

使用批量插入方式,插入一万条大约耗时1秒钟。动态SQL批量插入效率要比循环发送SQL执行要高得多。最关键的地方就在于减少了跟数据库交互的次数,并且避免了开启和结束事务的时间消耗。

批量更新

批量更新通过case when,来匹配id相关的字段值

sql 复制代码
update t_user set 
user_name = 
case id 
when ? then ? 
when ? then ? 
when ? then ? end ,
real_name = 
case id
when ? then ? 
when ? then ? 
when ? then ? end 
where id in ( ? , ? , ? )

所以在Mapper文件里面最关键的就是case when和where的配置。

xml 复制代码
    <update id="updateUserList">
     update t_user set
        user_name =
        <foreach collection="list" item="user" index="index" separator=" " open="case id" close="end">
        when #{user.id} then #{user.userName}
        </foreach>
         ,real_name =
         <foreach collection="list" item="user" index="index" separator=" " open="case id" close="end">
          when #{user.id} then #{user.realName}
         </foreach>
         where id in
         <foreach collection="list" item="item" open="(" separator="," close=")">
          #{item.id,jdbcType=INTEGER}
         </foreach>
     </update>

java代码实现

java 复制代码
    /**
     * 批量更新
     */
    @Test
    public void test3(){
        long start = System.currentTimeMillis();
        UserMapper mapper = session.getMapper(UserMapper.class);
        int count = 12000;
        List<User> list = new ArrayList<>();
        for (int i=2000; i< count; i++) {
            User user = new User();
            user.setId(i);
            user.setUserName("a"+i);
            list.add(user);
        }
        mapper.updateUserList(list);
        session.commit();
        session.close();
        long end = System.currentTimeMillis();
        System.out.println("批量更新"+count+"条,耗时:" + (end -start )+"毫秒");
    }

批量删除

批量删除也是类似的。

xml 复制代码
    <delete id="deleteByList" parameterType="java.util.List">
         delete from t_user where id in
        <foreach collection="list" item="item" open="(" separator="," close=")">
            #{item.id,jdbcType=INTEGER}
        </foreach>
    </delete>

BatchExecutor

MyBatis的动态标签的批量操作也是存在一定的缺点的,比如数据量特别大的时候,拼接出来的SQL语句过大。

MySQL的服务端对于接收的数据包有大小限制,max_allowed_packet 默认是 4M,需要修改默认配置或者手动地控制条数,才可以解决这个问题。

java 复制代码
Caused by: com.mysql.jdbc.PacketTooBigException: Packet for query is too large (7188967 > 4194304). You can change this value on the server by setting the max_allowed_packet' variable.

在全局配置文件中,可以配置默认的Executor的类型(默认是SIMPLE)。其中有一种BatchExecutor。

xml 复制代码
<setting name="defaultExecutorType" value="BATCH" />

也可以在创建会话的时候指定执行器类型

java 复制代码
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);

Executor

  1. SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。
  2. ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map内,供下一次使用。简言之,就是重复使用Statement对象。
  3. BatchExecutor:执行update(没有select,JDBC批处理不支持select),将所有sql都添加到批处理中(addBatch()),等待统一执行(executeBatch()),它缓存了多个Statement对象,每个Statement对象都是addBatch()完毕后,等待逐一执行executeBatch()批处理。与JDBC批处理相同。executeUpdate()是一个语句访问一次数据库,executeBatch()是一批语句访问一次数据库(具体一批发送多少条SQL跟服务端的max_allowed_packet有关)。BatchExecutor底层是对JDBC ps.addBatch()和ps. executeBatch()的封装。

关联查询

嵌套查询

在查询业务数据的时候经常会遇到关联查询的情况,比如查询员工就会关联部门(一对一),查询学生成绩就会关联课程(一对一),查询订单就会关联商品(一对多)等等。

嵌套查询 1对1 , 1个用户对应一个部门

xml 复制代码
 
    <resultMap id="nestedMap1" type="user">
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR" />
        <result property="realName" column="real_name" jdbcType="VARCHAR" />
        <result property="password" column="password" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
        <result property="dId" column="d_id" jdbcType="INTEGER"/>
        <association property="dept" javaType="dept">
            <id column="did" property="dId"/>
            <result column="d_name" property="dName"/>
            <result column="d_desc" property="dDesc"/>
        </association>
    </resultMap>

    <select id="queryUserNested" resultMap="nestedMap1">
        SELECT
            t1.`id`
            ,t1.`user_name`
            ,t1.`real_name`
            ,t1.`password`
            ,t1.`age`
            ,t2.`did`
            ,t2.`d_name`
            ,t2.`d_desc`
        FROM t_user t1
        LEFT JOIN
            t_department t2
            ON t1.`d_id` = t2.`did`
    </select>

嵌套查询 1对多 ,1个部门有多个用户

xml 复制代码
<!-- -->
    <resultMap id="nestedMap2" type="dept">
        <id column="did" property="dId"/>
        <result column="d_name" property="dName"/>
        <result column="d_desc" property="dDesc"/>
        <collection property="users" ofType="user">
            <id property="id" column="id" jdbcType="INTEGER"/>
            <result property="userName" column="user_name" jdbcType="VARCHAR" />
            <result property="realName" column="real_name" jdbcType="VARCHAR" />
            <result property="password" column="password" jdbcType="VARCHAR"/>
            <result property="age" column="age" jdbcType="INTEGER"/>
            <result property="dId" column="d_id" jdbcType="INTEGER"/>
        </collection>
    </resultMap>
    <select id="queryDeptNested" resultMap="nestedMap2">
        SELECT
            t1.`id`
            ,t1.`user_name`
            ,t1.`real_name`
            ,t1.`password`
            ,t1.`age`
            ,t2.`did`
            ,t2.`d_name`
            ,t2.`d_desc`
        FROM t_user t1
        RIGHT JOIN
            t_department t2
            ON t1.`d_id` = t2.`did`
    </select>

延迟加载

在MyBatis里面可以通过开启延迟加载的开关来解决这个问题。

在settings标签里面可以配置:

xml 复制代码
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!--当开启时,任何方法的调用都会加载该对象的所有属性。默认false,可通过select标签的 fetchType来覆盖-->
<setting name="aggressiveLazyLoading" value="false"/>
<!-- MyBatis 创建具有延迟加载能力的对象所用到的代理工具,默认JAVASSIST -->
<setting name="proxyFactory" value="CGLIB" />

lazyLoadingEnabled决定了是否延迟加载(默认false)。

aggressiveLazyLoading决定了是不是对象的所有方法都会触发查询。

1对1的延迟加载配置

xml 复制代码
    <!-- 延迟加载 1对1 -->
    <resultMap id="nestedMap1Lazy" type="user">
        <id property="id" column="id" jdbcType="INTEGER"/>
        <result property="userName" column="user_name" jdbcType="VARCHAR" />
        <result property="realName" column="real_name" jdbcType="VARCHAR" />
        <result property="password" column="password" jdbcType="VARCHAR"/>
        <result property="age" column="age" jdbcType="INTEGER"/>
        <result property="dId" column="d_id" jdbcType="INTEGER"/>
        <association property="dept" javaType="dept" column="d_id" select="queryDeptByUserIdLazy">

        </association>
    </resultMap>
    <resultMap id="baseDept" type="dept">
        <id column="did" property="dId"/>
        <result column="d_name" property="dName"/>
        <result column="d_desc" property="dDesc"/>
    </resultMap>
    <select id="queryUserNestedLazy" resultMap="nestedMap1Lazy">
        SELECT
            t1.`id`
            ,t1.`user_name`
            ,t1.`real_name`
            ,t1.`password`
            ,t1.`age`
            ,t1.d_id
        FROM t_user t1
    </select>
    <select id="queryDeptByUserIdLazy" parameterType="int" resultMap="baseDept">
        select * from t_department where did = #{did}
    </select>

开启了延迟加载的开关,调用user.getDept()时才会发起第二次查询;

java 复制代码
    /**
     * 1对1  关联查询 延迟加载
     * @throws Exception
     */
    @Test
    public void test03() throws Exception{
        init();
        UserMapper mapper = session.getMapper(UserMapper.class);
        List<User> users = mapper.queryUserNestedLazy();
        for (User user : users) {
      
            System.out.println(user.getUserName() + "---->"+user.getDept());
        }
    }

分页操作

逻辑分页

MyBatis里面有一个逻辑分页对象RowBounds,里面主要有两个属性,offset和limit(从第几条开始,查询多少条)。我们可以在Mapper接口的方法上加上这个参数,不需要修改xml里面的SQL语句。

接口中定义

java 复制代码
    public List<User> queryUserList(RowBounds rowBounds);

测试类

java 复制代码
    @Test
    public void test01() throws Exception{
        init();
        UserMapper mapper = session.getMapper(UserMapper.class);
        // 设置分页的数据
        RowBounds rowBounds = new RowBounds(1,3);
        List<User> users = mapper.queryUserList(rowBounds);
        for (User user : users) {
            System.out.println(user);
        }
    }

RowBounds的工作原理其实是对ResultSet的处理。它会舍弃掉前面offset条数据,然后再取剩下的数据的limit条。

java 复制代码
// DefaultResultSetHandler.java
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
   DefaultResultContext<Object> resultContext = new DefaultResultContext();
   ResultSet resultSet = rsw.getResultSet();
   this.skipRows(resultSet, rowBounds);
   while(this.shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
       ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(resultSet, resultMap, (String)null);
       Object rowValue = this.getRowValue(rsw, discriminatedResultMap, (String)null);
       this.storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
  }
}

很明显,如果数据量大的话,这种翻页方式效率会很低(跟查询到内存中再使用subList(start,end)没什么区别)。所以我们要用到物理翻页。

物理分页

物理翻页是真正的翻页,它是通过数据库支持的语句来翻页。

第一种简单的办法就是传入参数(或者包装一个page对象),在SQL语句中翻页。

xml 复制代码
<select id="selectUserPage" parameterType="map" resultMap="BaseResultMap">
  select * from t_user limit #{curIndex} , #{pageSize}
</select>

这种方式会存在两个问题:第一个问题是我们要在Java业务代码里面去计算起止序号;第二个问题是:每个需要翻页的Statement都要编写limit语句,会造成Mapper映射器里面很多代码冗余。

需要一种通用的方式,不需要去修改配置的任何一条SQL语句,我们只要传入当前是第几页,每页多少条就可以了,自动计算出来起止序号。

我们最常用的做法就是使用翻页的插件,比如PageHelper。

java 复制代码
// pageSize每一页几条
PageHelper.startPage(pn, 10);
List<Employee> emps = employeeService.getAll();
// navigatePages 导航页码数
PageInfo page = new PageInfo(emps, 10);
return Msg.success().add("pageInfo", page);

PageHelper是通过MyBatis的拦截器实现的,简单地来说,它会根据PageHelper的参数,改写我们的SQL语句。比如MySQL会生成limit语句,Oracle会生成rownum语句,SQL Server会生成top语句。

MyBatis Generator

在项目中使用MyBaits的时候,针对需要操作的一张表,需要创建实体类、Mapper映射器、Mapper接口,里面又有很多的字段和方法的配置,这部分的工作是非常繁琐的。而大部分时候我们对于表的基本操作是相同的,比如根据主键查询、根据Map查询、单条插入、批量插入、根据主键删除等等等等。当我们的表很多的时候,意味着有大量的重复工作。

所以有没有一种办法,可以根据我们的表,自动生成实体类、Mapper映射器、Mapper接口,里面包含了我们需要用到的这些基本方法和SQL呢?

MyBatis也提供了一个代码生成器,叫做MyBatis Generator,简称MBG(它是MyBatis的一个插件)。我们只需要修改一个配置文件,使用相关的jar包命令或者Java代码就可以帮助我们生成实体类、映射器和接口文件。

MBG的配置文件里面有一个Example的开关,这个东西用来构造复杂的筛选条件的,换句话说就是根据我们的代码去生成where条件。

原理:在实体类中包含了两个有继承关系的Criteria,用其中自动生成的方法来构建查询条件。把这个包含了Criteria的实体类作为参数传到查询参数中,在解析Mapper映射器的时候会转换成SQL条件。

添加配置文件

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<generatorConfiguration>
    <!-- 数据库的驱动包路径 -->
    <classPathEntry location="C:\Users\mysql-connector-java-8.0.11.jar" />

    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!-- 去掉生成文件中的注释 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>
        <!-- 数据库链接URL、用户名、密码 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/db?characterEncoding=utf-8&serverTimezone=UTC"
                        userId="root"
                        password="123456">
            <property name="nullCatalogMeansCurrent" value="true" />
        </jdbcConnection>

        <javaTypeResolver >
            <property name="forceBigDecimals" value="false" />
        </javaTypeResolver>
        <!-- 生成模型的包名和位置 -->
        <javaModelGenerator targetPackage="com.domain" targetProject="./src/main/java">
            <!-- 是否在当前路径下新加一层schema -->
            <property name="enableSubPackages" value="false" />
            <property name="trimStrings" value="true" />
        </javaModelGenerator>
        <!-- 生成的映射文件包名和位置 -->
        <sqlMapGenerator targetPackage="com.mapper"  targetProject="./src/main/java">
            <property name="enableSubPackages" value="false" />
        </sqlMapGenerator>
        <!-- 生成DAO的包名和位置 -->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.mapper"  targetProject="./src/main/java">
            <property name="enableSubPackages" value="false" />
        </javaClientGenerator>

        <table  tableName="t_user" domainObjectName="User"   />


    </context>
</generatorConfiguration>

添加插件

在pom.xml中添加对应的插件

xml 复制代码
    <build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.2</version>
                <configuration>
                    <!-- 指定配置文件的位置 -->
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                </configuration>
            </plugin>
        </plugins>
    </build>

生成

maven项目中执行插件帮助我们快速生成需要的表结构对应的相关文件。

通用Mapper

当我们的表字段发生变化的时候,我们需要修改实体类和Mapper文件定义的字段和方法。如果是增量维护,那么一个个文件去修改。如果是全量替换,我们还要去对比用MBG生成的文件。字段变动一次就要修改一次,维护起来非常麻烦。

方式一

因为MyBatis的Mapper是支持继承的。所以可以把Mapper.xml和Mapper接口都分成两个文件。一个是MBG生成的,这部分是固定不变的。然后创建DAO类继承生成的接口,变化的部分就在DAO里面维护。

java 复制代码
public interface UserMapperExt extends UserMapper {
    public List<User> selectUserByName(String userName);
}

对应的映射文件

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.mapper.UserMapperExt" >
  <resultMap id="BaseResultMapExt" type="com.domain.User" >
    <id column="id" property="id" jdbcType="INTEGER" />
    <result column="user_name" property="userName" jdbcType="VARCHAR" />
    <result column="real_name" property="realName" jdbcType="VARCHAR" />
    <result column="password" property="password" jdbcType="VARCHAR" />
    <result column="age" property="age" jdbcType="INTEGER" />
    <result column="d_id" property="dId" jdbcType="INTEGER" />
    <result column="i_id" property="iId" jdbcType="INTEGER" />
  </resultMap>
  
  <select id="selectUserByName" resultMap="BaseResultMapExt" >
    select * from t_user where user_name = #{userName}
  </select>
</mapper>

在全局配置文件中我们也需要扫描

xml 复制代码
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
        <mapper resource="mapper/UserMapperExt.xml"/>
    </mappers>

所以以后只要修改Ext的文件就可以了。这么做有一个缺点,就是文件会增多。

方式二

既然针对每张表生成的基本方法都是一样的,也就是公共的方法部分代码都是一样的,能不能把这部分合并成一个文件,让它支持泛型呢?

编写一个支持泛型的通用接口,比如叫GPBaseMapper,把实体类作为参数传入。这个接口里面定义了大量的增删改查的基础方法,这些方法都是支持泛型的。

自定义的Mapper接口继承该通用接口,例如BlogMapper extends GPBaseMapper,自动获得对实体类的操作方法。遇到没有的方法,我们依然可以在我们自己的Mapper里面编写。

能想到的解决方案,早就有人做了这个事了,这个东西就叫做通用Mapper

用途:主要解决单表的增删改查问题,并不适用于多表关联查询的场景。

除了配置文件变动的问题之外,通用Mapper还可以解决:

  1. 每个Mapper接口中大量的重复方法的定义;
  2. 屏蔽数据库的差异;
  3. 提供批量操作的方法;
  4. 实现分页。

使用方式:在Spring中使用时,引入jar包,替换applicationContext.xml中的sqlSessionFactory和configure。

xml 复制代码
<bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
   <property name="basePackage" value="com.crud.dao"/>
</bean>

MyBatis-Plus

MyBatis-Plus是原生MyBatis的一个增强工具,可以在使用原生MyBatis的所有功能的基础上,使用plus特有的功能。

MyBatis-Plus的核心功能:

通用 CRUD:定义好Mapper接口后,只需要继承BaseMapper 接口即可获得通用的增删改查功能,无需编写任何接口方法与配置文件。

条件构造器:通过EntityWrapper(实体包装类),可以用于拼接 SQL 语句,并且支持排序、分组查询等复杂的SQL。

代码生成器:支持一系列的策略配置与全局配置,比MyBatis的代码生成更好用。

相关推荐
yangchanghua1111 小时前
pgsql 如何查询今天范围内的数据(当天0点0分0秒 - 当天23点59分59秒....)
数据库·pgsql
larance1 小时前
SQLAlchemy 的异步操作来批量保存对象列表
数据库·python
python_chai1 小时前
从数据汇总到高级分析,SQL 查询进阶实战(下篇)—— 分组、子查询与窗口函数全攻略
数据库·sql·mysql
在努力的前端小白2 小时前
Spring Boot 敏感词过滤组件实现:基于DFA算法的高效敏感词检测与替换
java·数据库·spring boot·文本处理·敏感词过滤·dfa算法·组件开发
未来之窗软件服务2 小时前
自建知识库,向量数据库 (九)之 量化前奏分词服务——仙盟创梦IDE
数据库·仙盟创梦ide·东方仙盟·自建ai·ai分词
冒泡的肥皂5 小时前
MVCC初学demo(一
数据库·后端·mysql
.Shu.6 小时前
Redis Reactor 模型详解【基本架构、事件循环机制、结合源码详细追踪读写请求从客户端连接到命令执行的完整流程】
数据库·redis·架构
薛晓刚9 小时前
当MySQL的int不够用了
数据库
SelectDB技术团队9 小时前
Apache Doris 在菜鸟的大规模湖仓业务场景落地实践
数据库·数据仓库·数据分析·apache doris·菜鸟技术
星空下的曙光10 小时前
mysql 命令语法操作篇 数据库约束有哪些 怎么使用
数据库·mysql