mybatis比mysql安全吗_MyBatis看这一篇就够了

本文详细介绍了MyBatis框架的原理和优势,包括如何简化JDBC操作,如配置全局XML和Mapper文件,使用动态SQL,以及处理多表关系。还探讨了MyBatis的插入、更新、删除操作,以及缓存机制和动态SQL功能。通过对MyBatis的深入理解,有助于提升Java项目的开发效率和代码维护性。
摘要由CSDN通过智能技术生成

MyBatis看这一篇就够了

​MyBatis是一个优秀的基于Java的持久层框架,它内部封装了JDBC;试用MyBatis框架开发者只需要关注SQL语句本身。而不需要花费大量精力去处理:加载驱动、创建连接、创建Statement等繁杂过程。

​MyBatis通过xml或者注解的方式将要执行的各种Statement配置起来,并通过Java对象和Statement中SQL的动态参数进行映射生成最终执行的SQL语句,最后由MyBatis框架执行SQL并将结果映射为Java对象返回。

MyBatis和传统JDBC比起来,优势在哪里?

数据库创建链接、释放资源造成系统浪费从而影响系统性能;虽然使用连接池也可以解决,但是MyBatis底层帮我们封装好了,简化了操作。

SQL和Java代码耦合在一起,不利于代码的维护,在开发中Java代码变化的可能性不是很大,但是SQL会因为需求经常变化。MyBatis将SQL从代码中剥离,利于项目的维护。

使用PreparedStatement使用占位符的形式不够灵活,经常要加很多判断语句。导致代码可读性,可维护性降低,对于多变的SQL,MyBatis的动态SQL更加灵活。

JDBC查询到RestSet后我们还需要对结果进行提取,而MyBatis支持将结果直接封装为JavaBean、List、Map、Java基本类型等的数据类型。

MyBatis快速入门

1、新建maven工程

2、添加maven的坐标。此处用到了mybatis、mysql-connector-java、junit、log4j

org.mybatis

mybatis

3.4.5

junit

junit

4.10

test

mysql

mysql-connector-java

5.1.6

runtime

log4j

log4j

1.2.12

3、编写实体类,这里简单的编写一个User类

package cn.rayfoo.bean;

import java.io.Serializable;

import java.util.Date;

/**

* @author: rayfoo@qq.com

* @date: 2020/6/6 1:57 上午

* @description:

*/

public class User implements Serializable {

/**用户id*/

private Integer id;

/**用户名*/

private String username;

/**生日*/

private Date birthday;

/**性别*/

private String sex;

/**住址*/

private String address;

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

public Date getBirthday() {

return birthday;

}

public void setBirthday(Date birthday) {

this.birthday = birthday;

}

public String getSex() {

return sex;

}

public void setSex(String sex) {

this.sex = sex;

}

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address = address;

}

@Override

public String toString() {

return "User{" +

"id=" + id +

", username='" + username + '\'' +

", birthday=" + birthday +

", sex='" + sex + '\'' +

", address='" + address + '\'' +

'}';

}

public User(Integer id, String username, Date birthday, String sex, String address) {

this.id = id;

this.username = username;

this.birthday = birthday;

this.sex = sex;

this.address = address;

}

public User() {

}

}

4、编写持久层接口UserMapper

package cn.rayfoo.mapper;

import cn.rayfoo.bean.User;

import java.util.List;

/**

* @Author: rayfoo@qq.com

* @Date: 2020/6/6 2:12 上午

* @Description: 用户的查询接口

*/

public interface UserMapper {

/**

* 查询所有用户

* @return 返回查询到的所有用户

*/

List findAllUser();

}

5、创建MyBatis全局配置文件和映射文件

/p>

PUBLIC "-//mybatis.org//DTD Config 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-config.dtd">

/p>

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

select id,username,birthday,sex,address from `user`

6、编写测试类进行测试

package cn.rayfoo.test;

import cn.rayfoo.bean.User;

import cn.rayfoo.mapper.UserMapper;

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;

import java.util.List;

/**

* @Author: rayfoo@qq.com

* @Date: 2020/6/6 10:01 下午

* @Description:

*/

public class MyBatisTest {

public static void main(String[] args) throws Exception {

//1、读取全局配置文件

InputStream conf = Resources.getResourceAsStream("MyBatis-cfg.xml");

//2、创建工厂对象SqlSessionFactory

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(conf);

//3、使用工厂生产SqlSession

SqlSession sqlSession = sqlSessionFactory.openSession();

//4、创建接口的代理类对象

UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

//5、执行查询方法

List allUser = userMapper.findAllUser();

//6、查看结果

for (User user : allUser) {

System.out.println(user);

}

//7、释放资源

sqlSession.close();

conf.close();

}

}

MyBatis执行流程

实现由SQLSessionFactoryBuilder加载配置文件

然后由SQLSessionFactoryBuilder构建一个SqlSessionFactory

SqlSessionFactory生产SqlSession

SqlSession使用Executor来调度MappedStatement执行sql语句

MappedStatement会根据参数类型和返回值类型 执行并且返回对应类型的数据

此处介绍的略为简单,源码级别的分析详见转自CSDN博客

MyBatis配置文件的顺序

MyBatis配置文件是有先后顺序的,如果顺序不对就会报错,所以要注意其顺序问题。

![image-20200609005353368](/Users/rayfoo/Library/Application Support/typora-user-images/image-20200609005353368.png)

配置打印SQL

在MyBatis中,可以设置打印SQL来检测代码是否正常执行。在MyBatis的全局配置文件中加入如下配置即可。

配置驼峰转下划线

JavaBean中标准的属性命名规则是驼峰式,但是数据库中字段命名一般都由下划线间隔,解决这个问题就需要在MyBatis中配置驼峰转下划线。在MyBatis的全局配置文件的Settings标签中加入如下配置即可。

配置别名

MyBatis中配置别名的方法如下,直接将对应包中的所有类都配置别名

在全局配置文件中加载Mapper文件

使用MyBatis,需要告知其mapper接口对应的配置文件的位置,这个在全局配置文件中配置即可。

如果使用了Spring框架进行整合,就无须在此处配置。

MyBatis中Mapper接口使用JavaBean和Map作为参数

在MyBatis中的Mapper接口方法中,支持JavaBean或者Map作为参数直接传递。使用JavaBean需要设置为parameterType的全类名,使用map则需要设置为java.util.Map。

如果配置了别名,类名和map都可以省略包名使用首字母缩写。

#{}和#{}的区别

{}

使用#{}时,将在SQL作为一个占位符实现PreparedStatment中?的效果,占位符替换后会自动加上一对''

由于是试用占位符的形式,#{}可以防止SQL注入

{}可以接收基本数据类型和JavaBean

如果parameterType传输单个简单类型值,#{}中可以是value或者其他名称

${}

表示拼接SQL串

通过${}可以将parameterType传入的内容拼接在SQL中且不会加上''

${}可以接收基本数据类型和JavaBean

如果parameterType传输单个简单类型值,#{}中只能是value

使用模糊查询时,#{}必须在传参时加匹配符;使用字段名时(例如排序、分组)不能使用#{}只能使用\({},因为#{}会自动加上'',模糊查询也可以使用\){}直接拼接。

MyBatis中Mapper接口的定义规则

对应的xml文件的namespace必须和Mapper接口所在包名路径一致。

对应的xml文件id必须和接口方法名一致。

parameterType必须和接口方法参数类型一致。

resultType必须和接口方法返回值一致。

接口名尽量和xml文件名保持一致。

parameterType和resultType

前者用于指定输入参数类型,MyBatis通过OGNL从输入对象中获取参数值拼接在SQL中。

后者将MyBatis查询到的结果一行映射为resultType指定的类型对象;如果有多条对象则分行进行映射,并且将对象放入List容器中。

使用Map作为resultType

如果查询的结果是一个JavaBean对象,接口的返回值可以使用该Bean的类型或者Map来接收。如果作为Map接收,其字段名会作为key,值为value。

/**

* 使用JavaBean作为返回值 查询单个

* @param custId

* @return

*/

Hero findHeroById(Integer custId);

/**

* 使用Map作为返回值 查询单个

* @param custId

* @return

*/

Map findHeroById(Integer custId);

select * from `customer` where cust_id = #{value}

select * from `customer` where cust_id = #{value}

JavaBean作为返回值输出结果:

ClassName{column1=value,column2=value}

Map作为返回值输出结果:

{column1=value,column2=value}

如果查询的结果是List,依旧可以使用Map来接收,但是必须在在Mapper的接口方法上加上@MapKey("字段名")来指定key,其中T为该字段的类型。

/**

* 使用Map作为返回值 查询多个

* @return

*/

@MapKey("cust_id")

Map findAllHero();

select * from `customer`

[val1={column1=value,column2=value},val2={column1=value,column2=value},....]

推荐:亦或是使用List>作为返回值来接收

/**

* 使用List>作为返回值 查询多个

* @return

*/

List> findAllHero();

select * from `customer`

[{column1=value,column2=value},{column1=value,column2=value}....]

其打印结果类似于json串

MyBatis中的插入、删除和更新

insert、update、delete和查询基本一致不做太多说明。

注意点:

1、插入、删除、更新时的#{}以parameterType的属性名称对应,表名后的字段与数据库字段名保持一致。

2、一定要commit,否则数据库不会更新。

3、如果插入中文乱码,请检查数据库的url是否是characterEncoding=utf8,项目的编码是否为UTF8。

4、这三者一般都不设置resultType,默认返回值都是执行sql后数据库修改的行数。

案例:

insert into `customer`(cust_name,cust_profession,cust_phone,email)

values(#{custName},#{custProfession},#{custPhone},#{email})

MyBatis获取自增主键

方法1、在insert标签中加入子标签

select LAST_INSERT_ID()

方法2、设置insert标签的几个属性

​useGeneratedKeys设置为true

​keyProperty为数据库中主键字段的名称

​keyColumn为数据库中主键字段的名称

useGeneratedKeys="true" keyProperty="id" keyColumn="id"

方法2在MyBatis3.4.0以后的版本支持批量插入获取id,使用方法和普通的插入一致,主键会自动放入parameterType对应集合中对象的每一个id中。

public void addUserBatch(List list) {

this.getSqlSession().insert("userMapper.addUserBatch",list);

System.out.println(Arrays.toString(list.toArray()));

}

insert user(user_name,pass_word,address) values

( #{item.userName},#{item.passWord},#{item.address} )

MyBatis工具类

在使用MyBatis的过程中,我们需要频繁的加载配置文件,创建SqlSession等的繁杂过程,这就和原生JDBC一样有些累赘了,所以我们可以创建一个MyBatis工具类来简化这些重复的操作。

当然了,在和Spring整合后同样可以摒弃这些复杂的操作。

package cn.rayfoo.util;

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;

/**

* @Author: rayfoo@qq.com

* @Date: 2020/6/8 10:37 下午

* @Description: Mybatis工具类

*/

public class MyBatisUtils {

/**

* 加载配置文件

*/

private static InputStream conf = null;

/**

* SqlSession工厂

*/

private static SqlSessionFactory sqlSessionFactory = null;

/**

* SqlSession

*/

public static SqlSession sqlSession = null;

/**

* Session状态,true为开启中,false为已经关闭

*/

private static boolean sessionStatus = true;

static {

//1、读取全局配置文件

try {

conf = Resources.getResourceAsStream("MyBatis-cfg.xml");

} catch (IOException e) {

e.printStackTrace();

}

//2、创建工厂对象SqlSessionFactory

sqlSessionFactory = new SqlSessionFactoryBuilder().build(conf);

//3、使用工厂生产SqlSession

sqlSession = sqlSessionFactory.openSession();

}

/**

* 获取工厂信息

* @param clazz Mapper的字节码文件

* @return

*/

public static Object getMapper(Class clazz){

//1、如果Session为空或者已经关闭,则重新生产一个Session

if(!sessionStatus){

sqlSession = sqlSessionFactory.openSession();

}

//返回Mapper

return sqlSession.getMapper(clazz);

}

/**

* 返回一个sqlSession

* @return

*/

public static SqlSession getSqlSession(){

//1、如果Session为空或者已经关闭,则重新生产一个Session

if(!sessionStatus){

sqlSession = sqlSessionFactory.openSession();

}

return sqlSession;

}

/**

* 提交、销毁SqlSession

*/

public static void destory(){

//提交、关闭sqlSession

sqlSession.commit();

sqlSession.close();

try {

//关闭文件流

conf.close();

} catch (IOException e) {

e.printStackTrace();

}

sessionStatus = false;

}

private MyBatisUtils(){

}

}

测试工具类

package cn.rayfoo.test;

import cn.rayfoo.bean.Hero;

import cn.rayfoo.mapper.HeroMapper;

import cn.rayfoo.util.MyBatisUtils;

/**

* @Author: rayfoo@qq.com

* @Date: 2020/6/6 10:01 下午

* @Description: 测试类

*/

public class MyBatisTest {

public static void main(String[] args) throws Exception {

//获取Mapper

HeroMapper mapper = (HeroMapper) MyBatisUtils.getMapper(HeroMapper.class);

//创建一个英雄

Hero hero = new Hero("龙女","战士","10086","rayfoo@qq.com");

//添加英雄

mapper.insertCustomer(hero);

//提交事务

MyBatisUtils.destory();

}

}

Mapper接口中多参数问题

在开发过程中Mapper接口难免会遇到需要多个参数的时候,那么此时在xml中如何映射呢?

1、在MyBatis中,Mapper接口中的方法如果使用了多个参数,MyBatis会将这些参数打包到一个Map中,Map的key是param1、param2。。。以此类推,值也就是参数对应的值。MyBatis也允许使用#{arg0}、#{arg1}。。。的形式,如果是对象类型,就要使用#{param1.属性名}

2、MyBatis中还可以使用@Param注解来指定该属性的Key值,直接在参数之前加上@Param("key")即可。如果指定自定义key,仍然可以使用param1、param2。。的形式,但是不再允许使用arg0、arg1。。的形式了。

3、注意:尽量不要使用arg0、arg1。。。的形式,param是从1开始,arg是从0开始。

resultMap

resultType使用JavaBean作为返回值时,要求JavaBean的属性名和数据库字段名保持一致(驼峰转下划线为特例),使用resultMap可以解决这个问题。它的一大作用就是来映射数据库的字段名和JavaBean的属性名。如果查询时只想查询部分字段,同样可以使用resultMap来实现。

在Mapper接口对应的XML文件中加入resultMap标签,一般写在xml的最上方且命名为javaBean首字母小写+Map的形式。

其id属性必须唯一,可以被其他标签的resultMap属性指定。

type属性用于指定其对应的JavaBean的全类名(或者别名,但必须配置别名)。

id子标签用于指定数据库中的主键

result子标签用于指定数据库中普通字段

如果映射的不是全部字段,则查询时不会查询没有被映射的字段。

id和result都存在column和property两个属性,其中column映射数据库字段名,property映射JavaBean的属性名。

id和result都存在javaType和jdbcType两个属性,分别用来指定该标签中数据库和Java类型的属性,如果是一般类型的数据,这两个属性可以省略。

多表操作

一对多关系

在数据库中,经常会遇到一对多查询,那么常见的一对多关系比如部门>员工,班级>学员。。等等。遇到这类的数据库关系,就需要进行一对多映射。

一对多关系如何在数据库中建立?

在一对多关系中,通常会在多的一方建立一个外键用于保存一的一方的主键。

例如已有客户表,现在建立一个订单表,那么客户表无需更改,只需要在新建立的订单表中加入order原有的"order_id"、“order_name”、“order_num”以及"cust_id"四个字段即可。其中“cust_id”添加外键约束。

一对一关系如何在Java中映射?

在java类中,如果要映射一对多关系,一般是在多的一方中取消一对多中的“一方”外键属性的声明,使用一对多中的“一方”所在的java类取代。

public class Order {

/** 订单号 */

private Integer orderId;

/** 订单名称 */

private String orderName;

/** 订单金额 */

private Double orderPrice;

/** 下单人 */

private Customer customer;

getter setter 构造...

}

一对多关系如何在MyBatis中映射?

MyBatis中映射对多关系有多种方法,其中比较简单易于理解的就是使用连缀的方式,映射多对一中的“一方”的字段和属性。

案例:

select * from `order` o left join `customer` c on o.cust_id = c.cust_id

public interface OrderMapper {

List findOrders();

}

public class MyBatisTest {

public static void main(String[] args) throws Exception {

//获取Mapper

OrderMapper mapper = (OrderMapper) MyBatisUtils.getMapper(OrderMapper.class);

//获取全部订单

List orders = mapper.findOrders();

for (Order order : orders) {

System.out.println(order);

}

//销毁

MyBatisUtils.destory();

}

}

不映射直接使用Map同样可以解多对一多映射问题

select * from `order` o left join `customer` c on o.cust_id = c.cust_id

public interface OrderMapper {

List> findAllOrder();

}

public class MyBatisTest {

public static void main(String[] args) throws Exception {

//获取Mapper

OrderMapper mapper = (OrderMapper) MyBatisUtils.getMapper(OrderMapper.class);

//获取全部订单

List> allOrder = mapper.findAllOrder();

for (Map item : allOrder) {

System.out.println(item);

}

//销毁

MyBatisUtils.destory();

}

}

{cust_profession=战士, order_price=1536.29, cust_name=龙女, order_id=1, cust_id=13, cust_phone=10086, email=rayfoo@qq.com, order_name=花呗还款}

{cust_profession=战士, order_price=4000.0, cust_name=龙女, order_id=2, cust_id=14, cust_phone=10086, email=rayfoo@qq.com, order_name=基金加仓}

{cust_profession=战士, order_price=3298.0, cust_name=龙女, order_id=3, cust_id=13, cust_phone=10086, email=rayfoo@qq.com, order_name=购买手机}

使用association映射多对一关系

MyBatis提供的对多映射的另一种方式就是使用association,在resultMap中加入association子标签,在该标签中添加property和javaType,分别映射一对多中的“一方”在java类中的属性名,和其类型。在association中可以继续添加id和result来映射一对多中的“一方”。

association级联查询-分步查询

在association标签中,存在一个select属性,其值可以指定一个查询方法的id,如果该查询需要使用到参数,还可以指定column属性为查询参数。

select="cn.rayfoo.mapper.CustomerMapper.findCustomer" column="cust_id">

association级联查询-分步查询-懒加载

使用association进行分步查询,默认是查询时一定会执行select中的语句,无论是否用到了association中的元素。开启懒加载可以实现当用到association中的元素时执行才执行其对应的select语句。

修改mybatis的全局配置文件即可,当mybatis3.4.1以前都为true,之后的版本默认为false

但是当调用toString方法时,不管是否使用了association中映射的属性值,都会触发延迟加载,去掉lazyLoadTriggerMethods的value中的toString即可。

一对多的添加操作

对于一对多映射中的添加操作,应该先添加一的一方获取到一的一方的id才能添加多的一方。例如存在客户表和订单表,要进行客户和订单的添加,那么就需要先添加客户,获取客户的自增id,才能进而添加订单。

insert into `customer`(cust_name,cust_profession,cust_phone,email)

values(#{custName},#{custProfession},#{custPhone},#{email})

insert into `order`(order_name,order_price,cust_id)

values(#{orderName},#{orderPrice},#{customer.custId})

package cn.rayfoo.test;

import cn.rayfoo.bean.Customer;

import cn.rayfoo.bean.Order;

import cn.rayfoo.mapper.CustomerMapper;

import cn.rayfoo.mapper.OrderMapper;

import cn.rayfoo.util.MyBatisUtils;

import java.util.List;

import java.util.Map;

/**

* @Author: rayfoo@qq.com

* @Date: 2020/6/6 10:01 下午

* @Description: 测试类

*/

public class MyBatisTest {

public static void main(String[] args) throws Exception {

//获取Mapper

CustomerMapper customerMapper = (CustomerMapper) MyBatisUtils.getMapper(CustomerMapper.class);

OrderMapper orderMapper = (OrderMapper) MyBatisUtils.getMapper(OrderMapper.class);

//创建顾客

Customer customer = new Customer("盖伦","战士","10011","rayfooQqq.com");

customerMapper.insertCustomer(customer);

//创建订单

Order order1 = new Order("MacBook Pro",888.88,customer);

Order order2 = new Order("MacBook Air",999.99,customer);

orderMapper.insertOrder(order1);

orderMapper.insertOrder(order2);

//销毁

MyBatisUtils.destory();

}

}

一对多的查询操作

当映射的一方是多的一方,JavaBean中使用List来映射,resultMap也不能使用association了,association替换为collection,javaType替换为ofType,其余都没有变化。

分步查询并不见得更快,要结合实际的使用情况,分步查询会发送更多次的sql(每一条数据都会发送一条sql,相当于sql的循环调用),但是分步查询支持懒加载。

select * from `customer` as c left join `order` as o on c.cust_id = o.cust_id

package cn.rayfoo.test;

import cn.rayfoo.bean.Customer;

import cn.rayfoo.mapper.CustomerMapper;

import cn.rayfoo.util.MyBatisUtils;

import java.util.List;

import java.util.Map;

/**

* @Author: rayfoo@qq.com

* @Date: 2020/6/6 10:01 下午

* @Description: 测试类

*/

public class MyBatisTest {

public static void main(String[] args) throws Exception {

//获取Mapper

CustomerMapper customerMapper = (CustomerMapper) MyBatisUtils.getMapper(CustomerMapper.class);

List allCustomer = customerMapper.findAllCustomer();

for (Customer customer : allCustomer) {

System.out.print(customer);

}

//销毁

MyBatisUtils.destory();

}

}

一对多分步查询(不建议使用)

select * from `order` where order_id = #{value}

/**

* 根据cust_id查询

* @param cust_id

* @return

*/

List findOrderById(Integer cust_id);

package cn.rayfoo.test;

import cn.rayfoo.bean.Customer;

import cn.rayfoo.bean.Order;

import cn.rayfoo.mapper.CustomerMapper;

import cn.rayfoo.mapper.OrderMapper;

import cn.rayfoo.util.MyBatisUtils;

import java.util.List;

import java.util.Map;

/**

* @Author: rayfoo@qq.com

* @Date: 2020/6/6 10:01 下午

* @Description: 测试类

*/

public class MyBatisTest {

public static void main(String[] args) throws Exception {

//获取Mapper

CustomerMapper customerMapper = (CustomerMapper) MyBatisUtils.getMapper(CustomerMapper.class);

List allCustomer = customerMapper.findAllCustomer();

for (Customer customer : allCustomer) {

System.out.println(customer);

}

//销毁

MyBatisUtils.destory();

}

}

一对多删除

此时需要注意,如果用户已经不存在了,那么它对应的订单就是垃圾数据了,此时删除用户后应该同时删除掉用户的订单数据。

如果只想删除客户,不删除其关联的订单,可以先打破订单和客户之间的关系。使用update语句将符合条件的客户编号外键改为null即可。

多对多关系

在数据库中,也会遇到一对多查询,那么常见的多对多关系比如老师-学生,学生-课程,用户-角色。。等等。遇到这类的数据库关系,就需要进行多对多映射。

多对多建表原则

多对多关系,一般会建立一个中间表,中间表中创建两个外键存储两个表的主键。

例如现在存在学生表、教师表、则需要创建中间表教师_学生。

-- student table

create table student

(

student_id int auto_increment

primary key,

student_name varchar(20) null

);

-- teacher table

create table teacher

(

teacher_id int auto_increment

primary key,

teacher_name varchar(20) null

);

-- teacher_student table

create table teacher_student

(

id int auto_increment

primary key,

student_id int null,

teacher_id int null,

constraint teacher_student_student_student_id_fk

foreign key (student_id) references student (student_id),

constraint teacher_student_teacher_teacher_id_fk

foreign key (teacher_id) references teacher (teacher_id)

);

在javaBean中创建实体时,无需创建中间表的Bean对象。只需要创建Student、Teacher即可。

在MyBatis的映射中,我们可以将多对多转换为两个一对多,在两方别进行对多映射即可。和一对多的不同之处只有SQL语句。多对多同样支持分步查询。

多对多的添加分为三步,添加学生、添加老师、添加中间表。关系表的插入可以在学生、老师任意一段进行。

/**

* 添加一个学生

* @param student

*/

void insertStudent(Student student);

/**

* 更新学生和老师的关系

* @param teacherId

* @param studentId

*/

void updateRelation(@Param("teacherId") Integer teacherId, @Param("studentId") Integer studentId);

/**

* 添加一个教师

* @param teacher

*/

void insertTeacher(Teacher teacher);

insert into student(student_name) values(#{studentName})

insert into teacher_student(teacher_id,student_id)

values(#{teacherId},#{studentId})

insert into teacher(teacher_name) values(#{teacherName})

package cn.rayfoo.test;

import cn.rayfoo.bean.Customer;

import cn.rayfoo.bean.Order;

import cn.rayfoo.bean.Student;

import cn.rayfoo.bean.Teacher;

import cn.rayfoo.mapper.CustomerMapper;

import cn.rayfoo.mapper.OrderMapper;

import cn.rayfoo.mapper.StudentMapper;

import cn.rayfoo.mapper.TeacherMapper;

import cn.rayfoo.util.MyBatisUtils;

import java.util.List;

import java.util.Map;

/**

* @Author: rayfoo@qq.com

* @Date: 2020/6/6 10:01 下午

* @Description: 测试类

*/

public class MyBatisTest {

public static void main(String[] args) throws Exception {

//获取Mapper

TeacherMapper teacherMapper = (TeacherMapper) MyBatisUtils.getMapper(TeacherMapper.class);

StudentMapper studentMapper = (StudentMapper) MyBatisUtils.getMapper(StudentMapper.class);

//创建学生和老师

Student student = new Student("全浩");

Teacher teacher1 = new Teacher("李怡霖");

Teacher teacher2 = new Teacher("闫洪波");

//添加

studentMapper.insertStudent(student);

teacherMapper.insertTeacher(teacher1);

teacherMapper.insertTeacher(teacher2);

//更新关系

studentMapper.updateRelation(teacher1.getTeacherId(),student.getStudentId());

studentMapper.updateRelation(teacher2.getTeacherId(),student.getStudentId());

//销毁

MyBatisUtils.destory();

}

}

MySQL insert语句中中value和values的区别

这里补充一个知识点,在MySQL的insert语句中使用value和values都可以,建议在插入单行的时候使用VALUES,在插入多行的时候使用VALUE;在大多数情况下使用这样的方案效率比较高。而SQL Server中只支持values关键字。

动态SQL

MyBatis中提供了动态SQL的机制,所谓动态sql其实就是动态的拼接sql。MyBatis为我们提供了很多的标签,可以实现类似于java代码中的选择、分支、 循环等功能。通过这些标签可以动态的拼接sql。

if标签

if标签用于判断一个布尔值或者布尔表达式,来判定是否拼接标签内的SQL。一般用于where后条件的非空、忽略等判断。一般都会配合where标签一起使用。

where标签

在使用if标签的过程中会遇到一些问题,比如当if的test都为false时,就会多出一个where标签,当第一个条件为空时,就会多出一个and标签。此时就需要用到where标签

where标签可以自动的生成和删除where关键字、还可以删除where后第一个and关键字。

所以在使用where和if标签的时候 , 尽量使用前置and,不要使用后置and。

trim标签

此标签可以代替where标签来使用,主要的功能是对前缀、后缀的一些操作。

select * from `customer`

and cust_name = #{custName}

and profession = #{profession} and

此案例中,会在条件之前增加where,去掉条件之前的and、条件之后的and。

choose when otherwise标签

这个标签类似于java中的if else

当when中test的条件成立就只执行when中的语句,不再继续执行。否则执行otherwise中的内容。

其中when可以存在多个,otherwise只能存在一个。

select * from `customer`

and custName = #{cust_name}

and profession = #{profession}

and 1=1

set标签

在update的操作中 set后同样可以使用if来进行判断,此时如果存在多个if,就可能会出现多出“逗号”的情况,此时可以使用set标签来代替set关键字,set标签可以帮我们去掉最后一个逗号。一般使用set标签时,逗号都放在后面。

update `customer`

cust_name = #{custName},

cust_profession = #{custProfession}

cust_id = #{custId}

foreach标签

当查询的内容是多个时,可以使用foreach标签。foreach标签可以遍历list或者array类型的数据。

collection属性:指定参数类型,可选list和array,如果是array,那mybatis标签中的参数类型需要指定为对应类型的数组形式,例如parameterType="Integer[]",list则需要设置为java.util.List

open属性:开始标志一般用(

close属性:结束标志一般用)

separator属性:间隔符 一般用逗号

item属性:迭代变量 任意名称

在标签体内可以直接使用#{item对应的值}进行遍历

select * from `customer`

`cust_id` in

#{id}

foreach的高级用法:如果要遍历一个bean对象中的array或者list类型的属性,需要先将parameterType=指定为该对象。collection则指定此属性 其他用法相同。

select * from `customer`

`cust_id` in

#{id}

bind标签

该标签可以取出传入的值,重新处理,赋值给另一个值。

注意点:如果是普通类型的单值,必须加@Param注解,才能正常取出。如果是对象直接可以获取。

SQL片段

动态SQL支持将一段SQL封装为一个片段,此片段可以在多处执行。

用法:使用标签创建sql片段,使用引入。

sql片段中可以使用choose、if、等的逻辑判断

缓存机制

MyBatis中使用了缓存机制来提高性能,当查询数据时,会先从缓存中取出数据,如果缓存中没有,再到数据库中查询。

MyBatis中的缓存有两种:一级缓存和二级缓存

一级缓存

一级缓存是SqlSession(一次会话)级别的,二级缓存是Mapper级别的。

一级缓存默认是开启的状态,在SqlSession没有关闭之前,再去查询时,会从缓存中取出数据,不会重新发送新的sql。

一级缓存的失效:发生如下场景时,一级缓存会失效。

如果在下次查询之前进行了增、删、改操作,缓存就会失效。

手动清空了缓存(sqlSession.clearCache()方法)

两次查询的条件不一样,缓存也会失效

如果两个查询在不同的SqlSession当中

二级缓存

二级缓存是全局作用域缓存,一个namespace对应一个缓存,如果会话关闭,一级缓存的数据会被保存到二级缓存中,不同的namespace查出的数据,也会放到自己对应的缓存中,默认也是打开的,早期版本呢是false。

二级缓存使用的注意事项

1、确保全局配置文件中已经打开了二级缓存,settings->setting->cacheEnable == true

2、在对应的mapper中添加cache标签

​属性介绍

​eviction:回收策略LRU(默认)【移除最近最少使用】、FIFO【先进先出】、SOFT、WEAK

​flushInterval:刷新间距,单位是毫秒,默认不清空

​readOnly:是否只读,取值为true或false,为true是只读mybatis会直接给缓存的引用不安全,但是速度快,非只读mybatis会利用序列化和反序列化复制一份 速度慢

​size:可以存放多少个元素 默认的即可,也不能设置的太大,太大容易溢出

​type:可以用来指定自定义的缓存,例如redis

3、JavaBean要实现Serializable接口,因为readOnly设置为只读的话,需要进行复制操作。

注意:只要会话关闭后一级缓存中数据才会保存到二级缓存中!

缓存的相关配置

是否可以设置每个mapper方法的缓存是否开启?

在MyBatis的mapper方法中,可以单独的设置是否需要开启缓存,它的优先级是最高的。但是它只能控制二级缓存是否有用,无法影响一级缓存。

如何清空二级缓存?

增删改查标签中存在一个flushCache属性,默认为true,作用是清空一级、二级缓存。

可以手动调用sqlSession.clearCache()手动清空一级缓存。

如何禁用一级缓存?

在settings->setting->localCacheScope设置为STATEMENT,默认为SESSION,设置为STATEMENT可以关闭一级缓存。

缓存使用顺序

1、先到二级缓存找

2、二级缓存没有就去一级缓存找

3、一级缓存也没有就去数据库中找

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值