SpringBoot中Hibernate,Mybatis使用一点
使用SpringBoot开发有段时间了,主要是是从慕课上面看的两个课程入门的2小时学会Spring Boot、Spring Boot进阶之Web进阶,然后还购买了廖师兄的一个付费课程SpringBoot微信点餐系统,然后就开始SpringBoot开发了。因为之前写的是PHP,对Java完全不了解,感觉写起来非常繁琐,尤其是数据库这一块,操作非常麻烦,对查询返回的结果都要建一个对象。因为跟着课程来的,所以刚开始用的是hibernate,但是后来用了mybatis就放弃了hibernate,因为hibernate虽然不用写SQL但是不灵活。其实文章标题取得不是很准确,Hibernate是JPA规范的实现,主要是JPA和MyBatis进行对比的。看完本篇,你即可使用mybatis进行简单的开发。
Hibernate使用
MySQL user表
CREATE TABLE `user` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_name` varchar(255) DEFAULT NULL COMMENT '用户姓名',
`user_phone` varchar(255) DEFAULT NULL COMMENT '用户电话',
`user_password` varchar(255) DEFAULT NULL COMMENT '用户密码',
`user_type` tinyint(4) DEFAULT NULL COMMENT '用户注册类型',
`an_ticket` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '安券',
`integral` float NOT NULL DEFAULT '0' COMMENT '积分',
`user_photo` varchar(255) DEFAULT NULL COMMENT '用户头像',
`user_input_invitation` varchar(255) DEFAULT NULL COMMENT '用户输入的邀请码',
`user_invitation` varchar(255) DEFAULT NULL COMMENT '用户自己生成的邀请码',
`login_time` timestamp NULL DEFAULT NULL COMMENT '上次登录时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`user_id`),
UNIQUE KEY `user_phone` (`user_phone`) USING BTREE,
UNIQUE KEY `user_name` (`user_name`) USING BTREE,
KEY `user_type` (`user_type`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=50 DEFAULT CHARSET=utf8;
上面是user表,需要建一个对应的Java对象
import lombok.Data;
import org.hibernate.annotations.DynamicUpdate;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.math.BigDecimal;
import java.util.Date;
/**
* Create by fengguofei
* Date: 2017/9/2
* Time: 18:02
*/
@Entity
@Data
@DynamicUpdate
public class User {
@Id
@GeneratedValue
private Integer userId;
//用户姓名
private String userName;
//用户手机
private String userPhone;
//用户密码
private String userPassword;
//用户类型
private Integer userType;
//账户安券
private BigDecimal anTicket;
//账户积分
private BigDecimal integral;
//用户头像
private String userPhoto;
//用户填写的邀请码
private String userInputInvitation;
//用户自己生成的邀请码
private String userInvitation;
//用户上次登录时间
private Date loginTime;
//创建时间
private Date createTime;
//更新时间
private Date updateTime;
}
上面是对应的Java对象,其中@Data是lombok插件,会自动帮你生成get,set,toString等常用的方法,非常方便。@Entity代表是数据库对象,供springboot扫描使用,@DynamicUpdate表示动态更新,主要是数据库中的updateTime会更新当对user表更新时。
MySQL列名字使用下划线分割,对应的Java对象使用驼峰规则,当然你也可以不这样做,使用@Column注解对列进行说明,@Table注解对表进行说明。
创建Repository对象
import com.anbixuan.dataobject.User;
import com.anbixuan.dto.interfaceMysqlData.LoginJudgeInterface;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
/**
* Create by fengguofei
* Date: 2017/9/2
* Time: 18:28
*/
public interface UserRepository extends JpaRepository<User, Integer>{
User findUserByUserPhone(String userPhone);
User findUserByUserName(String userName);
User findUserByUserInvitation(String invitation);
User findUserByUserId(Integer userId);
//根据用户的姓名,手机号码,邮箱来检测用户是否存在
@Query(value = "select u.user_id,u.user_type, u.user_password\n" +
"from `user` u,csoperson cp\n" +
"where u.user_id = cp.user_id and (u.user_name=:userNameOrPhoneOrEmail or u.user_phone=:userNameOrPhoneOrEmail\n" +
"or cp.csoperson_email = :userNameOrPhoneOrEmail)\n" +
"UNION\n" +
"select u.user_id,u.user_type, u.user_password\n" +
"from `user` u,csocompany cc\n" +
"where u.user_id = cc.user_id and (u.user_name=:userNameOrPhoneOrEmail or u.user_phone=:userNameOrPhoneOrEmail\n" +
"or cc.csocompany_email = :userNameOrPhoneOrEmail)\n" +
"UNION\n" +
"select u.user_id,u.user_type, u.user_password\n" +
"from `user` u,factor f\n" +
"where u.user_id = f.user_id and (u.user_name=:userNameOrPhoneOrEmail or u.user_phone=:userNameOrPhoneOrEmail\n" +
"or f.factor_email = :userNameOrPhoneOrEmail)\n" +
"UNION\n" +
"select u.user_id,u.user_type, u.user_password\n" +
"from `user` u,producer p\n" +
"where u.user_id = p.user_id and (u.user_name=:userNameOrPhoneOrEmail or u.user_phone=:userNameOrPhoneOrEmail\n" +
"or p.producer_email = :userNameOrPhoneOrEmail)\n" +
"UNION\n" +
"select u.user_id,u.user_type, u.user_password\n" +
"from `user` u,hospital h\n" +
"where u.user_id = h.user_id and (u.user_name=:userNameOrPhoneOrEmail or u.user_phone=:userNameOrPhoneOrEmail\n" +
"or h.hospital_email = :userNameOrPhoneOrEmail)",nativeQuery = true)
List<LoginJudgeInterface> findUserByNameOrPhoneOrEmail(@Param("userNameOrPhoneOrEmail") String userNameOrPhoneOrEmail);
}
可以看到对数据库的查询十分方便,从函数的名字就能知道这个查询的意义。第一个查询是通过用户的手机号码、第二个查询是通过用户的名字,以此类推,其中可以使用@Query进行SQL编写,除此之外JPA还提供了像like、orderBy、Not、In等操作具体可以查看Spring Data JPA - Reference Documentation。
缺点
这个缺点是我自己的观点,可能不是很准确,因为我也不是很精通这个。一般建表都是创建主键,如果数据量很多,再加上索引。像其他的什么触发器、函数、外键都不创建,这些都由编程实现。问题出现了,JPA的动态查询如果关联表的话,你的Java对应的实体必须要创建外键,也就是manytomany、manytoone这些东西。此外你的查询结果如果只是一个表中的部分字段,你还要创建一个接口,比如上面的findUserByNameOrPhoneOrEmail查询就创建了一个LoginJudgeInterface接口,如果我查询结果很多,那岂不是要创建一堆接口。对我来多有点头疼。我的想法就是MySQL就是一个主键加索引,我的实体对象就是单个表的对应,甚至不需要创建表的实体对应,没有那些外键,查询要支持动态查询,返回结果也要比较方便。这些mybatis都能做的很好。
Mybatis
需要在pom.xml中引入mybatis依赖
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
在.yml配置文件中加一些配置
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
call-setters-on-nulls: true
配置的作用是表明.xml文件的位置,以及如果查询结果是null也要返回对应的字段
通过@Select注解
import org.apache.ibatis.annotations.ResultType;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
/**
* Create by fengguofei
* Date: 2018/5/11
* Time: 14:43
*/
public interface RoleMapper {
@Select("select DISTINCT(ar.role_id) as `value`, ar.`name` as label from admin_role ar")
@ResultType(List.class)
List<Map<String, Object>> getRoleList();
@Select("select DISTINCT(ar.role_id) as roleId, ar.`name`, ar.introd from admin_role ar")
@ResultType(List.class)
List<Map<String, Object>> getRoleIntrodList();
}
如果这个Mapper文件没有放到之前指定的目录下面,springboot是无法扫描到的,可以添加@Mapper注解进行扫描。
通过xml
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/**角色权限设置
* Create by fengguofei
* Date: 2018/5/21
* Time: 19:54
*/
public interface RoleAuthSetMapper {
/**
* 获得角色列表
* @return
*/
List getRoleList();
/**
* 根据name获得菜单列表
* @param roleName
* @return
*/
List getRoleListByName(@Param("roleName") String roleName);
/**
* 获得菜单权限列表
* @return
*/
List getMenuAuth();
/**
* 添加角色,批量插入数据库
* @param roles
*/
void addRole(List<Map<String, String>> roles);
/**
* 获得最大的role_id
* @return
*/
Integer getRoleIdMax();
/**
* 获得某个角色对应的权限菜单
* @param roleId
* @return
*/
List getRoleMenuAuth(@Param("roleId") String roleId);
/**
* 删除指定role_id的角色
* @param roleIds
*/
void deleteRoles(List roleIds);
}
对应的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="com.xxx.dataobject.mapper.admin.RoleAuthSetMapper">
<select id="getRoleList" resultType="java.util.Map">
select distinct(ar.role_id) as roleId, ar.`name`, ar.introd
from admin_role ar
</select>
<select id="getRoleListByName" parameterType="java.lang.String" resultType="java.util.Map">
select distinct(ar.role_id) as roleId, ar.`name`, ar.introd
from admin_role ar
<where>
<if test="roleName != "-1"">
and ar.name like '%${roleName}%'
</if>
</where>
</select>
<select id="getMenuAuth" resultType="java.util.Map">
select am.`name` as menuName, am.menu_id as menuId,aa.authority_id as authorityId, aa.`name` as authorityName
from admin_menu am, admin_authority aa
where am.authority_id = aa.authority_id
</select>
<insert id="addRole" parameterType="java.util.ArrayList">
insert into admin_role (role_id, `name`, introd, authority_id, change_by)
VALUES
<foreach collection="list" item="role" separator=",">
(#{role.roleId}, #{role.roleName}, #{role.introd}, #{role.authorityId}, #{role.changeBy})
</foreach>
</insert>
<select id="getRoleIdMax" resultType="java.lang.Integer">
SELECT MAX(ar.role_id)
from admin_role ar;
</select>
<select id="getRoleMenuAuth" resultType="java.util.Map">
select ar.`name` as roleName, ar.introd, am.`name` as menuName, am.menu_id as menuId,aa.authority_id as authorityId, aa.`name` as authorityName
from admin_menu am, admin_authority aa, admin_role ar
where am.authority_id = aa.authority_id
and ar.authority_id = am.authority_id
and ar.role_id = ${roleId}
</select>
<delete id="deleteRoles" parameterType="java.util.List">
delete from admin_role
where role_id IN
<foreach collection="list" item="roleId" open="(" separator="," close=")">
#{roleId}
</foreach>
</delete>
</mapper>
.xml文件一定要放到之前设置的目录下面,其中namespace一定要指定具体的mapper文件,不然会报错。接下来说一下对应的增删改查、动态查询、like查询以及其他操作。
增删改查
插入一条数据
/**
* 插入一条敏感词数据
* @param sensitiveMap
*/
void insertSensitiveWord(Map<String, String> sensitiveMap);
<insert id="insertSensitiveWord" parameterType="java.util.Map">
INSERT admin_sensitive_word_dict(sensitive_word, remark, change_by)
VALUE (#{word}, #{remark}, #{changeBy})
</insert>
批量插入数据
/**
* 添加角色,批量插入数据库
* @param roles
*/
void addRole(List<Map<String, String>> roles);
<insert id="addRole" parameterType="java.util.ArrayList">
insert into admin_role (role_id, `name`, introd, authority_id, change_by)
VALUES
<foreach collection="list" item="role" separator=",">
(#{role.roleId}, #{role.roleName}, #{role.introd}, #{role.authorityId}, #{role.changeBy})
</foreach>
</insert>
就是通过foreach进行循环,foreach的具体用法可以查看官网
删除一条数据
/**
* 删除指定role_id的角色
* @param roleId
*/
void deleteRole(Integer roleId);
<delete id="deleteRoles" parameterType="java.lang.Integer">
delete from admin_role
where role_id = #{roleId}
</delete>
批量删除数据
/**
* 删除指定role_id的角色
* @param roleIds
*/
void deleteRoles(List roleIds);
<delete id="deleteRoles" parameterType="java.util.List">
delete from admin_role
where role_id IN
<foreach collection="list" item="roleId" open="(" separator="," close=")">
#{roleId}
</foreach>
</delete>
插入一条数据
/**
* 插入后台用户
* @param adminMap
*/
Integer insertAdminUser(Map adminMap);
<insert id="insertAdminUser" parameterType="java.util.Map" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert admin_user (`name`, `password`, role_id, phone,
branch_id, picture, education, graduate_school, major, id_number,
change_by)
value (#{name}, #{password}, #{role_id}, #{phone},
#{branch_id}, #{picture}, #{education}, #{graduate_school}, #{major}, #{id_number},#{change_by})
<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
SELECT LAST_INSERT_ID() as id
</selectKey>
</insert>
批量插入数据
/**
* 添加角色,批量插入数据库
* @param roles
*/
void addRole(List<Map<String, String>> roles);
<insert id="addRole" parameterType="java.util.ArrayList">
insert into admin_role (role_id, `name`, introd, authority_id, change_by)
VALUES
<foreach collection="list" item="role" separator=",">
(#{role.roleId}, #{role.roleName}, #{role.introd}, #{role.authorityId}, #{role.changeBy})
</foreach>
</insert>
更新一条数据
/**
* 更新后台用户
* @param adminMap
*/
void updateAdminUser(Map adminMap);
<update id="updateAdminUser" parameterType="java.util.Map">
update admin_user
set `name` = #{userName}, role_id = #{role_id}, phone = #{phone},
branch_id = #{branch_id}, education = #{education}, graduate_school = #{graduate_school},
major = #{major}, id_number = #{idNumber},change_by = #{change_by}
where id = #{userId}
</update>
动态查询
动态返回字段
/**
* 动态返回查询字段
* @param startDate
* @param endDate
* @param columns
* @return
*/
List<Map<String, Object>> getOffLineDateByColumnAll(String startDate,String endDate, String[] columns);
<select id="getOffLineDateByColumnAll" resultType="java.util.HashMap" >
select
<foreach collection="arg2" item="column" open="" separator="," close="">
${column}
</foreach>
from slabdatarealtime
where slabdatarealtime.SlabTime >= #{arg0}
and slabdatarealtime.SlabTime <= #{arg1}
</select>
动态查询条件
动态and
/**
*根据name获得后台用户列表
* @param name
* @return
*/
Page getAdminUserByNameList(@Param("sname") String name);
<select id="getAdminUserByNameList" parameterType="java.lang.String" resultType="java.util.HashMap">
select DISTINCT(au.id) as userId, au.`name` as userName, ar.`name` as roleName,au.phone,ab.`name` as branchName,
au.education, au.graduate_school, au.major, au.id_number as idNumber
from admin_user au, admin_role ar, admin_branch ab
where au.role_id = ar.role_id
and au.branch_id = ab.id
<if test="sname != "-1"">
and (au.`name` like '%${sname}%' or ar.`name` like '%${sname}%')
</if>
</select>
动态where
/**
* 根据name获得菜单列表
* @param roleName
* @return
*/
List getRoleListByName(@Param("roleName") String roleName);
<select id="getRoleListByName" parameterType="java.lang.String" resultType="java.util.Map">
select distinct(ar.role_id) as roleId, ar.`name`, ar.introd
from admin_role ar
<where>
<if test="roleName != "-1"">
ar.name like '%${roleName}%'
</if>
</where>
</select>
like
mybatis中like有几种写法,在此列举一下,以供学习。
通过bind
<select id="getSensitiveWord" resultType="java.util.Map" parameterType="java.lang.String">
select aswd.id, aswd.sensitive_word as word, aswd.remark
from admin_sensitive_word_dict aswd
<where>
<if test="sensitive != null">
<bind name="sent" value="'%' + sensitive + '%'"></bind>
aswd.sensitive_word like #{sent}
</if>
</where>
</select>
通过concat
<if test="date != null ">
and td.create_time like CONCAT('%','${date}','%')
</if>
直接写
<if test="roleName != "-1"">
ar.name like '%${roleName}%'
</if>
分页
通过PageHelper进行分页
引入依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.2</version>
</dependency>
配置
/**
* Create by fengguofei
* Date: 2018/3/1
* Time: 18:31
*/
import com.github.pagehelper.PageHelper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Properties;
@Configuration
public class MybatisConfig {
//配置mybatis的分页插件pageHelper
@Bean
public PageHelper pageHelper(){
PageHelper pageHelper = new PageHelper();
Properties properties = new Properties();
properties.setProperty("offsetAsPageNum","true");
properties.setProperty("rowBoundsWithCount","true");
properties.setProperty("reasonable","true");
properties.setProperty("dialect","mysql"); //配置mysql数据库的方言
// properties.setProperty("params","count=countSql");
pageHelper.setProperties(properties);
return pageHelper;
}
}
接下来就可以使用了
.mapper接口
/**
*根据name获得后台用户列表
* @param name
* @return
*/
Page getAdminUserByNameList(@Param("sname") String name);
.xml文件
<select id="getAdminUserByNameList" parameterType="java.lang.String" resultType="java.util.HashMap">
select DISTINCT(au.id) as userId, au.`name` as userName, ar.`name` as roleName,au.phone,ab.`name` as branchName,
au.education, au.graduate_school, au.major, au.id_number as idNumber
from admin_user au, admin_role ar, admin_branch ab
where au.role_id = ar.role_id
and au.branch_id = ab.id
<if test="sname != "-1"">
and (au.`name` like '%${sname}%' or ar.`name` like '%${sname}%')
</if>
</select>
service层
import com.github.pagehelper.PageInfo;
/**
* Create by fengguofei
* Date: 2018/4/16
* Time: 16:07
*/
public interface AdminUserService {
/**
* 根据name获得后台用户列表
* @param name
* @param page
* @param size
* @return
*/
PageInfo getAdminUserByNameList(String name, Integer page, Integer size);
serviceImpl层
import com.xxx.dataobject.mapper.admin.AdminUserMapper;
import com.anbixuan.service.admin.AdminUserService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Create by fengguofei
* Date: 2018/4/16
* Time: 16:09
*/
@Service
public class AdminUserServiceImpl implements AdminUserService {
@Autowired
private AdminUserMapper adminUserMapper;
/**
* 根据name获得后台用户
* @param name
* @param page
* @param size
* @return
*/
@Override
public PageInfo getAdminUserByNameList(String name, Integer page, Integer size) {
PageHelper.startPage(page, size);
Page<Map<String, Object>> result = adminUserMapper.getAdminUserByNameList(name);
return result.toPageInfo();
}
controller
/**
* @param name
* @param page
* @param size
* @return
*/
@GetMapping("/get/user/by/name")
public ResultVO getUserByName(@RequestParam(value = "name", defaultValue = "-1") String name,
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "size", defaultValue = "10") Integer size,
HttpServletRequest request){
PageInfo adminUser = adminUserService.getAdminUserByNameList(name, page, size);
Map<String, Object> resultMap = new LinkedHashMap<>();
resultMap.put("number", adminUser.getTotal());
resultMap.put("currentPage", page);
resultMap.put("data",adminUser.getList());
return ResultVOUtil.success(resultMap);
}
其中ResultVO是自定义的返回json格式,大家可以不使用,直接返回也可以。
特殊符号
原符号 < <= > >= & ' "
替换符号 < <= > >= & ' "
例如:sql如下:
create_date_time >= #{startTime} and create_date_time <= #{endTime}