MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射,MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作

2023-11-18

Mybatis

什么是Mybatis

  • MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
  • MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。
  • MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

持久层

数据持久化
  • 持久化就是将程序的数据在持久状态和瞬间状态转化的过程
  • 持久化就是将我们内存中的数据存在数据库中,
  • 数据库(jdbc),IO文件持久化
  • 生活:冷藏,罐头

为什么需要持久化

  • 内存珍贵
  • 有一些对象数据,不能让他丢掉,让他保存起来
持久层

Dao,Service,Controller层…

  • 完成持久化工作的代码块
  • 层界限十分明显
为什么需要Mybatis
  • 帮助程序员将数据存入到数据库中

  • 方便

  • 传统的jdbc代码太复杂。简化,框架。

  • 帮助程序员将数据存入到数据库中

  • 不用Mybatis也可以,mybatis更容易上手。技术没有高低之分

  • 优点:image-20220223092923094

最重要得是使用的人很多


第一个Mybatis程序

思路:搭建环境–>导入Mybatis–>编写代码–>测试

搭建环境

-- 创建数据库
create database mybatis
-- 进入数据库
use mybatis
-- 创建表
create table user(
-- not null 不允许为空 PRIMARY KEY唯一主键
id int(20) not null PRIMARY KEY,
-- default null 默认为null
name varchar(30) default null,
pwd varchar(30) default null
-- engine=innodb设置数据库引擎为innodb,
-- default charset=utf8;设置默认字符集为utf-8
)engine=innodb default charset=utf8;

-- 新增数据
insert into user(id,name,pwd)values(1,'星辰','123456');
insert into user(id,name,pwd)values(2,'张三','123456');
insert into user(id,name,pwd)values(3,'李四','123456');
insert into user(id,name,pwd)values(4,'王五','123456');

新建项目

  1. 新建一个普通的maven项目(不要选择任何模板)
  2. 清除src目录
  3. 导入maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<!--    夫项目是我们的mybatis,子项目可以引用夫项目的依赖-->
    <parent>
        <artifactId>mybatis</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mybatis-01</artifactId>

<!--    这里一定要写,不然会读取不到我们的mapper-->
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                </includes>
            </resource>

            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.xml</include>
                    <include>**/*.properties</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

创建配置文件

  1. 在子模块的src/resources下创建一个mybatis-config.xml,编写mybatis的核心配置文件
<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="0000"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/yunduo/dao/UserDaoMapper.xml"/>
    </mappers>
</configuration>

2.编写mybtais工具类

package com.yunduo.utils;

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.InputStream;

/**
 * Mybatis工具类
 * 工厂类
 */
public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static{
        try{
            //使用mybatis第一步,获取SqlSessionFactory对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        }catch (Exception exception){
            exception.getMessage();
        }
    }
    //这里的sqlSession对象就相当于jdbc中的Connection对象
    //返回sqlSession对象
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession();
    }
}

3. 编写代码操作数据库

实体类

package com.yunduo.pojo;

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

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

    public User() {
    }

    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;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

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

接口

package com.yunduo.dao;

import com.yunduo.pojo.User;

import java.util.List;

public interface UserDao {
    List<User> getUserList();
}

接口对应sql语句

<?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">

<!--namespace命名空间,需要绑定对应的dao接口-->
<mapper namespace="com.yunduo.dao.UserDao">
<!--    这里ID对应我们接口中的方法  resultType代表返回类型-->
    <select id="getUserList" resultType="com.yunduo.pojo.User">
        select * from user
  </select>
</mapper>

测试

package com.dao;

import com.yunduo.pojo.User;
import com.yunduo.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDao {

    @Test
    public void test(){
        //我们通过工程类创建SqlSession操作数据库的对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //然后通过sqlsession对象获取我们dao层的接口
        //方式一
        com.yunduo.dao.UserDao mapper = sqlSession.getMapper(com.yunduo.dao.UserDao.class);
        //方式二(不推荐使用)
//        List<UserDao> UserDao = sqlSession.selectList("com.yunduo.dao.UserDao.class");

        //返回的对象执行自己的接口
        List<User> userList = mapper.getUserList();
        //如果这里打印出错,那么就是没有配置myabtis-config.xml中mappers中注册到mybatis中,以及要配置maven的pom.xml中build设置过滤
        //输出
        for (User user : userList) {
            System.out.println(user);
        }
        //关闭数据库对象
        sqlSession.close();

    }
}

image-20220223150616516

CRUD

  1. 我们对应的mapper.xml中的namespace一定要和我们对应的接口地址一致,否认绑定不上

image-20220223151259665

  1. ID:对应的就是我们namespace中的方法名字
  2. ResyltType:sql语句的返回值
  3. parameteType:我们sql语句中的参数类型
  4. 增删改:必须提交事务

Dao

Pojo

package com.yunduo.pojo;

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

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

    public User() {
    }

    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;
    }

    public String getPwd() {
        return pwd;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

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

UserMapper

package com.yunduo.dao;

import com.yunduo.pojo.User;

import java.util.List;

public interface UserDaoMapper {
    //查询所有用户
    List<User> getUserList();
    //根据ID查询用户
    User getUserById(User user);
    //新增用户
    int AddUser(User user);
    //根据ID修改用户
    int UpdateUserById(User user);
    //根据用户ID清除用户
    int DeleteUserById(User user);
}

UserMapper.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">

<!--namespace命名空间,需要绑定对应的dao接口-->
<mapper namespace="com.yunduo.dao.UserDaoMapper">
<!--    这里ID对应我们接口中的方法  resultType代表返回类型-->
    <select id="getUserList" resultType="com.yunduo.pojo.User" parameterType="com.yunduo.pojo.User">
        select * from user
  </select>
    <select id="getUserById" resultType="com.yunduo.pojo.User" parameterType="com.yunduo.pojo.User">
        select * from user where id=#{id}
    </select>
    <insert id="AddUser" parameterType="com.yunduo.pojo.User">
        insert into user(id,name,pwd)values(#{id},#{name},#{pwd})
    </insert>
    <update id="UpdateUserById" parameterType="com.yunduo.pojo.User">
        update user set pwd=#{pwd} where id=#{id}
    </update>
    <delete id="DeleteUserById" parameterType="com.yunduo.pojo.User">
        delete from user where id=#{id}
    </delete>

</mapper>

Test

package com.dao;

import com.yunduo.dao.UserDaoMapper;
import com.yunduo.pojo.User;
import com.yunduo.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDao {
    //查询所有用户
    @Test
    public void getUserList() {
        //我们通过工程类创建SqlSession操作数据库的对象
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //然后通过sqlsession对象获取我们dao层的接口
        //方式一
        UserDaoMapper mapper = sqlSession.getMapper(UserDaoMapper.class);
        //方式二(不推荐使用)
//        List<UserDao> UserDao = sqlSession.selectList("com.yunduo.dao.UserDao.class");

        //返回的对象执行自己的接口
        List<User> userList = mapper.getUserList();
        //如果这里打印出错,那么就是没有配置myabtis-config.xml中mappers中注册到mybatis中,以及要配置maven的pom.xml中build设置过滤
        //输出
        for (User user : userList) {
            System.out.println(user);
        }
        //关闭数据库对象
        sqlSession.close();

    }
    //根据ID查询用户
    @Test
    public void getUserById() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDaoMapper user = sqlSession.getMapper(UserDaoMapper.class);
        System.out.println(user.getUserById(new User(1,null,null)));
        sqlSession.close();
    }
    //新增用户
    @Test
    public void AddUser() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDaoMapper mapper = sqlSession.getMapper(UserDaoMapper.class);
        int lao = mapper.AddUser(new User(5, "老六", "1234567"));
        //增删改,必须提交事务
        sqlSession.commit();
        if (lao>0){
            System.out.println("新增成功");
        }else{
            System.out.println("新增失败");
        }
    }
    //根据ID修改用户
    @Test
    public void UpdateUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDaoMapper mapper = sqlSession.getMapper(UserDaoMapper.class);
        int i = mapper.UpdateUserById(new User(5, null, "123456"));
        //提交事务
        sqlSession.commit();
        if (i>0){
            System.out.println("修改成功");
        }else{
            System.out.println("修改失败");
        }
    }
    //根据用户ID清除用户
    @Test
    public void DeleteUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserDaoMapper user = sqlSession.getMapper(UserDaoMapper.class);
        int i=0;
        try{
            i = user.DeleteUserById(new User(5, null, null));
            sqlSession.commit();
        }catch(Exception e){
            e.getMessage();
        }finally {
            sqlSession.rollback();
        }
        if (i>0){
            System.out.println("清除成功");
        }else{
            System.out.println("清除失败");

        }    }
}

利用Map来传递xml中sql参数

Map传递参数,直接在sql中取出来【parameterType=“map”】

对象传递参数,直接在sql中取对象的属性【parameterType=“com.yunduo.pojo.User”】

只有一个基本类型参数的情况下,可以直接在sql中取到【parameterType=“int”】

多个参数用Map或者注解

//根据ID查询用户
User getUserById2(HashMap<String,Object> map);
<!--    这里如果传递是一个map的话,那么我们传递的参数和key对应即可-->
    <select id="getUserById2" resultType="com.yunduo.pojo.User" parameterType="map">
        select * from user where id=#{kkk}
    </select>
@Test
public void get(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDaoMapper user = sqlSession.getMapper(UserDaoMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("kkk",1);
    User userById2 = user.getUserById2(map);
    System.out.println(userById2);
}

image-20220223161821360

模糊查询

我们不能在sql语句中写入%#{value}%

我们需要在我们传递参数的时候给参数加上%参数%即可

方案一

在sql中加通配符进行拼接

<select id="getUserLike" resultType="com.yunduo.pojo.User" parameterType="map">
    select * from user where pwd like '%'#{value}'%'
</select>
public void get(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDaoMapper user = sqlSession.getMapper(UserDaoMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("value","1");
    List<User> userLike = user.getUserLike(map);
    for (User user1 : userLike) {
        System.out.println(user1);
    }
}

方案二

在代码中给参数做通配符进行拼接

<select id="getUserLike" resultType="com.yunduo.pojo.User" parameterType="map">
    select * from user where pwd like #{value}
</select>
public void get(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserDaoMapper user = sqlSession.getMapper(UserDaoMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("value","%1%");
    List<User> userLike = user.getUserLike(map);
    for (User user1 : userLike) {
        System.out.println(user1);
    }
}

配置解析

之前的都只是入门,正真掌握的在下面

  • MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下

  • configuration(配置)
    properties(属性)
    settings(设置)
    typeAliases(类型别名)
    typeHandlers(类型处理器)
    objectFactory(对象工厂)
    plugins(插件)
    environments(环境配置)
    environment(环境变量)
    transactionManager(事务管理器)
    dataSource(数据源)
    databaseIdProvider(数据库厂商标识)
    mappers(映射器)
    

环境配置

  1. mybatis可以配置多套数据库

  2. 不过要记住,尽管可以配置多个环境,但每个SqlSessionFactory只能选择一套环境

  3. 要学会配置多套环境

  4. Mybatis默认的事务管理器是jdbc,默认的链接池是POOLED

属性(properties)

这些属性可以在外部进行配置,并可以进行动态替换。你既可以在典型的 Java 属性文件中配置这些属性,也可以在 properties 元素的子元素中设置。

编写一个外部的properties文件配置我们的数据库链接信息

db.properties

Driver=com.mysql.jdbc.Driver
Url=jdbc:mysql://localhost:3306/mybatis
UserName=root
PassWord=0000

在我们mybatis核心文件mybatis-config.xml中引入我们的db.properties

image-20220223200622076

image-20220223200944537

<?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="db.properties"/>

    <environments default="development">
        <environment id="development">
            <!--    //事务是采用jdbc-->
            <transactionManager type="JDBC"/>
            <!--    //数据源用的是POOLED-->
            <dataSource type="POOLED">
<!--                这里的${Driver}引入的是我们外部配置文件的key-->
                <property name="driver" value="${Driver}"/>
                <property name="url" value="${Url}"/>
                <property name="username" value="${UserName}"/>
                <property name="password" value="${PassWord}"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/yunduo/dao/UserDaoMapper.xml"/>
    </mappers>
</configuration>
  • 可以直接引入外部文件
  • 可以在其中增加一些属性配置
  • 如果两个文件有同一个字段,优先使用外部配置文件

类型别名(typeAliases)

  • 类型别名是为java类型对象设置一个短的名字

  • 存在的意义仅在于用来减少类完全限定名的冗余

        <typeAliases>
    <!--        这里默认别名到一个对象-->
            <typeAlias alias="user" type="com.yunduo.pojo.User"/>
        </typeAliases>
    

    也可以指定一个报名,Mybatis会在包名下面搜索需要的Java Bean,比如:

    扫码实体类的包,它的默认别名就为了这个类的类名,首字母小写!

        <typeAliases>
    <!--        这里别名到一个包,包下面的对象别名就是包名-->
            <package name="com.yunduo.pojo"/>
        </typeAliases>
    

    怎么选择那种呢

    如果实体类少,就使用第一种

    如果实体类十分多,建议使用第二种

    两个区别就是第一个可以自定义别名,第二个不能自定义别名只能用类的名称当别名

    如果你使用的是第二种包扫描,又想自定义别名,那么就可以使用注解来起别名

    image-20220223203148070

image-20220223203237876

设置

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为

开启缓存和懒加载

image-20220224085737170

日志

image-20220224085749028

其他配置

插件

  1. MyBatis Generator Core
  2. MyBatis Plus
  3. 通用Mapper

映射器

MapperRegistry:注册绑定我们的Mapper文件

方式一【推荐】

使用绝对定位定位到我们对应的映射器的xml文件

<mappers>
    <mapper resource="com/yunduo/dao/UserDaoMapper.xml"/>
</mappers>

方式二

使用class定位我们接口

  1. 接口和他的mapper配置文件必须同名
  2. 接口和他的mapper配置文件必须在同一个包下
<mappers>
    <mapper class="com.yunduo.dao.UserDaoMapper"/>
</mappers>

方式三

使用包扫描

  1. 接口和他的mapper配置文件必须同名
  2. 接口和他的mapper配置文件必须在同一个包下
<mappers>
    <package name="com.yunduo.dao"/>
</mappers>

生命周期

image-20220224093626594

生命周期,和作用域,是至关重要的,因为错误的使用会导致非常严重的并发问题

SqlSessionFactoryBuilder

  • 一旦创建了 SqlSessionFactory,就不再需要它了
  • 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)

SqlSessionFactory

  • 说白了就是数据库链接池

  • SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例

  • 因此SqlsessionFactory的最佳作用域是应用作用域

  • 最简单的就是使用单例模式或者静态单例模式

SqlSession

  • 链接到线程池的一个请求!

  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。

  • 用完之后赶紧关掉

image-20220224093749575

解决属性名和字段名不一致问题

数据库中的字段

image-20220224094429168

新建一个项目,测试实体类字段不一致的情况

image-20220224105159286

这里会发现,我们数据库中的字段和我们实体类里面的字段不同

就会出现问题,查询出来的不一样的字段为null

image-20220224105255591

解决方案

ResultMap

数据库 id name pwd
实体类 id name password
我们将pwd绑定到我们实体类中的password

<!--    这里的我们使用resultMap getuser是给这个select取名字 -->
    <select id="getUserList" resultMap="getuser" parameterType="user">
        select * from user
  </select>

<!--    这里使用resultMap标签,id对应select中的resultMap定义的名称,type表示我们需要解决字段不一致的实体类-->
    <resultMap id="getuser" type="user">
<!--        使用result标签,进行映射不一致的属性字段   column对应数据库中的字段,property对应我们修改对应实体类中的属性-->
        <result column="pwd" property="password"/>
    </resultMap>
  • resultmap元素是mybatis中最强大的一个元素
  • RestultMao的设计思想是,对应简单的语句根本不需要配置显式的结果映射,而对于复杂一点的语句只需要描述他们的关系就行了
  • resultmap最优秀的地方在于,虽然你对他已经相当了解,但是根本就不需要显示地用到他们

日志

日志工厂

如果一个数据库操作,出现了异常,我们需要排错。日志就是最好的助手!

以往我们一般输出东西我们都是System.out.println,Debug

现在:日志工厂来帮我们

image-20220224143506362

  • SLF4J
  • LOG4J【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING【掌握】
  • NO_LOGGING

在Mybatis中具体使用哪一个日志实现,在设置中设定!

标准日志

在mybatis核心配置文件中写入

    <settings>
<!--        标准的日志工厂-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

Log4J

什么是log4j

  • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件
  • 我们也可以控制每一条日志的输出格式
  • 通过定义每一条日志信息的级别
  • 可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。

使用

  1. 导入log4j包

    <!-- 注意:1.2.17下载报错,请使用1.2.12 -->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.12</version>
    </dependency>
    
  2. 在resources,下创建一个log4j.properties配置文件

    #将等级为DEBUG的日志信息输出到console和file这两个目的地,console和file的定义在下面的代码
    log4j.rootLogger=DEBUG,console,file
    
    #控制台输出的相关设置
    log4j.appender.console = org.apache.log4j.ConsoleAppender
    log4j.appender.console.Target = System.out
    log4j.appender.console.Threshold=DEBUG
    log4j.appender.console.layout = org.apache.log4j.PatternLayout
    log4j.appender.console.layout.ConversionPattern=【%c】-%m%n
    
    #文件输出的相关设置
    log4j.appender.file = org.apache.log4j.RollingFileAppender
    log4j.appender.file.File=./log/yunduo.log
    log4j.appender.file.MaxFileSize=10mb
    log4j.appender.file.Threshold=DEBUG
    log4j.appender.file.layout=org.apache.log4j.PatternLayout
    log4j.appender.file.layout.ConversionPattern=[%p][%d{yyy-MM-dd}][%c]%m%n
    
    #日志输出级别
    log4j.logger.org.mybatis=DEBUG
    log4j.logger.java.sql=DEBUG
    log4j.logger.java.sql.Statement=DEBUG
    log4j.logger.java.sql.ResultSet=DEBUG
    log4j.logger.java.sql.PreparedStatement=DEBUG
    

    log4j默认3种级别

    info,debug,error

logger.info("info:启动test1成功");
logger.debug("debug:启动test1成功");
logger.error("error:启动test1成功");

Mybatis怎么使用Log4j打印sql日志?

在mybatis核心配置文件种引入

<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>

image-20220224165002858

分页

为什么要分页?因为一次性查询很多,会很耗时,就使用分页

方法一:使用limit

方法二:使用代码形式进行分页

//查询所有用户
List<User> getUserList();
<select id="getUserList" resultTypr="user" parameterType="user">
      select * from user
</select>

测试

public void query(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //我们通过mybatis自带的插件RowBounds对象进行分页
    RowBounds rowBounds = new RowBounds(0,2);
    List<User> user = sqlSession.selectList("com.yunduo.dao.UserDaoMapper.getUserList", null, rowBounds);
    for (User user1 : user) {
        System.out.println(user1);
    }
}

使用注解开发

注解只能完成简单sql语句,复杂的建议使用xml形式

@Select("select * from user")
List<User> queryUserList();
<!--    映射器注册-->
    <mappers>
        <mapper class="dao.UserMapper05"/>
    </mappers>
public void test1(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper05 mapper = sqlSession.getMapper(UserMapper05.class);
    List<User> users = mapper.queryUserList();
    for (User user : users) {
        System.out.println(user);
    }
    sqlSession.close();
}

本质:用的反射机制实现

底层:动态代理。

image-20220224193531506

#号和$符号

#号是预编译是占位符,(prepared statement)编译好SQL语句再取值

$符号是取值后在编译,(createStatement)取值以后再去编译SQL语句

Mybatis执行流程

  1. Properties加载我们mybatis全局配置文件
  2. 创建我们SqlSessionFactoryBuilder对象
  3. SqlSessionFactoryBuilder的builder方法里面会解析mybatis全局配置文件
  4. 最后得到我们configuration所有的配置信息
  5. 接着就可以实例化我们的sqlsessionFactory对象
  6. sqlsessionfactory对象中里面有这个事务管理器,来控制我们sql的事务的
  7. 接着创建executor执行器,来执行我们crud的操作的,
  8. 然后检查crud是否执行成功,如果执行失败就回到我们事务管理器回滚,
  9. 如果执行成功,就提交事务
  10. 最后关闭

image-20220224195844821

image-20220224195930917

image-20220224195940236

复杂查询

多对一

image-20220224203220475

  • 多个学生对应一个老师
  • 对于学生这边而言,关联…多个学生,关联一个老师【多对一】
  • 对应老师这边而言,集合…一个老师有很多个学生【一对多】

image-20220224203935675

测试环境搭建

  1. 导入lombok
  2. 新建实体类Teacher,Student
  3. 建立对应的Mapper接口
  4. 建立Mapper.xml文件
  5. 在核心配置文件中注册我们mapper接口
  6. 测试查询环境搭建成功
按照查询嵌套处理

image-20220225103556786

image-20220225103609971

按照结果嵌套处理
    <resultMap id="getstudent" type="student">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
<!--        通过查询出来的结果去老师对象中映射-->
        <association property="teacher" javaType="teacher">
<!--            老师对象中的属性 对应 数据库中结果的字段-->
            <result property="name" column="teacher"/>
        </association>
    </resultMap>

    <select id="queryStudentList" resultMap="getstudent">
    select s.id id,s.name name,t.name teacher from student s,teacher t where s.tid=t.id
    </select>

回顾mysql多对一查询方式

  • 子查询 (select s.id,s.name,(select t.name from teacher t where s.tid=t.id) teacher from student s)
  • 联表查询 (select s.id id,s.name name,t.name teacher from student s,teacher t where s.tid=t.id)

一对多

根据结果嵌套处理

<!--按照结果嵌套查询-->
    <resultMap id="queryTeacher" 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="stid"/>
        </collection>
    </resultMap>

    <select id="queryTeacherList" resultMap="queryTeacher">
        select t.id tid,t.name tname,s.id sid,s.name sname,s.tid stid from teacher t,student s where s.tid=t.id and t.id=#{id}
    </select>

Teacher(id=1, name=秦老师, students=[Student(id=1, name=小明, tid=1), Student(id=2, name=小红, tid=1), Student(id=3, name=小张, tid=1), Student(id=4, name=小李, tid=1), Student(id=5, name=小王, tid=1)])

小结

  1. 关联-association【多对一】
  2. 集合-collection 【一对多】
  3. javaType & ofType
    1. javatype用来指定实体类中属性的类型
    2. oftypr用来指定映射到List或者集合中的pojo类型,泛型中的约束类型!

注意点:

  1. 保证SQL的可读性,尽量保证通俗易懂
  2. 注意一对多和多对一中,属性名和字段的问题!
  3. 如果问题不好排查错误,可以使用日志,建议使用Log4j

慢SQL 1s 1000s

面试高频

  • Mysql引擎
  • InnoDB底层原理
  • 索引
  • 索引优化

动态SQL

什么是动态SQL:动态SQL就是根据不同的条件生成不同的SQL语句

image-20220225150500957

IF

<select id="queryStudentList" resultType="Student" parameterType="map">
    select * from student
    <where>
        <if test="id!=null">
            id=#{id}
        </if>
        <if test="name!=null">
            and name=#{name}
        </if>
        <if test="tid!=null">
            and tid=#{tid}
        </if>
    </where>
</select>
SqlSession sqlSession = MybatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
HashMap<String, Object> map = new HashMap<String, Object>();
map.put("id",1);
map.put("name","小明");
map.put("tid",1);
List<Student> students = mapper.queryStudentList(map);
for (Student student : students) {
    System.out.println(student);
}
SELECT *
FROM student
WHERE id = 1
	AND name = '小明'
	AND tid = 1
==>com.yun.dao.StudentMapper.queryStudentList, cost=405ms
Student(id=1, name=小明, tid=1)

choose(when,outerwise)

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

<select id="queryStudentList2" resultType="Student" parameterType="map">
    select * from student
    <where>
        <choose>
            <!--这里条件匹配,从上往下,只能匹配一个 -->
            <when test="id!=null">
                id=#{id}
            </when>
            <when test="name!=null">
                and name=#{name}
            </when>
            <when test="tid!=null">
                and tid=#{tid}
            </when>
       <!-- 如果上面都不满足,就执行otherwise中的语句 -->
            <otherwise>
                and id=5
            </otherwise>
        </choose>
    </where>
</select>
    public void test2(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
        HashMap<String, Object> map = new HashMap<String, Object>();
//        map.put("id",1);
//        map.put("name","小明");
        List<Student> students = mapper.queryStudentList2(map);
        for (Student student : students) {
            System.out.println(student);
        }
    }

load mybatis-log agent success.
==>com.yun.dao.StudentMapper.queryStudentList2
SELECT *
FROM student
WHERE id = 5
==>com.yun.dao.StudentMapper.queryStudentList2, cost=294ms
Student(id=5, name=小王, tid=1)

Trim(where set)

前面几个例子已经方便地解决了一个臭名昭著的动态 SQL 问题。现在回到之前的 “if” 示例,这次我们将 “state = ‘ACTIVE’” 设置成动态条件,看看会发生什么。

select * from student
<where>
    <if test="id!=null">
        id=#{id}
    </if>
    <if test="name!=null">
        and name=#{name}
    </if>
    <if test="tid!=null">
        and tid=#{tid}
    </if>
</where>
@Test
public void test1(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("id",1);
    map.put("name","小明");
    map.put("tid",1);
    //这里我们就算什么都不传递,它会自动去掉where
    List<Student> students = mapper.queryStudentList(map);
    for (Student student : students) {
        System.out.println(student);
    }
}

load mybatis-log agent success.
==>com.yun.dao.StudentMapper.queryStudentList
SELECT *
FROM student
WHERE id = 1
	AND name = '小明'
	AND tid = 1
==>com.yun.dao.StudentMapper.queryStudentList, cost=310ms
Student(id=1, name=小明, tid=1)

Set

通常用于update语句

<update id="updateStudentByid" parameterType="map">
    update student
    <set>
        <if test="name!=null">
            name=#{name},
        </if>
        <if test="tid!=null">
            tid=#{tid},
        </if>
    </set>
    where id=#{id}
</update>
public void test3(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("id",1);
    map.put("name","星辰");
    mapper.updateStudentByid(map);
    //提交事务
    sqlSession.commit();
    sqlSession.close();
}

UPDATE student
SET name = '星辰'
WHERE id = 1

SQL片段

有的时候,我们可能会将一些功能的部分抽取出来,方便复用【工具类】

  1. 使用sql标签抽取出公共部分
  2. 在需要使用的地方,使用include标签引用即可
<!--    sql工具类-->
    <sql id="if-sql">
        <if test="id!=null">
            id=#{id}
        </if>
        <if test="name!=null">
            and name=#{name}
        </if>
        <if test="tid!=null">
            and tid=#{tid}
        </if>
    </sql>

<!-- 使用include标签引用-->
    <select id="queryStudentList" resultType="Student" parameterType="map">
        select * from student
        <where>
            <include refid="if-sql"></include>
        </where>
    </select>
public void test1(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("id",1);
    //这里我们就算什么都不传递,它会自动去掉where
    List<Student> students = mapper.queryStudentList(map);
    for (Student student : students) {
        System.out.println(student);
    }
}

SELECT *
FROM student
WHERE id = 1
==>com.yun.dao.StudentMapper.queryStudentList, cost=364ms
Student(id=1, name=星辰, tid=1)

注意:

  1. 最好基于单张表查询
  2. 不要存在where标签

Foreach

需求,我想查出id为1,5,9,11的用户信息

select * from student where id in(1,5,9,11)

但是这样参数是固定的,我们需要传递一个可变长的list参数。

<!--    select * from student where id in(1,2,5)-->
    <select id="querystudent" parameterType="list" resultType="student">
        select * from student
        <where>
            <foreach collection="list" item="id"
                     open="id in("  separator="," close=")">
                #{id}
            </foreach>
        </where>
    </select>
public void test4(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
    ArrayList<Integer> arrayList = new ArrayList<Integer>();
    arrayList.add(1);
    arrayList.add(5);
    List<Student> querystudent = mapper.querystudent(arrayList);
    for (Student student : querystudent) {
        System.out.println(student);
    }
}

SELECT *
FROM student
WHERE id IN (1, 5)
==>com.yun.dao.StudentMapper.querystudent, cost=255ms
Student(id=1, name=星辰, tid=1)
Student(id=5, name=小王, tid=1)

缓存

查询:链接数据库,耗资源

​ 一次查询的结果,给他暂存在一个可以直接取到的地方!---->内存:缓存

我们再次查询相同数据的时候,直接走缓存,就不用走数据库了

  1. 什么是缓存?

    • 是存放在内存中的临时数据
    • 将用户经常查询的数据放在缓存(内存)中,用户去查询数据就不用从磁盘上(数据库)查询,而是直接从缓存中查询,从而提高查询效率,解决了高并发系统问题。
  2. 为什么使用缓存?

    • 减少和数据库交互的次数,减少系统的开箱,提高系统效率
  3. 什么样的数据能使用缓存

    • 经常查询并且不改变的数据【可以使用缓存】
    • 增删改是不能用缓存的【不难使用缓存】

怎么使用缓存

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

一级缓存

仅在一个sqlsession中查询相同的才有效

  1. 开启日志
  2. 测试链接一个Sqlsession查询两次相同记录
  3. 查看日志输出

可以发现,myabtis中只执行了一次sql,那么第二次就是从缓存中拿取的

image-20220227153217271

SQL

image-20220227153023234

当我们查询不一样的数据的时候,会查询出两条sql

image-20220227153612905

缓存失效的情况

如果当前sqlsession中有查询又有增删改,那么就会出现缓存失效情况

image-20220227154542234

image-20220227154535672

  1. 查询出不同的东西

  2. 增删改可能会对我们查询的数据做出更改,所以会清除当前缓存

  3. 查询不同Mapper

  4. 手动清除缓存

    sqlSession.clearCache();
    
小结

Mybatis一级缓存是默认开启的,只在一次sqlsession中开启和关闭之间是有效的。

一级缓存就是一个map,第一次获取时候put进去,后续在取得时候直接get

image-20220227160009103

二级缓存

生效条件,当前sqlsession死亡后,才会将当前得数据保存进二级缓存中。

  • 二级缓存也叫全局缓存,一级缓存作用域太低,所以诞生了二级缓存
  • 基于namespace级别得缓存,一个名称空间,对应一个二级缓存;
  • 工作机制
    • 一个会话查询一条语句,这个数据就会被放在当前会话得一级缓存中;
    • 如果当前会话关闭了,这个会话对应得一级缓存就没了;但是我们想要得是,会话关闭了,一级缓存中得数据被保存到二级缓存中;
      • 新的会话查询信息,就可以从二级缓存中获取内容;
      • 不同得mapper查出得数据会被放在对应得缓存(map)中

使用

  1. 在mybatis配置中心开启

    <!--        开启缓存-->
            <setting name="cacheEnabled" value="true"/>
    
  2. 在需要使用二级缓存得Mapper中添加开启

    <!--方法一:-->
    <cache/>
    
    <!--方法二,自定义参数-->
    <!--    开启缓存 eviction清除策略,flushInterval刷新时间,size缓存大小,readOnly是否只读-->
        <cache
                eviction="FIFO"
                flushInterval="60000"
                size="512"
                readOnly="true"/>
        />
    

问题:

使用缓存,一定要得我们pojo进行序列化,实现Serializable接口

测试

mapper

<!--    开启缓存 eviction清除策略,flushInterval刷新时间,size缓存大小,readOnly是否只读-->
    <cache/>

    <resultMap id="queryUserListmap" type="com.yunduo.pojo.User">
        <result property="password" column="pwd"/>
    </resultMap>

    <select id="queryUserList" resultMap="queryUserListmap" parameterType="com.yunduo.pojo.User">
        select * from user where id=#{id}
    </select>

pojo

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
    private int id;
    private String name;
    private String password;
}

test

@Test
public void test1(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.queryUserList(1);
    System.out.println(user);
    sqlSession.close();

    SqlSession sqlSession1 = MybatisUtils.getSqlSession();
    UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
    User user1 = mapper1.queryUserList(1);
    System.out.println(user1);
    sqlSession.close();
    System.out.println(user==user1);
}

结果

Cache Hit Ratio [com.yunduo.dao.UserMapper]: 0.0
Opening JDBC Connection
Created connection 127702987.
==>  Preparing: select * from user where id=? 
==> Parameters: 1(Integer)
<==    Columns: id, name, pwd
<==        Row: 1, 星辰, 123456
<==      Total: 1
==>com.yunduo.dao.UserMapper.queryUserList
SELECT *
FROM user
WHERE id = 1
==>com.yunduo.dao.UserMapper.queryUserList, cost=316ms
User(id=1, name=星辰, password=123456)
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@79c97cb]
Returned connection 127702987 to pool.
Cache Hit Ratio [com.yunduo.dao.UserMapper]: 0.5
User(id=1, name=星辰, password=123456)
false
小结
  • 只要开启了二级缓存,在同一个Mapper下才有效
  • 所有得数据都会先放在一级缓存中;
  • 只有当会话提交,或者关闭得时候,才会提交到二级缓存中!

缓存原理

  1. 先查询二级缓存有没有
  2. 在查询一级缓存有没有
  3. 最后查询数据库

image-20220227163544770

自定义缓存

Ehcache是一种广泛用于Java的分布式缓存,主要面向通用缓存

使用

  1. 导包

    <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
    <dependency>
        <groupId>org.mybatis.caches</groupId>
        <artifactId>mybatis-ehcache</artifactId>
        <version>1.1.0</version>
    </dependency>
    
  2. 修改mapper中cache中的type

    <cache type="org.mybatis.caches.ehcache"/>
  1. 添加配置文件
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--
       diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
       user.home – 用户主目录
       user.dir  – 用户当前工作目录
       java.io.tmpdir – 默认临时文件路径
     -->
    <diskStore path="java.io.tmpdir/Tmp_EhCache"/>
    <!--
       defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
     -->
    <!--
      name:缓存名称。
      maxElementsInMemory:缓存最大数目
      maxElementsOnDisk:硬盘最大缓存个数。
      eternal:对象是否永久有效,一但设置了,timeout将不起作用。
      overflowToDisk:是否保存到磁盘,当系统当机时
      timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
      timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
      diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
      diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
      diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
      memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
      clearOnFlush:内存数量最大时是否清除。
      memoryStoreEvictionPolicy:可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
      FIFO,first in first out,这个是大家最熟的,先进先出。
      LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
      LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
   -->
    <defaultCache
            eternal="false"
            maxElementsInMemory="10000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="259200"
            memoryStoreEvictionPolicy="LRU"/>

    <cache
            name="cloud_user"
            eternal="false"
            maxElementsInMemory="5000"
            overflowToDisk="false"
            diskPersistent="false"
            timeToIdleSeconds="1800"
            timeToLiveSeconds="1800"
            memoryStoreEvictionPolicy="LRU"/>

</ehcache>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射,MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作 的相关文章

  • 如何检测线程是否被IO阻塞?

    在Java中 线程可以有不同的状态 新的 可运行的 阻塞的 等待的 TIMED WAITING 的 终止的 然而 当线程被IO阻塞时 其状态为 RUNNABLE 如何判断是否被IO阻塞 NEW 线程已创建但尚未处理 可运行 线程正在占用CP
  • 在 SQL Server 中执行嵌套 case 语句逻辑的最佳方法

    我正在编写一个 SQL 查询 其中返回的一些列需要根据很多条件进行计算 我目前正在使用嵌套的 case 语句 但它变得混乱 有更好的 更有组织性和 或可读性 方法吗 我使用的是 Microsoft SQL Server 2005 一个简化的
  • 如何使用 log4j 自动记录类中调用的每个方法

    我有一个包含数据库调用的类 我通常希望使用 log4j 记录该类中调用的每个方法 带参数 logger debug foo id id initiated 可以自动执行此操作吗 也许通过在每个方法的开头使用某种注释而不是编写每个 logge
  • 使用 ScheduledExecutorService 安排每月任务

    我想在该月的某一天的特定时间安排一项任务 每次运行之间的间隔可以设置在 1 到 12 个月之间 在java中 可以使用ScheduledExecutorService以固定的时间间隔调度任务 既然一个月的天数不固定 那么如何实现呢 提前致谢
  • 在 Jenkins 内运行构建时,我收到“java/lang/OutOfMemoryError”

    2020 02 25 10 11 24 986 0000 id 79 信息hudson model AsyncPeriodicWork lambda doRun 0 开始maven repo cleanup 2020 02 25 10 11
  • JTable AutoCreateRowSorter 将数字排序为字符串

    我有一个 JTable JTable table new JTable String colNames c1 DefaultTableModel model new DefaultTableModel Integer x new Integ
  • C# 中的 Culture 相当于 Java 中的 Locale 吗?

    C 使用文化的概念 这在操作上与 Java 中的 Locale 类似吗 或者底层概念是否存在显着差异 从文化而不是语言环境的角度进行工作是一种寻找正确抽象层次的尝试 从以类似方式做事的人群的角度来考虑事物 而不是谈论地理区域和语言 并有点疯
  • x.person 上的 @OneToOne 或 @ManyToOne 引用未知实体:y.Person - 继承问题

    我的 Hibernate 架构有问题 我有一个 MappedSuperClass 人员 一名员工和一名客户 gt Person class MappedSuperclass Audited public class Person exten
  • 实体框架与oracle数据库的连接

    我使用的是 Entity Framework 6 1 版本和 oracle 11 我是实体框架的新手 任何人都可以建议连接 oracle 的先决条件是什么 任何更改都需要在 web config 中进行 在web config中 默认它是与
  • WSDL 表示中的枚举类型

    WSDL 表示如下
  • 从 Java 调用 Python 代码时出现问题(不使用 jython)

    我发现这是从 java 运行 使用 exec 方法 python 脚本的方法之一 我在 python 文件中有一个简单的打印语句 但是 我的程序在运行时什么也没做 它既不打印Python文件中编写的语句 也不抛出异常 程序什么都不做就终止了
  • 如何在 Eclipse 中使用 Hibernate Tools 生成 DAO?

    我在用着 Eclipse Java EE IDE Web 开发人员 版本 Indigo 发布 使用 hibernate 工具 我对 Eclipse 中的 hibernate 很陌生 所以我学习如何配置 hibernate 并使用注释生成 P
  • JFreeChart MeterPlot

    我目前正在用java做Agent项目 在某些时候 我需要显示一个仪表 例如 电池电量 我的程序中有 5 个代理 每个代理都会创建自己的带有名称的仪表图 但不知何故他们没有更新数据集 或者他们正在更新数据集 只是它没有显示在仪表图上 任何想法
  • 使用 Java 进行 AES 加密并使用 Javascript 进行解密

    我正在制作一个需要基于 Java 的 AES 加密和基于 JavaScript 的解密的应用程序 我使用以下代码作为基本形式进行加密 public class AESencrp private static final String ALG
  • 存储过程和视图有什么区别?

    我对以下几点感到困惑 存储过程和视图有什么区别 在 SQL Server 中 什么时候应该使用存储过程 什么时候应该使用视图 视图是否允许创建可以传递参数的动态查询 哪一个最快 基于什么原因其中一个比另一个更快 视图或存储过程是否永久分配内
  • 仅在java中使用数组计算50的阶乘

    我是java的初学者 我有一个作业要编写一个完整的程序 使用数组计算 50 的阶乘 我无法使用像 biginteger 这样的任何方法 我只能使用数组 因为我的教授希望我们理解背后的逻辑 我猜 然而 他并没有真正教我们数组的细节 所以我在这
  • 用于将字符串与通配符模式进行匹配的递归函数

    所以我一整天都在试图解决这个作业 只是无法完成 以下函数接受 2 个字符串 第二个 不是第一个 可能包含 的 星号 An 是字符串的替换 空 1个字符或更多 它可以出现 仅在s2中 一次 两次 更多或根本不出现 它不能与另一个相邻 ab c
  • SAXParseException:找不到元素“定义”的声明

    我对 camunda 和 DMN 完全陌生 我试图在 spring boot 中运行 DMN 示例 链接在这里 https github com camunda camunda bpm examples tree master dmn en
  • 当我必须在 Netty4 编码器中调用 ByteBuf.retain() 时?

    我正在编写一个以 NUL 终止 JSON 消息的编码器 以便在消息碎片的情况下可以对其进行解码 我找到了这个样本 gt click https github com netty netty blob master codec src mai
  • 1° 夏令时 Java 和 JS 表现出不同的行为

    假设巴西利亚 GMT 0300 夏令时于 21 10 2012 00 00 00 此时时钟应提前一小时 Java new Date 2012 1900 9 21 0 0 0 Sun Oct 21 01 00 00 BRST 2012 Chr

随机推荐

  • 解决Uncaught SyntaxError: Unexpected reserved word

    解决思路 首先 我运行项目报错 我查看了一下node版本 是否太低 如果是14版本的话 那么node需要升级 目前 node已经升级到19 升级到16即可 无需太高 更新完node版本之后 发现它还是报错 然后接着从网上搜报错 经历无数次的
  • 【I2C】Linux使用GPIO模拟I2C

    文章目录 1 I2C GPIO系统架构简介 2 如何使能I2C GPIO驱动 2 1 config配置 2 2 dts配置 2 3 测试验证 3 简单分析i2c gpio c驱动 3 1 解析设备树 3 2 配置SDA和SCL 3 3 注册
  • vue进度条

  • 金九银十之面试闲谈

    文章目录 前言 面试流程 资料总结 刷题指南 个人经验总结 寄语 前言 今年的金九银十带着几分不确定性来了 加上各个大厂hc的收紧 今年的金九银十很难恢复往日的 荣光 不过肯定还是有很多毕业生或者其他原因的朋友们出来找工作 面试流程 面试流
  • Sharding-JDBC分布式事务总结(四)之BASE事务(Seat框架中——AT模式的介绍以及理解)

    Sharding分布式事务之BASE事务 Seat框架中 AT模式 1 什么是BASE事务 2 Seata框架的AT模式 2 1介绍 2 2原理 2 3特性 写隔离与读隔离 AT模式的 写隔离 读隔离 2 4优势 相较于XA事务 2 5启动
  • Windows设置本地DNS域名解析Hosts文件的方法

    我们需要先了解DNS解析查询的顺序 在用户输入域名之后 DNS解析查询的顺序是下面这样的 1 浏览器会首先查看自身的缓存 如果浏览器缓存中有对应的解析记录 直接返回结果 2 如果浏览器没有缓存 电脑会查看本地操作系统的缓存 如果有记录 直接
  • 2021我们相约一起用.NET改变Windows软件世界

    目录 成为C 版主 互联网启示录 改变 NET桌面应用 从替换Application Run开始 现在 让我们开始吧 令人惊讶的FirstApp exe 新起点从第一个Web页面开始 成为C 版主 不管最终是出于什么原因 我成为了C 论坛版
  • Basic Level 1034 有理数四则运算 (20分)

    题目 本题要求编写程序 计算 2 个有理数的和 差 积 商 输入格式 输入在一行中按照 a1 b1 a2 b2 的格式给出两个分数形式的有理数 其中分子和分母全是整型范围内的整数 负号只可能出现在分子前 分母不为 0 输出格式 分别在 4
  • MEM工程管理硕士的含金量与就业前景?

    MEM工程管理硕士的含金量与就业前景 修改 13年7月毕业 工作半年 想知道MEM现在的含金量怎么样 比起普通硕士而言呢 毕业前景如何 社会认可度高不高 我现在就比较想考这个 想在多学习学习 还有没有其他较好的选择 修改 举报 1 条评论
  • [4G&5G专题-123]:5G培训部署篇-1-5G网络架构与关键技术

    作者主页 https blog csdn net HiWangWenBing 文章出处 https blog csdn net HiWangWenBing article details 118437789 目录 第1部分 5G概述 第2部
  • HDMI之EDID使用说明

    Q1 为什么要写这篇文章 A1 在最近的工作中遇到了不少问题 其中很多都是和EDID相关的 可以说 作为一家以 显示 为生的企业 我们时时刻刻在与EDID打交道 EDID这东西很简单 但是如果不了解其基本原理和概念的话 会给我们的工作带来不
  • 服务器内存占用率76%,IT运维常见问题之一:服务器内存占有率高

    登录服务器一看 服务器也很卡 打开任务管理器 一看内存占有率99 了 在仔细一查看是 数据库占用了大量内存 打开数据库一看是部署的时候没有对数据库实例设置 最大服务器内存 下面就分享一下SQL Server数据库占用过高内存的处理方法 一
  • extern详解

    extern 关键字 extern是C语言中的一个关键字 一般用在变量名前或函数名前 作用是用来说明 此变量 函数是在别处定义的 要在此处引用 extern这个关键字大部分读者应该是在变量的存储类型这一类的内容中 遇到的 下面先分析C语言不
  • java获取接口的流_Java请求Http协议接口,流式请求,流式接收

    package com test gov supervision processor gd gz import com alibaba fastjson JSON import com alibaba fastjson JSONArray
  • 2022年 hust OJ 最新搭建方式

    文章目录 一 准备环境 二 hust oj 搭建 三 踩坑 一 准备环境 1核2G 服务器一台 腾讯云 阿里云均可 现在可能买不到了 2核2G 的也可以 xshell windterm 连接服务器的工具 二 hust oj 搭建 HustO
  • 【Python百日基础系列】Day77 - Pandas可视化Cufflinks图表库(一)

    文章目录 一 Cufflinks入门 1 1 Cufflinks简介 1 2 前置安装plotly 1 3 Cufflinks安装 1 4 Pycharm解决AttributeError 1 4 1 错误现象 1 4 2 解决方法 1 4
  • 深入理解计算机系统 --- 链接

    本章目的 提供了关于链接各方面的全面讨论 从传统静态链接到加载时的共享库的动态链接 以及到运行时的共享库的动态链接 链接 linking 是将各种代码和数据片段收集并组合成一个单一文件的过程 这个文件可被加载 复制 到内存被并执行 链接可以
  • QML String和数字互相转换

    String 转换成数字 QML代码中 如果遇到字符串转数字 可以使用Number str 将str转换成数字类型 import QtQuick 2 12 import QtQuick Window 2 12 Window visible
  • JavaScript、图片防盗链

    文章目录 防盗链方式 HTTP Referer 检查 CORS 跨域资源共享 Token 验证 服务器端限制 生成临时链接 水印 防盗链方式 HTTP Referer 检查 HTTP Referer 是一个 HTTP 头部字段 它包含了请求
  • MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射,MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作

    Mybatis 什么是Mybatis MyBatis 是一款优秀的持久层框架 它支持自定义 SQL 存储过程以及高级映射 MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作 MyBatis 可以通过简单的 XML