【JAVAEE基础学习(11)】--简述MyBatis(2)-小节篇

一、总述:

单独使用mybatis框架对数据库进行增删改查整体思路如下:
         导入相关jar包: 

hamcrest-core-1.3.jar
junit-4.12.jar
log4j-1.2.17.jar
mybatis-3.5.6.jar
mysql-connector-java-5.1.37-bin.jar

        在src下编写相应的类包以及基本配置文件{主要有mybatis配置文件,日志配置文件,数据库连接配置文件},主要有mapper包{主要用于存放mapper接口和对应的xml文件}、pojo(或entity)包{主要用于存放表格映射对象,即表格实体类}和test包{主要用于存放测试类}

二、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>
    主要配置信息
</configuration>

加载数据库配置信息:在加载配置信息部分存在两种配置方式。

        第一种配置方式:在配置文件中进行配置

    <!--
          properties用于配置键值对
            resource属性指定加载的属性配置文件路径
    -->
    <properties>
        <property name="url" value="jdbc:mysql://ip:端口/数据库"/>
        <property name="username" value="账户"/>
        <property name="password" value="密码"/>
        <property name="driver" value="com.mysql.jdbc.Driver"/>
    </properties>

        第二中配置方式:引入外部配置文件

jdbc.properties配置文件{

url=jdbc:mysql://ip:端口/对应数据库?useSSL=false
username=root
password=
driver=com.mysql.jdbc.Driver
 
======================================================================
url=jdbc:mysql://ip:端口/对应数据库
username=root
password=root
driver=com.mysql.jdbc.Driver

}

===========================分割线======================================

在mybatis配置文件中引入jdbc.properties配置文件{

    <!--
          properties用于配置键值对
            resource属性指定加载的属性配置文件路径
    -->
    <properties resource="jdbc.properties">
    </properties>

}

配置延迟加载:延迟加载在一定程度上可以减少不必要的查询,给数据库服务器提升性能上的优化

    <settings>
        <!--        打开延迟加载的开关-->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!--        将积极加载改为消极加载 按需加载-->
        <setting name="aggressiveLazyLoading" value="false"/>
    </settings>

定义多个别名:采用typeAliases标签定义多个别名

<typeAliases>
    <!--
            type属性是全类名
            alias属性指定别名
            <typeAlias type="com.lixxkv.mybatis.hello.pojo.UserOne" alias="userOne" />
    -->
    <!--
        指定包名,mybatis会自动扫描这个包下所有类,自动映射别名
        别名就是它的类名或类名首字母小写:
            com.lixxkv.mybatis.hello.pojo.UserOne  ====>>> User 或 userOne
    -->
    <package name="com/lixxkv/mybatis/hello/pojo"/>
    <package name="com/lixxkv/mybatis/hello/domain"/>
</typeAliases>

配置连接数据库的信息

<!--
    environments环境其实就是连接数据库的信息
    default指定要连接哪一个数据库
-->
<environments default="development">
    <!--
            指定连接数据库的环境,其中用id标志使用的环境
                id="development"开发环境
                id="test"测试环境
            environment是一个数据库连接的环境
                id是唯一标识
    -->
    <environment id="development">

        <!--
                transactionManager标识事务管理器
                    type属性指定使用那个事务管理器
                        JDBC:       有事务管理,有提交和有回滚
                        MANAGED:     事务交给容器管理没有提交没有管理
        -->

        <transactionManager type="JDBC"/>

        <!--
                dataSource type="POOLED"表示使用数据库连接池
                    type属性有两个值:
                        UNPOOLED:不适用数据库连接池技术,名称使用connection连接对象需要手动创建,用完即关
                        POOLED:使用数据库连接池
        -->

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


    <!--<environment id="test">-->
    <!--    <transactionManager type="JDBC"/>-->
                <!--dataSource type="POOLED"表示使用数据库连接池子-->
    <!--        <dataSource type="POOLED">-->
    <!--        <property name="driver" value="com.mysql.jdbc.Driver"/>-->
    <!--        <property name="url" value="jdbc:mysql://ip:port/test?useSSL=false"/>-->
    <!--        <property name="username" value="root"/>-->
    <!--        <property name="password" value="root"/>-->
    <!--    </dataSource>-->
    <!--</environment>-->
</environments>

<databaseIdProvider type="DB_VENDOR">
  <!--
    name是数据库的名称
    value是表示数据库的id值,这个id值可以写在databaseid属性中
  -->
    <property name="MySQL" value="mysql"/>
    <property name="Oracle" value="oracle"/>
    <property name="DB2" value="db2"/>
    <property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>

注册mapper文件信息:

<!--
    mybatis是把sql语句放到配置文件中,而不写到java类中
        设置Mapper映射文件(sql映射文件)
-->
<mappers>
    <!--    1、从类路径下加载mapper.xml配置文件-->
    <!--    <mapper resource="com/lixxkv/mybatis/hello/mapper/UserMapperFour.xml"/>-->

    <!--
            2.表示按接口加载sql的配置文件
                注意事项:
                    1.mapper接口和mapper.xml配置文件必须在同一个包下
                    2.mapper接口的类名必须和mapper.xml文件名称一致
    -->
    <!--        <mapper class="com.lixxkv.mybatis.hello.mapper.UserMapperOne"/>-->

    <!--        3.mybatis会自动根据指定的包名,加载所有的sql配置文件-->
    <package name="com.lixxkv.mapper"/>
</mappers>

在已经注册mapper文件的情况下如果运行时仍然找不到对应的mapper文件而出现报错,那么要在pom.xml中添加以下代码:

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

三、日志文件详解:

使用logforjava--log4j日志框架可以较为清楚地看见整个mybatis框架对后端查询的过程,因此可以将log4j日志框架引入项目中,首先引入log4j相关jar包,其次和数据库配置文件一样进行log4j配置文件(log4j.properties)的配置,其配置内容如下

#全局日志配置
log4j.rootLogger=DEBUG,stdout

#控制台输出
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] -%m%n

四、对JavaBean的详解

在此部分,按照数据库表中字段,建立相应的属性,最后一张表就是一个JavaBean。在涉及一对一、多对多表关系时,按照关系在属性部分添加关系表的集合属性即可(如下)

//一对一情况下
//在班级表javabean中添加学生集合属性:
private List<Student> studentList;
//正常此属性的get/set方法

============================分割线===========================
//多对多情况下
//多对多的关系映射:一个用户可以具备多个角色,则在用户表中添加角色集合属性
private List<Role> roles;

//多对多关系映射,一个角色可以赋给过个用户。则在角色表中添加用户集合属性
private List<UserFive> userFives;

//剩下的get/set属性该怎么写就怎写

五、详解mybatis框架中具体实现对持久层进行操作的接口和接口对应的配置.xml文件

1.详解.xml文件
声明./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">

2.配置映射名称空间

<!--
    mapper就是映射,映射mapper接口的方法和sql语句的对应关系
        namespace(名称空间/命名空间)属性:设置为Mapper接口的全类名
-->
<mapper namespace="从src开始到指定的接口文件的全路径,其中以“.”做分隔符">
	具体的查询方法声明及sql语句的定义
</mapper>

3.具体的查询方法声明及sql语句的定义
    1)用于查询的声明
    不带参数的查询

<select //标签表示配置select查询语句
	id="对应mapper接口中对应方法名称" 
	parameterType="参数类型(可选设置,若是javaBean,则意见设置,是int类型即可省略)"
	resultType="设置为方法的返回值的类型的全类名,指的是查询回来的每一行记录转换为具体的javabean类型"
	databaseId="指定的sql是哪个厂场的,是mysql就只填写mysql(默认是mysql),是oracle就填oracle"
>
    具体的sql查询语句,不带任何参数


    select `id`,`username`,`birthday`,`sex`,`address` from user
</select>

==========================分割线===================================
查询结果是map的查询

mapper中{
    @MapKey("id")//可以把返回结果转换为map对象,并指定一个属性作为key(唯一)
    public Map<Integer, UserOne> queryUsersForMap();
}
mapper对应的xml中{
    <!--
	    @MapKey("id"):可以把返回结果转换为map对象,并指定一个属性作为key(唯一)
	    public Map<Integer,User> queryUsersForMap();
	-->
    <select id="queryUsersForMap" resultType="userOne">
        select `id`,`username`,`birthday`,`sex`,`address` from user
    </select>
}

   带普通参数的查询

javaBean对象属性与表中字段不一致查询{
	在mappper接口中的方法:public List<User> queryUsers();
	在对应的.xml中{
	<!--
	    public List<User> queryUsers();
	    <resultMap>的作用(手动将数据库中字段名称与javaBean属性向对应起来):
		    它可以把查询的结果集映射成为需要的数据返回。
		    使用resultMap标签之前使用的是resultType属性,指定返回值的类型,此时mybatis的底层做的是自动映射
		    id属性表示一个唯一的标识
		    type属性指定和那个类做映射
		一般来说,resultMap应用与复杂的javaBean对象上手动映射会取得较好效果,简单的javaBean对象属性均是普通类型
		复杂的javaBean对象,其属性还会包含其他javaBean对象,或者集合类型中每一个元素又是另外一个javaBean对象,以上两种情况都叫复杂javaBean对象
	-->
	    <resultMap id="resultMap1" type="全类名javabean">
	<!--
	            id标签专门用于给主键列做映射关系
	            result标签用于非主键列做映射关系
	                column指定列名称
	                property指定属性名称
	-->
	        <id column="id" property="id"/>
	        <result column="username" property="username"/>
	        <result column="字段1" property="属性1"/>
	        <result column="字段2" property="属性2"/>
	

	    </resultMap>
	    <select id="queryUsers" resultMap="resultMap1">
	        select 具体字段 from user
	    </select>

	}
}
单个参数{
	在mapper接口中的查询方法:public Xxx queryXxxById(Integer id);
	对应.xml中的内容为{
		//当接口参数类型为一个普通类型的时候则#{}中可以写任意内容,一般推荐写上参数名称,提高可读性
		<select id="queryXxxById" resultType="src下全路径Xxx">
	        select 具体字段 from user where id=#{id}
	    </select>
	}
}
多个参数{
	在mapper接口中的查询方法:
		public List<Xxx> queryXxxByNameOrId(String name,Integer id);
		或
		public List<Xxx> queryXxxByNameOrId(@Param("id") Integer id, @Param("username") String username);
	对应.xml中的内容为{

		/*
			当参数位多个普通数据类型的时可以传递参数如下
				param1  表示第一个参数
        		param2  表示第二个参数
        		...,以此类推
		*/

		<select id="queryUserByNameOrId" resultType="com.lixxkv.pojo.UserThree">
			select 具体字段 from user where id=#{param1} or username=#{param2}   
			或
			select 具体字段 from user where id=#{id} or username=#{username}
		</select>
	}
}
当map作为方法参数时{
	/*
        public List<User> queryUsersByMap(Map<String ,Object> paramMap);
        若参数的类型是一个map只需要让map中的key和#{}占位符中的参数名称一致即可
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("id",46);=====》》》》#{id}
            paramMap.put("username","老王");======》》》》#{username}
            for(User userOne : mapper.queryUsersByMap(paramMap)){
                System.out.println(userOne);
            }
	*/

    <select id="queryUsersByMap" resultType="com.lixxkv.pojo.User">
        select 具体字段 from user where id=#{id} or username=#{username}
    </select>
}
关于模糊查询{
	/*
	    public List<User> queryUsersLikeName(String name);
	    Select * FROM userOne WHERE username LIKE li%
	        #{} 是占位符 ?
	            若只有一个指定参数,可以随意填写
	        ${} 把指定参数输出。然后和sql中的内容做字符串拼接操作
	            可以写value
	            也可以写@Param注解来给参数命名使用
	            ${}一般用于替换表
	*/

	在mapper接口中方法:public List<User> queryUsersLikeName(@Param("name") String name);

	对应的.xml中{
		<select id="queryUsersLikeName" resultType="com.lixxkv.pojo.UserTwo">
	        select 具体字段 from userOne  where username like '${name}%'
	    </select>

		<!--也可以使用concat函数实现模糊插查询-->
	    <select id="queryUsersLikeName" resultType="com.lixxkv.pojo.UserThree">
	        select 具体字段 from user where username like CONCAT(#{name},'%')
	    </select>
	}
}

   条件查询

在相应的mapper接口中的方法:public List<User> queryUserBySample(User user);
在相应的.xml中{
	<!--
	    定义一个sql片段
	        id是唯一标识
	-->
	    <sql id="sqlOne">
	        `id`,`username`,`birthday`,`sex`,`address`
	    </sql>

	<!--    mapper中的方法接口实现通过用户的年龄或性别进行查询-->
	    <select id="queryUserBySample" parameterType="全路径.User" resultType="全路径.User">
	        select
	            <!--include是引入,refid是标识引入那个SQL判断-->
	            <include refid="sqlOne" />
	        from
	        user
	        <!--
	              trim可以动态地在包含的语句前面和后面进行指定内容的添加和删除
					prefix 前面添加内容
					prefixOverrides 删去前面内容
					suffix 后面添加内容
					suffixOverrides 删去后面内容
	        -->
	        <trim suffixOverrides="OR" prefix="WHERE">
	            <!--
	                    if标签做if判断;if语句可以动态根据你的值决定是否想要动态修改查询条件,最终产生不同的sql语句
	            -->
	            <if test="username!=null">
	                username like concat(#{username},'%')
	                OR
	            </if>

	            age=#{age}
	        </trim>
	    </select>

	==========================分割线======================================

	<!--
	    在username,age中选择有效的进行执行,若都无效,则可以自定义查询条件
	    选择查询:choose(when,otherwise)语句:
				类似switch case语句
	-->
	    <select id="queryUserFourBySample" parameterType="全路径.User" resultType="全路径.User">
	        select
	            `id`,`username`,`birthday`,`sex`,`address`
	        from
	            user
	            <where>
	                <choose>
	                    <when test="username!=null">
	                        username like concat(#{username},'%')
	                    </when>

	                    <otherwise>
	                        age=#{age}
	                        OR
	                        id=50
	                    </otherwise>
	                </choose>
	            </where>
	    </select>

    ==========================分割线======================================

<!--
    public List<UserFour> updateUserFouByIds(List<Integer> ids);
    循环查询:
-->
    <select id="updateUserFouByIds" resultType="com.lixxkv.pojo.UserFour">
        select
            <include refid="sqlOne" />
        from
            user
        where
            id in
<!--
        foreach标签用于遍历操作
            collection是遍历的数据源(参数名),默认参数名称:list
            item当前遍历到的的数据
            separator元素间的分隔符
            open="(" 表示遍历开始的内容
            close=")"表示变量结束的内容
-->
            <foreach collection="ids" item="id" separator="," open="(" close=")">
                #{id}
            </foreach>
    </select>
}

   带缓存的查询

缓存:
    把经常访问的数据保存到一个高速的缓冲区,方便后面访问的时候可以快速的获取到这些数据

缓存只有一个目标,就是为了提高访问速度

一级缓存:
    同一个SqlSession对象

二级缓存:
    同一个SqlSessionFactory对象

缓存的使用顺序说明
    1.当mybatis执行一个查询的时候,会前去二级缓存中查询数据,有数据则直接返回
    2.若二级缓存中无数据,再到一级缓存中取数据,有数据则直接返回
    3.若一、二级缓存中无数据,则发送sql语句到数据库中查询
    4.查到结果后会马上方到一级缓存中
    5.当SqlSession关闭时,会把一级缓存中的数据同步到二级缓存中

mybatis在执行查询的时候会做以下的操作
    1.mapper.queryUserById(50)会先从sqlSession缓存中查看是否有缓存数据
    2.若sqlSession缓存中没有缓存数据,mapper.queryUserById(50)则发SQL语句到数据库进行查询
    3.mapper.queryUserById(50)查完之后,将查询结果保存到sqlSession缓存中
一级缓存默认实现类:
    org.apache.ibatis.cache.impl.PerpetualCache

一级缓存失效的四种情况:
    1、不在同一个SqlSession对象中
    2、执行语句的参数不同,缓存中也不存在数据
    3、手动清空缓存数据---sqlSession.clearCache();//清空缓存
    4、执行增删改语句会清空缓存

对应的mapper接口中的方法:public UserTwo queryUserById(Integer id);
自定义二级缓存{
	/*
	    自定义二级缓存
	        1.编写一个实现Cache接口的实现类A
	        2.在mapper.xml配置文件的cache标签中,设置type="全类名.实现类A"
	*/

	public class MySecondCache implements Cache {

	    private final String id;
	    private Map<Object,Object> cache=new HashMap<>();

	    public MySecondCache(String id) {
	        this.id = id;
	    }


	    @Override
	    public String getId() {
	        return this.id;
	    }

	    @Override
	    public void putObject(Object key, Object value) {
	        System.out.println("自定义 二级缓存 保存数据 putObject");
	        this.cache.put(key,value);
	    }

	    @Override
	    public Object getObject(Object key) {
	        System.out.println("自定义 二级缓存 获取数据 getObject");
	        return this.cache.get(key);
	    }

	    @Override
	    public Object removeObject(Object key) {
	        return this.cache.remove(key);
	    }

	    @Override
	    public void clear() {
	        this.cache.clear();
	    }

	    @Override
	    public int getSize() {
	        return this.cache.size();
	    }

	    @Override
	    public ReadWriteLock getReadWriteLock() {
	        return null;
	    }

	    @Override
	    public boolean equals(Object o) {
	        if (this == o) return true;
	        if (o == null || getClass() != o.getClass()) return false;
	        MySecondCache that = (MySecondCache) o;
	        return Objects.equals(id, that.id) &&
	                Objects.equals(cache, that.cache);
	    }

	    @Override
	    public int hashCode() {
	        return Objects.hash(id, cache);
	    }
	}
}
对应的.xml中{
	<!--
	    使用二级缓存
	        type属性指定二级缓存实现类
	-->
	    <cache type="全类路径.MySecondCache"/>

	<!--
	    public User queryUserById(Integer id);
	    useCache="true"表示把查询结果放到二级缓存中
	    useCache="false"表示不把查询结果放到二级缓存中
	-->
	    <select id="queryUserById" resultType="全路径.User" useCache="true">
	        select 具体字段 from user where id=#{id}
	    </select>
}

带表关系的查询

        一对一查询

        在持久层存在一对锁和钥匙表,其中锁表的主键是钥匙表的外键,表示一把钥匙配一把锁。在对象关系映射后的锁对象和钥匙类中,锁类仅仅拥有自己的属性,钥匙类除了拥有自己的属性外,还将锁类(不是锁对象的某一属性)引入了自己的属性中。

则对应的Keymapper.xml中主要的内容:

解决级联属性

<!--
	id表示唯一标识,可以自定义
	type表示和那个javaBean对象做列的映射关系
-->
	<resultMap id="resultMapOne" type="全路径.Key">
		<id column="id" property="id"/>
		<result column="name" property="name"/>
<!--
		级联属性映射
			一级一级地去映射列

		<result column="lock_id" property="lock.id"/>
		<result column="lock_name" property="lock.name"/>

-->
<!--
		使用association映射子对象的属性
			property设置子对象(子对象属性名称)
-->

		<association property="lock" javaType="全路径.Lock">
			<result column="lock_id" property="id"/>
			<result column="lock_name" property="name"/>
		</association>
	</resultMap>

通过钥匙id查询一对钥匙和锁

<!--    public Key queryKeyById(Integer id);-->
<select id="queryKeyById" resultMap="resultMapOne">
    SELECT
        t_key.id id,
        t_key.`name` name,
        t_lock.id lock_id,
        t_lock.`name` lock_name
    FROM
        t_key LEFT JOIN t_lock
    ON
        t_lock.id=t_key.lock_id
    WHERE
        t_key.id=#{id}
</select>

仅仅对钥匙表进行查询

<resultMap id="resultMapTwo" type="全路径.Key">
	<id column="id" property="id"/>
	<result column="name" property="name"/>
	<!--
		association标签可以用于映射一个子对象
			一种是通过调用一个查询来得到一个子对象
			一种是通过建立查询好的列和子对象的属性来进行映射

			property属性指定要映射那个子对象
			select属性指定要使用那个查询得到子对象,在此处调用了对锁表操作的mapper接口中的方法queryLockById
			column属性表示把结果集中的那个列作为参数给调用的方法的使用
	-->
	<association property="lock" column="lock_id" select="com.lixxkv.mapper.LockMapper.queryLockById" />
</resultMap>


<!--
	public Key queryKeyByIdForSimple(Integer id);
-->
<select id="queryKeyByIdForSimple" resultMap="resultMapTwo">
	SELECT `id`,`name`,`lock_id` FROM t_key WHERE id=#{id}
</select>

底层sql语句的实现

#内连接查询
SELECT 
	t_key.id key_id,
	t_key.`name` key_name,
	t_lock.id lock_id,
	t_lock.`name` lock_name
	
FROM 
	t_key,t_lock
WHERE
	t_lock.id=t_key.lock_id
AND
	t_key.id=1
	
	
#外连接查询
SELECT
	t_key.id key_id,
	t_key.`name` key_name,
	t_lock.id lock_id,
	t_lock.`name` lock_name
FROM
	t_key LEFT JOIN t_lock
ON
	t_lock.id=t_key.lock_id
WHERE
	t_key.id=2

一对多查询

        在持久层存在学生班级表,其中班级表中的主键是学生表中的外键,表示一个班级可以有多个学生,一个学生只有一个班级。在对象关系映射后,学生类仅仅只有自己的属性,班级类除了拥有自己的属性外还将学生类引入自己的属性中。

则对应的学生mapper.xml

<!--
    通过班级id查询学生信息
    public List<Student> queryStudentByClazzId(Integer clazzId);
-->
    <select id="queryStudentByClazzId" resultType="com.lixxkv.pojo.Student">
        select `id`,`name` FROM t_student where clazz_id=#{classid}
    </select>

<!--
    通过班级id或班级模糊名字查询学生信息
    public List<Student> queryStudentByClazzIdAndLikeName(
        @Param("clazzId") Integer clazzId,
        @Param("name") String name
    );
-->
    <select id="queryStudentByClazzIdAndLikeName" resultType="全路径.Student">
        select `id`,`name` FROM t_student 
        where clazz_id=#{clazzId} 
        and name 
        like concat('%',#{name},'%')
    </select>

对应的班级mapper.xml

解决级联问题

<resultMap id="resultMapOne" type="全路径.Clazz">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
<!--
    collection表示集合
        property表示集合的属性名称
        ofType表示集合元素类型
-->
    <collection property="studentList" ofType="全路径.Student">
        <id column="stu_id" property="id"/>
        <result column="stu_name" property="name"/>
    </collection>
</resultMap>

一次性,根据id查询出对应班级,以及班级所有学生信息

(public Clazz queryClazzById(Integer id);)

<select id="queryClazzById" resultMap="resultMapOne">
    SELECT
        t_clazz.*,
        t_student.id stu_id,
        t_student.`name` stu_name
    FROM
        t_student
    LEFT JOIN
        t_clazz
    ON
        t_clazz.id=t_student.clazz_id
    WHERE
        t_clazz.id=#{id}
</select>

根据班级编号查询,同时对学生姓名做模糊查询

public List<Student> queryStudentByClazzIdAndLikeName(
    @Param("clazzId") Integer clazzId,
    @Param("name") String name
);
<resultMap id="resultMapTwo" type="全路径.Clazz">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
	<!--
    collection标签不仅可以把查询回来的结果映射成为一个集合
    也可以调用一个查询,得到一个集合的结果
        property是集合的属性名称!!!!!!!!
        select属性指定调用那个查询(包名+类名+方法名称 或 名称空间+id)
        column指定那个列作为调用方法的参数值


     //根据班级编号查询,同时对学生姓名做模糊查询
     public List<Student> queryStudentByClazzIdAndLikeName(
        @Param("clazzId") Integer clazzId,
        @Param("name") String name
     )
     以上方法需要两个参数,他们在sql语句中刚好是#{clazzId}和#{name},

     多列赋值的格式如下:
        { clazzId=值,name=值 }
        { clazzId=id,name=name }

	-->
    <collection property="studentList" column="{ clazzId=id,name=name }"
                select="全路径.StudentMapper.queryStudentByClazzIdAndLikeName"/>
</resultMap>
<!--
	public Clazz queryClazzByIdFprTowStep(Integer id);
-->
<select id="queryClazzByIdFprTowStep" resultMap="resultMapTwo">
    select `id`,`name` from t_clazz where id=#{id}
</select>

多对多查询

        在持久层存在用户、角色表。一个用户对应多个角色,一个角色对应多个用户。在角色表中除了角色本身的属性以外,还将用户类关联到自己的属性中。同理在用户表中除了用户本身的属性以外,还将角色类关联到自己的属性中。

则对应的角色mapper

解决级联问题

<!--
    定义roleMap
-->
    <resultMap id="roleMap" type="全路径.Role">
        <id property="roleId" column="rID"/>
        <result property="roleName" column="ROLE_NAME"/>
        <result property="roleDesc" column="ROLE_DESC"/>
<!--        property是javaBean中的属性集合名称-->
        <collection property="userFives" ofType="全路径.UserFive">
            <id column="id" property="id"></id>
            <result column="username" property="username"></result>
            <result column="birthday" property="birthday"></result>
            <result column="sex" property="sex"></result>
            <result column="address" property="address"></result>
        </collection>
    </resultMap>

查询所有角色(public List<Role> findAll();)

<!--
    查询所有
    List<Role> findAll();

    错误一:
        resultType和resultMap混用
        resultMap的id还不一致
-->
    <select id="findAll"  resultMap="roleMap">
        select u.*,r.id as rid, r.ROLE_NAME,r.ROLE_DESC from role r
        left outer join user_role ur
        on r.id=ur.rid
        left  outer join `user` u
        on u.id=ur.uid
    </select>

对应的用户mapper

解决级联问题

<!--
    定义roleMap
-->
    <resultMap id="userMap" type="全路径.UserFive">
        <id column="id" property="id"></id>
        <result column="username" property="username"></result>
        <result column="birthday" property="birthday"></result>
        <result column="sex" property="sex"></result>
        <result column="address" property="address"></result>

        <!--        property是javaBean中的属性集合名称-->
        <collection property="roles" ofType="全路径.Role">
            <id property="roleId" column="rID"/>
            <result property="roleName" column="ROLE_NAME"/>
            <result property="roleDesc" column="ROLE_DESC"/>
        </collection>
    </resultMap>

查询所有用户(public List<UserFive> findAllTwo();)

<!--
    查询所有
     public List<Role> findAllTwo();
-->
<select id="findAllTwo"  resultMap="userMap">
    select u.*,r.id as rid, r.ROLE_NAME,r.ROLE_DESC from `user` u
    left outer join user_role ur
    on u.id=ur.uid
    left  outer join role r
    on r.id=ur.rid
</select>

2)用于插入
简单插入:
    在mapper接口定义int类型插入方法,在对应的mapper.xml中使用insert标签在insert标签体内存在以下属性:

        id="在mapper接口中定义的插入方法名称"
        useGeneratedKeys="true"表示使用,即在日志中返回插入的id值
        keyProperty="id"是指将在日志中返回的主键id值,注入到参数的那个属性中,
        parameterType="全路径.插入对象类"

        在inser标签中自定义插入语句:
            insert  into `表`(`字段列表`) values(#{字段一},#{字段二},...)

        在insert标签中可以使用selectKey标签用于在日志中返回插入数据的主键值,在selectKey标签体中有以下属性

order属性设置两个语句的执行顺序(selectKey语句的执行顺序)
    BEFORE:先执行selectKey语句
    AFTER:后执行selectKey语句
resultType="int":表示返回Integer类型,即数据库中的影响的行数
keyProperty="id"是指将在日志中返回的主键id值,注入到参数的那个属性中
在selectKey中可以使用"select last_insert_id()"用于接收数据库中插入数据的主键值

    当参数是一个javaBean对象(pojo对象),只需要再把#{}中写上属性名称即可

    public class User {
        private Integer id;
        private String username;   ====》》》#{username}   
        private Date birthday;       ====》》》#{birthday}    以此类推
        private String sex;
        private String address;
    }

        insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`) values (null,"lixxkv","2022-03-04","男","上海")

插入实例

mapper接口定义方法

public int saveUser(User user);

对应的xml

方式一{
	<insert id="saveUser" useGeneratedKeys="true" keyProperty="id"parameterType="全路径.User">
        insert  into `user`(`字段列表`) values(#{字段一},#{字段二},...)
    </insert>
}

方式二{
    <insert id="saveUser" parameterType="user">
        <selectKey order="AFTER" keyProperty="id" resultType="int">
            select last_insert_id()
        </selectKey>
        insert  into `user`(`字段列表`) values(#{字段一},#{字段二},...)
    </insert>
}
方式三{
	<insert id="saveUser" parameterType="全路径.User">
        insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values(#{id},#{username},#{birthday},#{sex},#{address})
    </insert>
}

带缓存的插入:
    在update,delete,insert标签上,都有flushCache属性。表示是否清空缓存,默认是true,表示清空缓存
    public int insertUser(User user);
        flushCache="true"表示执行当前insert操作清空缓存,false则相反

实例一

mapper接口中的方法定义

public int insertUser(User user);

对应的mapper.xml中的定义

<insert id="insertUser" parameterType="全路径.User" flushCache="true">
    insert into `user`(`id`,`username`,`birthday`,`sex`,`address`) values(#{id},#{username},#{birthday},#{sex},#{address})
</insert>

多值插入:
    可以在insert标签下使用foreach标签实现多只插入,在foreach标签体内有以下几个属性:
        collection="list":声明方法中传入的参数是什么集合类型
        item="UserFou":声明集合中每个个体是什么类
        separator=",":将“,”做为集合中每个类的分割界限

实例一:

mapper接口中的方法定义

//inser into 表(列名,列名)value(值1,值2),(值1,值2),(值1,值2)
public int insertUser(List<User> users);

对应的mapper.xml

<insert id="insertUser" >
    insert  into `user`(`id`,`username`,`birthday`,`sex`,`address`)
    values
    <foreach collection="list" item="User" separator=",">
        (#{User.id},#{User.username},#{User.birthday},#{User.sex},#{User.address})
    </foreach>
</insert>

3)用于更新
简单更新:
    可以使用update标签,将对象作为参数对数据库中的表格数据进行更新,updat标签体的属性:parameterType="user"用于指定更新的数据对象。
实例

mapper接口中的自定义方法

public int updateUser(User user);

对应mapper.xml

<update id="updateUser" parameterType="user">
    update user set `username`=#{username},`birthday`=#{birthday},`sex`=#{sex},`address`=#{address} where id=#{id}
</update>

指定更新:
    使用set标签,可以对数据库表格中的数据可以进行指定更新,其中set标签可以删除set关键字条件前后的逗号
实例

mapper接口中自定义的方法

public int updateUser(User user);

 对应的mapper.xm

<update id="updateUser" parameterType="全路径.UserFour" >
    update
        user
    <set>
        `username`=#{username},
        `birthday`=#{birthday},
        `sex`=#{sex},
        <if test="address!=null">
            `address`=#{address}
        </if>
    </set>
    <where>
        `id`=#{id}
    </where>
</update>

4)删除
    使用delete标签,其标签体内的属性:id="deleteUserById"用于指定mapper接口中绑定的方法
实例

mapper接口自定义方法

public int deleteUserById(Integer id);

对应mapper.xml

<delete id="deleteUserById">
    delete from user where id=#{id}
</delete>

六、测试类详解篇

在搭建好mybatis框架后,对框架功能进行测试,在此处使用单元测试方法进行测试

在测试之前

定义SqlSessionFactory

static SqlSessionFactory sqlSessionFactory;

使用@Before注解(@Before注解标识的方法会在测试方法之前执行)对sqlSessionFactory进行初始化

@Before
public void init() throws IOException {
    System.out.println("初始化");
    
    //由mybatis-config.xml配置文件,先构建SqlSessionFactory对象实例
    //读取mybatis-config.xml配置文件
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);

    //可以简写
    //sqlSessionFactory=new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));

}

测试框架定义

@Test
public void functionName(){
	//1通过SqlSessionFactory对象获取SqlSession;相当于jdbc中的Connection连接对象->用完即关。每定义一次SqlSession就有一个新的SqlSession对象
	SqlSession sqlSession = sqlSessionFactory.openSession();
	try{
		//2通过SqlSession获取mapper接口(代理对象)
		UserMapper mapper = sqlSession.getMapper(UserMapper.class);
		//添加数据
		User user = new User(null, "lixxkv", new Date(), "男", "上海");
		//3.调用接口方法
		int count = mapper.saveUser(user);
		System.out.println("影响的行数=>"+count);
		//手动提交事务
		sqlSession.commit();
	}catch (Exception e) {
        e.printStackTrace();
    }finally{
		//4.关闭SqlSession对象
		sqlSession.close();
	}
}

七、注解开发
    使用注解开发和不适用注解开发的区别仅仅只是在注解开发中少了mapper接口对应的.xml文件
    在mapper接口中使用以下注解

@Select
@Insert
@Update
@Delete
@ResultMap//用于返回数据

查询实例

普通查询

@Select("select 字段列表 from user")
List<User> findAllAnn();

@Select("select 聚合函数 from user")
int findTotalUser();

带条件的查询

@Select("select 字段列表 from user where id=#{id}")
User findById(Integer userId);

//@Select("select 参数列表 from user where username like concat(#{username},'%')")
@Select("select 字段列表 from user where username like '${value}%'")
List<User> findUserByName(String username);

简单增,删和修改

@Insert("insert  into `user`(`字段列表`) values (#{属性一},#{属性二},...")
void saveUser(User user);

@Update("update user set `username`=#{属性一},`birthday`=#{属性二},... where id=#{id}")
void updateUser(User user);

@Delete("delete from user where id=#{id}")
void deletUser(Integer userId);

在多对多条件下的特有查询,以用户(user)和账户(account)和为列,一个用户有多个账户,一个账户有一个用户

针对账户的操作

//由用户id查询账户信息
@Select("select 参数列表 from account where uid=#{userId}")
List<Account> findAccountByUid(Integer userId);


//查询所有账户,并且获取每个账户下所属用户信息
@Select("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(property = "user",column = "uid",one=@One(select="全路径.UserMapper.findById(方法名称)",fetchType= FetchType.EAGER))
})
List<Account> findAll();

针对用户的操作

//查询所有用户信息
@Select("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(property = "accounts",column = "id",many=@Many(select = "全路径.AccountMapper.findAccountByUid(方法名称)",fetchType = FetchType.LAZY))
})
List<UserSeven> findAllAnnSeven();

八、逆向工程
    使用逆向工程时,在src同级目录下配置逆向工程配置.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>

    相关配置信息

</generatorConfiguration>

设置版本

<!--
    targetRuntime可以设置生成的版本
        MyBatis3Simple:基本的增删改查
        MyBatis3:带条件查询的增删改查
-->
<context id="DB2Tables" targetRuntime="MyBatis3">

	相关配置信息

</context>

去掉注释

<!--
    去掉全部注释
-->
<commentGenerator>
    <property name="suppressAllComments" value="true"/>
</commentGenerator>

修改数据库的四个连接属性

<jdbcConnection driverClass="com.mysql.jdbc.Driver"
                connectionURL="jdbc:mysql://ip:端口/数据库"
                userId="账户"
                password="数据库代码">
</jdbcConnection>
<javaTypeResolver >
    <property name="forceBigDecimals" value="false" />
</javaTypeResolver>

生成javaBean

<!--
    javaModelGenerator生成javaBean
        targetPackage指定生成的包
        targetProject指定生成的项目位置
-->
<javaModelGenerator targetPackage="全路径.pojo" targetProject=".\mybatis-mbg\src">
    <property name="enableSubPackages" value="true" />
    <property name="trimStrings" value="true" />
</javaModelGenerator>

生成mapper.xml配置文件的信息

<!--
    javaClientGenerator生成mapper接口
        targetPackage指定生成的包
        targetProject指定生成的项目位置
-->
<javaClientGenerator type="XMLMAPPER" targetPackage="全路径.mapper"  targetProject=".\mybatis-mbg\src">
    <property name="enableSubPackages" value="true" />
</javaClientGenerator>

逆向工程数据库选择

<!--
    table标签,一个标签,一个数据库表
        tableName表名
        domainObjectName生成的类名
-->
<table  tableName="user" domainObjectName="UserForMBG" ></table>
<table  tableName="t_lock" domainObjectName="Tlock" ></table>
<table  tableName="t_key" domainObjectName="Tkey" ></table>

在配置文件配置和后。设置启动类

import org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class Runner {
    public static void main(String[] args) throws Exception{
        List<String> warnings = new ArrayList<String>();
        boolean overwrite = true;
        File configFile = new File("mybatis-mbg/mbg.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(configFile);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config, callback, warnings);
        myBatisGenerator.generate(null);
    }
}

运行启动类,运行成功后逆向工程完成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值