04_MyBatis 单表 CRUD 和多表处理关联操作

MyBatis 单表 CRUD 操作

单标的CRUD操作,主要根据业务如何编写高质量的SQL语句.单标操作的时候如果表中的列和实体的属性不对应的时候,建议使用别名方式进行影射处理.

#{} / ${value}的区别

  • #{} 表示占位符
  • ${value}SQL字符串的拼接 里面必须写value。而#{} 可以任意写.如果参数类型是实体类,那么这里面写的是实体类中的属性名.

模糊查询SQL语句的写法


- SQL:select * from tb_user where username like '%username%'

- MyBatis:
	- select * from tb_user where username like concat('%',#{username},'%')
    
	- select * from tb_user where username like "%"#{username}""%";
	
	- select * from tb_user where username LIKE '%${value}%' ;
	    - ###如果传递是简单数据类型,使用${}里面必须写value

重构抽取重复的SQL

重构原则:写且一次,凡是不超过三次,如果超过三次,需要进行重构

在MyBatis中有一个SQL片段。把重复写的SQL进行抽取

<sql id="tbuser">
	username,
	password,
	email,
	phone
</sql>

使用<include refid="tbuser"/>
面向对象设计特性:封装、继承、多态.
重构原则:写且一次,凡是不超过三次,如果超过三次,需要进行重构

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="shop.tbzj.mapper.UserMapper">

    <sql id="tbUserInfo">
        a.id,
          a.username,
          a.password,
          a.phone,
          a.email,
          a.created,
          a.update
    </sql>

    <select id="selectAll" resultType="TbUser">
        SELECT
          <include refid="tbUserInfo"/>
        FROM
          tbuser AS a
    </select>

    <select id="selectByUsername" resultType="TbUser">
       SELECT

        <include refid="tbUserInfo"/>
  FROM
	tbuser AS a
  WHERE
	username

	LIKE CONCAT("%",#{username},"%");

    </select>
</mapper>

多表关联操作映射文件编写

单表或者一对一结果映射
- 单表操作如果实体属性和表列名称不一致,除了使用别名,
 还可以使用`resultMap`进行一对一的进行映射除了。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="shop.tbzj.mapper.UserMapper">

    <sql id="tbUserInfo">
        a.id,
          a.username,
          a.password,
          a.phone,
          a.email,
          a.created,
          a.update
    </sql>

    <select id="selectAll" resultMap="tbUserMapping">
        SELECT
          <include refid="tbUserInfo"/>
        FROM
          tbuser AS a
    </select>

    <!-- resultMap最终还是要将结果映射到pojo上,type就是指定映射到哪一个pojo -->
    <resultMap id="tbUserMapping" type="TbUser">
        <!-- 定义主键 ,非常重要。如果是多个字段,则定义多个id -->
        <!-- property:主键在pojo中的属性名 -->
        <!-- column:主键在数据库中的列名 -->
       <!--定义主键 -->
        <id column="id" property="id"/>
        <!-- 定义普通属性 -->
        <result column="username" property="user_name"/>
        <result column="password" property="password"/>
        <result column="phone" property="phone"/>
        <result column="email" property="email"/>
        <result column="created" property="created"/>
        <result column="update" property="update"/>
    </resultMap>


<select id="selectByUsername" resultMap="tbUserMapping">
       SELECT
        <include refid="tbUserInfo"/>
        FROM
        	tbuser AS a
        WHERE
        	username
        LIKE CONCAT("%",#{username},"%");
</select>

实体类TbUser中的user_name和数据库tbuser表中的username 字段不一致,使用resultMap进行结果集映射处理

关联结果集映射
一对一

比如一个订单属于一个用户。

usernameuserName 可以对结果进行映射,但是user_nameusername 不能进行结果集映射。

比如用户表user 和订单表order的关系是一个用户对应多个订单。在查询的时候进行关联查询,查询的结果的映射有两种方式进行处理

  • 结果集映射使用resultType。
    专门使用一个pojo类包含订单信息和用户信息。
    比如:
    public class UserOrder extends Order{
    //-----下面的字段是用户信息的字段
    //通过继承方式把Order类的非私有字段继承过来.
        private Long id;
        private String user_name;
        private String password;
        private String phone;
        private String email;
        private Date created;
        private Date update;
        
    }
    
    在resultType中写"UserOrder“.
    
    
    <select id="queryOrderUser" resultType="orderUser">
	SELECT
    	o.id,
    	o.user_id
    	userId,
    	o.number,
    	o.createtime,
    	o.note,
    	u.username,
    	u.address
	FROM
	    `order` o
	LEFT JOIN 
	    `user` u ON o.user_id = u.id
</select>
    
有个缺点就是在java中的继承不能继承私有。如果子类访问父类的非私有属性和方法是通过关键字super()关键字进行访问.

被final修饰的类是不能被继承的。
  • 使用resultMap进行结果集映射
<resultMap type="order" id="orderUserResultMap">
	<id property="id" column="id" />
	<result property="userId" column="user_id" />
	<result property="number" column="number" />
	<result property="createtime" column="createtime" />
	<result property="note" column="note" />

	<!-- association :配置一对一属性 -->
	<!-- property:order里面的User属性名 -->
	<!-- javaType:属性类型 -->
	<association property="user" javaType="user">
		<!-- id:声明主键,表示user_id是关联查询对象的唯一标识-->
		<id property="id" column="user_id" />
		<result property="username" column="username" />
		<result property="address" column="address" />
	</association>
</resultMap>

<!-- 一对一关联,查询订单,订单内部包含用户属性 -->
<select id="queryOrderUserResultMap" resultMap="orderUserResultMap">
	SELECT
	o.id,
	o.user_id,
	o.number,
	o.createtime,
	o.note,
	u.username,
	u.address
	FROM
	`order` o
	LEFT JOIN `user` u ON o.user_id = u.id
</select>
一对多结果映射

比如:一个用户对一多个订单

public class User{
    private int id;
    private String username;
    private Date birthday;
    private String addres;
    //一个用户拥有多个订单使用List进程存储.
    private List<Order> orders;
}
UserMapper.xml的编写:

<resultMap type="user" id="userOrderResultMap">
	<id property="id" column="id" />
	<result property="username" column="username" />
	<result property="birthday" column="birthday" />
	<result property="sex" column="sex" />
	<result property="address" column="address" />

	<!-- 配置一对多的关系 -->
	<collection property="orders" javaType="list" ofType="order">
		<!-- 配置主键,是关联Order的唯一标识 -->
		<id property="id" column="oid" />
		<result property="number" column="number" />
		<result property="createtime" column="createtime" />
		<result property="note" column="note" />
	</collection>
</resultMap>

<!-- 一对多关联,查询订单同时查询该用户下的订单 -->
<select id="queryUserOrder" resultMap="userOrderResultMap">
	SELECT
	u.id,
	u.username,
	u.birthday,
	u.sex,
	u.address,
	o.id oid,
	o.number,
	o.createtime,
	o.note
	FROM
	`user` u
	LEFT JOIN `orders` o ON u.id = o.user_id
</select>
    

插入数据返回主键问题

  • MySQL中的表主键类型为自增类型
public class Order {
    private Long id;
    private String name;
    private Double price;
    }

OrderMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="shop.tbzj.mapper.OrderMapper">
    <insert id="insertOrder" useGeneratedKeys="true" keyProperty="id">
          insert into tb_order(name,price)values(#{name},#{price});
    </insert>
</mapper>

参考 中文官网MyBatis
useGeneratedKeys=“true” 开启获取主键
keyProperty=“id” 配置的是把获取的主键和实体类属性id进行对应映射

测试:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import shop.tbzj.entity.Order;
import shop.tbzj.mapper.OrderMapper;


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-context.xml")
public class TestOrder {

    @Autowired
    private OrderMapper orderMapper;

    @Test
    public void insertOrder() {
        Order order = new Order();
        order.setName("tom2");
        order.setPrice(22.3);

        orderMapper.insertOrder(order);
        //插入一条数据,吧主键id返回给实体类Order中的id
        System.out.println(order.toString());
    }

}

  • MySQL中的表主键类型为UUID类型
<!-- 保存订单 这里没有配置parameterType是因为在spring-contxt.xml中
    中的sqlSessionFactroy中进行配置了的
-->
<insert id="saveOrder">
	<!-- selectKey 标签实现主键返回 -->
	<!-- keyColumn:主键对应的表中的哪一列 -->
	<!-- keyProperty:主键对应的pojo中的哪一个属性 -->
	<!-- order:设置在执行insert语句前执行查询id的sql,还是在执行insert语句之后执行查询id的sql  uuid是BEFORE-->
	<!-- resultType:设置返回的id的类型 -->
	<selectKey keyColumn="id" keyProperty="id" order="BEFORE"
		resultType="string">
		SELECT LAST_INSERT_ID()
	</selectKey>
	insert into tb_order(id,name,price)values(#{id},#{name},#{price})
</insert>

select last_insert_id(); 查看最后插入的值是多少。

在分布式集群中如何解决主键ID冲突问题:
    这里讨论情况是id为自动增长的情况: 设置步长
        有个缺点,集群的数量如果有改动的话,需要重新设置步长。
        
    查询自增的步长
    SHOW VARIABLES LIKE 'auto_inc%'
    修改自增的步长
    SET @@auto_increment_increment=10;
    修改起始值
    SET @@auto_increment_offset=5;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值