随便看看,写得很拉

Mybatis学习笔记

Mybatis工作原理

  • 解析配置文件(mybatis-config.xml、mapper.xml),Mybatis基于XML配置文件生成Configuration和多个MappedStatement(包括了参数映射配置、动态SQL语句、结果映射配置),其对应着<select|update|delete|insert>标签项。

  • SqlSessionFactoryBuilder创建会话工厂SqlSessionFactory。

  • SqlSessionFactory创建会话SqlSession。

  • 执行器将mapperStatement对象进行解析,sql参数转化、动态sql拼接,生成jdbcStatement对象,使用parameterHandler绑定参数。

  • JDBC执行sql,借助MappedStatement中的结果映射关系,使用ResultSetHandler将返回结果转化成HashMap、JavaBean等存储结构并返回。

  • 关闭SqlSession会话。

1、搭建环境

1.1 搭建数据库

以个人为例,在WampServer中的MySQL创建mybatis数据库,同时创建mybatis表

image-20220114170406698

1.2 创建Maven项目(模块)

image-20220114170933000

1.3 导入依赖

在pom.xml的dependencies中加入以下代码。

如果在父项目的pom.xml中添加以下依赖,那么在子项目中即可使用,无需再次添加。

是否需要将依赖写在父项目或者子项目中,视情况而定。

<dependencies>
    <!--导入mysql依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.19</version>
    </dependency>

    <!--导入mybatis依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>

    <!--导入junit-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13</version>
    </dependency>
</dependencies>

1.4 添加核心配置文件

在模块中的resource文件夹下添加mybatis-config.xml,并写入以下代码

<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=GMT%2B8&amp;useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value=""/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!-- 使用mapper.xml实现sql用resource -->
        <mapper resource="MybatisMapper.xml"/>
        
        <!-- 使用接口注解实现sql用class -->
        <mapper class="usermapper.class"/>
        
    </mappers>
</configuration>
  • url、username、password视情况修改
  • 对于每一个新创建的mapper的xml文件,都需要在mappers标签下进行注册,格式如上所示。
  • 为了避免引用错误,尽量将Mapper文件写在Resource文件夹下

2、SqlSession工具类创建

  • 这个工具类主要用于对SqlSessionFactory的初始化配置
package mybatis;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class Utils {

    private static SqlSessionFactory sqlSessionFactory;

    // 初始化Mybatis配置
    static {
        try {
            String resource = "mybatis-config.xml";	// 配置文件名,可更改
            InputStream inputStream = Resources.getResourceAsStream( resource );
            sqlSessionFactory = new SqlSessionFactoryBuilder().build( inputStream );
        } catch ( IOException e ) {
            e.printStackTrace();
        }
    }
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
}

3、创建实体类

  • 实体类根据需求创建
  • 一般包含的元素与数据库表中的字段一一对应。
package pojo;

public class User {
    private int id;
    private String name;

    public User() {
    }

    public User( int id, String name ) {
        this.id = id;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId( int id ) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName( String name ) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

3、编写接口

  • 根据业务需求,编写所需要的业务方法
package mybatis;

import org.apache.ibatis.annotations.Param;
import pojo.User;

import java.util.List;

public interface userMapper {

    List< User > getUserList();

    void insertUser( @Param( "user" ) User user );

    void deleteUser( int id );

    void updateUser( @Param("id") int id, @Param("name") String name );
}

4、编写接口Mapper.xml

  • 这个xml文件的作用于接口实现类的作用相同,只是Mybatis将JDBC代码省略,我们只需写出相应的sql语句即可

  • namespace里面放的是接口名(全限定名)

  • 每个标签里面的 id 对应 方法名

  • resultType可以放实体名(全限定名)和String等类型

  • Mapper写完之后记得在配置文件 config.xml 中注册

  • 参数类型需要在接口方法参数中通过注解@param来进行配置

<?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="mybatis.userMapper">

    <select id="getUserList" resultType="pojo.User">
        select * from user
    </select>

    <insert id="insertUser">
        insert into user(id, user) values(#{user.id}, #{user.name})
    </insert>

    <delete id="deleteUser" parameterType="int">
        delete from user where id = #{id}
    </delete>

    <update id="updateUser">
        update user SET user = #{name} WHERE id = #{id}
    </update>
</mapper>

5、获取接口实现类

  • 在业务层需要与数据库交互时,可通过以下代码来获取DAO实现类
SqlSession sqlSession = Utils.getSqlSession();
userMapper userMapper = sqlSession.getMapper( userMapper.class );
userMapper.deleteUser( 3 );
sqlSession.commit();
sqlSession.close();
// Mybatis CRUD操作
public class MybatisTest {
    @Test
    public void test() {
        
        // 在try的括号中开启sqlSession,无需管理流的关闭
        // 语句块结束之后,能够自动关闭sqlSession
        try ( SqlSession sqlSession = Utils.getSqlSession() ) {
            // sqlSession.getMapper(接口名.class)
            // 在配置文件xml中实现接口,得到实现类
            userMapper userMapper = sqlSession.getMapper( userMapper.class );

            // select
            List< User > userList = userMapper.getUserList();
            for ( User user : userList ) {
                System.out.println( user );
            }

            // insert
            userMapper.insertUser( new User( 3, "杨鑫" ) );

            // update
            userMapper.updateUser( 3, "林耀博" );

            // delete
            userMapper.deleteUser( 3 );

            // select 不需要commit
            // insert、delete、update 事务操作需要commit
            sqlSession.commit();
        }
    }
}

6、CRUD

6.1 select

<select id="getUserList" resultType="pojo.User">
    select * from user
</select>

<select id="getUserList" resultType="pojo.User" parameterType="map">
    select * from user where id = #{key1}, name = #{key2}
</select>
Map<String,Object> map = new HashMap<String,Object>();

map.put("key1","value1");
map.put("key2","value2");

mapper.getUserList(map);

6.2 insert

<insert id="insertUser">
    <!-- user是接口那边的@param("user") -->
    insert into user(id, user) values(#{user.id}, #{user.name})
</insert>

6.3 update

<update id="updateUser">
    <!-- @param("id") -->
    update user set user = #{name} where id = #{id}
</update>

6.4 delete

<delete id="deleteUser" parameterType="int">
    delete from user where id = #{id}
</delete>

6.5 模糊查询SQL

select * from user where name like "%"#{name}"%"

select * from user where name like concat('%',#{name},'%')

7、配置优化

7.1 属性优化

  • 通过配置文件properties存储相关信息
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false
username=root
password=
  • 在核心配置文件mybatis-config.xml中添加内容
  • 通过${key}获取配置文件中对应的value值
<!-- db.properties文件和mybatis-config.xml在同一目录下 -->
<properties resource="db.properties"/>

<dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
</dataSource>

7.2 别名优化

7.2.1 typeAlias

  • 在核心配置文件mybatis-config.xml中添加字段 typeAliases
  • 存在的意义是减少类全限定名的冗余
  • 在底下的所有Mapper均可使用这些别名
<typeAliases>
    <typeAlias type="pojo.User" alias="User"/>
</typeAliases>

7.2.2 package

  • 也可以指定一个包名,Mybatis会在包名下面搜索需要的Java Bean
<typeAliases>
    <package name="pojo"/>
</typeAliases>

7.2.3 注解@Alias (最方便)

  • 也可以通过注解@Alias(" "),在实体类的上方声明
  • 无需通过typeAliases标签配置
@Alias("User")
public class User {
    private int id;
    private String name;
    
    /*
    *	todo
    */
}

8、设置

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

9、分页

9.1 SQL Limit分页

select * from user limit 0,3; //从第0(1)条记录开始,选取3

9.2 RowBounds分页

RowBounds rowBounds = new RowBounds(0,3);

//第一个参数要写全限定名,指向某一个Mapper接口中的某一个方法
List<User> list = sqlSession.selectList("mybatis.userMapper.getUserList", null, rowbounds);

9.3 PageHelper分页插件

9.3.1 使用步骤

  • 导入Maven依赖
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>4.2.0</version>
</dependency>
  • 在mybatis-config.xml中添加默认plugin配置(添加在environment上面)
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <property name="dialect" value="mysql"/>
        <!-- 该参数默认为false -->
        <!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
        <!-- 和startPage中的pageNum效果一样-->
        <property name="offsetAsPageNum" value="true"/>
        <!-- 该参数默认为false -->
        <!-- 设置为true时,使用RowBounds分页会进行count查询 -->
        <property name="rowBoundsWithCount" value="true"/>
        <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
        <!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型)-->
        <property name="pageSizeZero" value="true"/>
        <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
        <!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
        <!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
        <property name="reasonable" value="true"/>
    </plugin>
</plugins>

9.3.2 使用范例

@Test
public void searchUserByParam()
{
    int pageIndex = 2; //获取第2页的数据
    int pageSize = 10; //每页10条数据
    String orderBy = "create_time ASC"; //排序
 
    //分页信息
    //PageHelper.startPage只会在接下来最近的一条查询语句中生效
    //且必须接的是select语句,不能是增删改语句,否则失效
    PageHelper.startPage(pageIndex, pageSize, orderBy);
    
    //查询条件参数类
    UserSearchParam userSearchParam = new UserSearchParam();
    //userSearchParam.setUserName("pan_junbiao的博客"); //查询条件1
    //userSearchParam.setProvince("广东省"); //查询条件2
 
    //执行分页查询
    //userMapper.searchUserList()返回类型为 Page<UserInfo> 然后转化为 PageInfo<UserInfo>
    PageInfo<UserInfo> userInfoPage = new PageInfo<UserInfo>(userMapper.searchUserList(userSearchParam));
 
    //打印用户列表
    List<UserInfo> userInfoList = userInfoPage.getList();
    userInfoList.stream().forEach(System.out::println);
 
    //打印分页信息
    System.out.println("当前页码:第" + userInfoPage.getPageNum() + "页");
    System.out.println("分页大小:每页" + userInfoPage.getPageSize() + "条");
    System.out.println("数据总数:共" + userInfoPage.getTotal() + "条");
    System.out.println("总页数:共" + userInfoPage.getPages() + "页");
}

10、多对一

public class student {
    int id;
    String name;
    Teacher teacher;
}

10.1 按照查询嵌套处理(子查询)

<!--
    思路:
    1.查询所有的学生信息
    2.根据查询出来的学生的tid,寻找对应的老师!子查询
-->
<select id="getstudent" resultMap="studentTeacher1">
    select * from student
</select>

<resultMap id="studentTeacher1" type="student">
    <result property="id" column="id" />
    <result property="name" column="name" />
    
    <!-- 复杂的属性,我们需要单独处理 -->
    <!-- 对象:association 集合: collection -->
    
    <!-- column="tid" 作为参数传递给子查询的#{tid},#{}里面的参数名任意取 -->
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher" />
</resultMap>
<select id="getTeacher" resultType="Teacher">
    select * from teacher where id = #{tid}
</select>

10.2 按照结果嵌套处理(联表查询)

<!-- 按照结果嵌套处理 -->
<select id="getstudent2" resultMap="studentTeacher2">
    select s.id sid,s.name sname ,t.name tname
    from student s,teacher t
    where s.tid = t.id;
</select>

<resultMap id="studentTeacher2" type="student">
    <result property="id" column="sid" />
    <result property="name " column="sname " />
    <association property="teacher" javaType="Teacher">
        <result property="name" column="tname" />
    </association>
</resultMap>

11、一对多

public class Teacher {
    int id;
    String name;
    List<Student> students;
}

11.1 按照查询嵌套处理(子查询)

<select id="getTeacher" resultMap="Teacherstudent">
    select * from mybatis.teacher where id = #{tid}
</select>
<resultMap id="Teacherstudent" type="Teacher">
    <collection property="students" javaType="ArrayList" ofType="Student" 
                select="getstudentByTeacherId" column="id" />
</resultMap>
<select id="getstudentByTeacherId" resultType="student">
    select * from mybatis.student where tid = #{tid}
</select>

11.2 按照结果嵌套处理(联表查询)

<!-- 按结果嵌套查询 -->
<select id="getTeacher" resultMap="TeacherStudent">
    select s.id sid, s.name sname,t.name tname,t.id tid 
    from student s,teacher t
    where s.tid = t.id and t.id = #{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
    <result property="id" column="tid" />
    <result property="name" column="tname" />
    <!-- 复杂的属性,我们需要单独处理 -->
    <!-- 对象:association 集合: collection javaType=""指定属性的类型! -->
    <!-- 集合中的泛型信息,我们使用ofType获取 -->
    <collection property="students" ofType="Student">
        <result property="id" column="sid" />
        <result property="name" column="sname" />
        <result property="tid" column="tid" />
    </collection>
</resultMap>

12、动态SQL

12.1 IF

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from mybatis.blog where 1=1
    <if test="title != nu11">
        and title = #{title}
    </if>
    <if test="author !=-nui1">
        and author = #{author}
    </if>
</select>

12.2 choose、when、otherwise(类似switch)

<select id="findActiveBlogLike" resultType="B1og">
    select * from blog where state = 'ACTIVE'
    <choose>
        <when test="title != null">
            AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
            AND author_name like #{author.name}
        </when>
        <otherwise>
            AND featured = 1
        </otherwise>
    </choose>
</select>

12.3 where

  • where元素只会在至少有一个子元素的条件返回SQL子句的情况下才去插入WHERE"子句。
  • 而且,若语句的开头为AND"或'OR'"where元素也会将它们去除。
<select id="findActiveBlogLike" resultType="Blog">
    SELECT * FROM BLOG
    <where>
        <if test="state != null">
            state = #{state}
        </if>
        <if test="title != null">
            AND title like #{title}
        </if>
        <if test="author != null and author! name != null">
            AND author_name like #{author.name}
        </if>
    </where>
</select>

12.4 set

  • set元素会动态前置SET关键字,同时也会删掉多余的逗号
<update id="updateAuthorIfNecessary" >
    update Author
    <set>
        <if test="username != null">username=#{username},</if>
        <if test="password != null">password=#{password},</if>
        <if test="email != null">email=#{email},</if>
        <if test="bio != null">bio=#{bio}</if>
    </set>
    where id=#{fid}
</update>

12.5 trim

属性 描述
prefix 给sql语句拼接的前缀
suffix 给sql语句拼接的后缀
prefixOverrides 去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides 去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定

示例:

<select id="findUserInfoByTrim" parameterType="Map"  
        resultMap="UserInfoResult">  
    select * from userinfo  
    <trim prefix="where" prefixOverrides="and|or">  
        <if test="department!=null">  
            AND department like #{department}  
        </if>  
        <if test="gender!=null">  
            AND gender=#{gender}  
        </if>  
        <if test="position!=null">  
            AND position like #{position}  
        </if>  
    </trim>  
</select>  

12.6 SQL片段

  • 使用sql标签抽取公共的部分
  • 在需要的地方使用include标签引用即可
<sql id="if-title-author">
    <if test="title != nu11">
        title = #{title}
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

<select id="queryBlogIF" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <include refid="if-title-author"></include>
    </where>
</select>

12.7 foreach

HashMap map = new HashMap();
ArrayList<Integer> ids = new ArrayList<Integer>();
ids.add(1);

map.put( "ids",ids ) ;
List<B1og> blogs = mapper.queryBlogForeach( map );

for (Blog blog : blogs) {
    system.out.print1n(blog);
}
  • collection的对应:
  • map的key为ids,value为id列表
  • id列表变量名为ids,select标签的parameterType为java.util.List
<select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from mybatis.blog
    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id = #{id}
        </foreach>
    </where>
</select>

13、缓存

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  • MyBatis系统中默认定义了两级缓存:一级缓存二级缓存
    • 默认情况下,只有一级缓存开启。(SqlSession级别的缓存,也称为本地缓存)。
    • 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
    • 为了提高扩展性,MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存。

13.1 一级缓存

  • 一级缓存也叫本地缓存:
    • 默认情况开启一级缓存。
    • 与数据库同一次会话期间(SqlSession)查询到的数据会放在本地缓存中。
    • 以后如果需要获取相同的数据,直接从缓存中拿,没必须再去查询数据库;

13.2 二级缓存

  • 二级缓存也叫全局缓存,一级缓存作用域太低了,所以诞生了二级缓存。

  • 基于namespace级别的缓存,一个名称空间(Mapper接口),对应一个二级缓存。

  • 工作机制:

    • 一个会话查询一条数据,这个数据就会被放在当前会话的一级缓存中;
    • 如果当前会话关闭了,这个会话对应的一级缓存就没了;但是我们想要的是,会话关闭了,一级缓存中的数据被保存到二级缓存中。
    • 新的会话查询信息,就可以从二级缓存中获取内容;
    • 不同的mapper查出的数据会放在自己对应的缓存(map)中;
  • 使用步骤:

    • 在mybatis-config.xml中开启全局缓存
    <!-- cacheEnabled默认为true -->
    <settings>
        <setting name="cacheEnabled" value="true" />
    </settings>
    
    • 在需要使用二级缓存的Mapper中开启
    <!--在当前Mapper.xml中使用二级缓存-->
    <cache />
    

    ​ 也可以自定义参数

    <!--在当前Mapper.xml中使用二级缓存-->
    <cache eviction="FIFO"
           flushInterva1="60000"
           size="512"
           readonly="true" />
    

    小结:

    • 只要开启了二级缓存,在同一个Mapper下就有效。

    • 所有的数据都会先放在一级缓存中。

    • 只有当会话提交,或者关闭的时候,才会提交到二级缓存中!

    • 缓存查询顺序:

      • 二级缓存
      • 一级缓存
      • 数据库查询

14、注意事项

14.1 xml文件资源导出失败时

  • 在pom.xml添加以下代码
  • 这样写在java文件夹下的配置文件和Mapper.xml都能导出了
<build>
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

14.2 resource,路径要用 / 分开 ,不要用 .

14.3 实体类属性名和数据库字段名不一致的问题

  • 方案一:SQL语句中,对应字段起别名 as xxx
  • 方案二:resultMap
数据库:id user
实体类:id name
第二个字段没有对应

<!-- 在Mapper.xml中添加以下代码 -->
<!-- 结果集映射 -->
<resultMap id="UserMap" type="User">
    
    <!-- column为数据库中的字段, property为实体类中的属性 -->
    <result column="id" property="id"/>
    <result column="user" property="name"/>
</resultMap>

<select id="getUserById" resultMap="UserMap">
    select* from mybatis.user where id = #{id}
</select>

15、SpringBoot+Mybatis

import org.apache.ibatis.annotations.*;
import springboot.bean.Department;


//Mapper接口通过@Autowired注入
@Mapper   //告诉mybatis这是一个操作数据库的mapper
public interface DepartmentMapper {

    @Select("select * from department where id=#{id}")
    public Department getDeptById(Integer id);

    @Delete("delete from department where id=#{id}")
    public int deleteDeptById(Integer id);

    @Options(useGeneratedKeys = true,keyProperty = "id")   //使用自动生成主键,keyProperty = "id"表示id为封装主键
    @Insert("insert into department(departmentName) values(#{departmentName})")
    public int insertDept(Department department);

    @Update("update department set departmentName=#{departmentName} where id=#{id}")
    public int updateDept(Department department);
}
posted @   Doooong  阅读(57)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示