MP实战系列(三)之实体类讲解
首先说一句,mybatis plus实在太好用了!
mybaits plus的实体类:
以我博客的用户类作为讲解
package com.blog.entity; import com.baomidou.mybatisplus.annotations.TableField; import com.baomidou.mybatisplus.annotations.TableId; import com.baomidou.mybatisplus.annotations.TableLogic; import com.baomidou.mybatisplus.annotations.TableName; import com.baomidou.mybatisplus.enums.IdType; import java.io.Serializable; import java.util.Date; /** * * * @author youcong * @email ${email} * @date 2018-04-21 15:27:01 */ @TableName("user") public class UserEntity implements Serializable { private static final long serialVersionUID = 1L; /** * 用户ID */ @TableId(type=IdType.AUTO) private Integer user_id; /** * 用户名 */ private String username; /** * 性别 */ private String sex; /** * 电话 */ private String phone; /** * 密码 */ private String password; /** * 等级 */ private Integer level; /** * 用户创建时间 */ @TableField(value="create_time") private String createTime; /** * 邮箱 */ private String email; /** * 登录标识 */ private Integer logo; /** * 设置:用户ID */ public void setUserId(Integer user_id) { this.user_id = user_id; } /** * 获取:用户ID */ public Integer getUserId() { return user_id; } /** * 设置:用户名 */ public void setUsername(String username) { this.username = username; } /** * 获取:用户名 */ public String getUsername() { return username; } /** * 设置:性别 */ public void setSex(String sex) { this.sex = sex; } /** * 获取:性别 */ public String getSex() { return sex; } /** * 设置:电话 */ public void setPhone(String phone) { this.phone = phone; } /** * 获取:电话 */ public String getPhone() { return phone; } /** * 设置:密码 */ public void setPassword(String password) { this.password = password; } /** * 获取:密码 */ public String getPassword() { return password; } /** * 设置:等级 */ public void setLevel(Integer level) { this.level = level; } /** * 获取:等级 */ public Integer getLevel() { return level; } /** * 设置:用户创建时间 */ public void setCreateTime(String createTime) { this.createTime = createTime; } /** * 获取:用户创建时间 */ public String getCreateTime() { return createTime; } /** * 设置:邮箱 */ public void setEmail(String email) { this.email = email; } /** * 获取:邮箱 */ public String getEmail() { return email; } /** * 设置:登录标识 */ public void setLogo(Integer logo) { this.logo = logo; } /** * 获取:登录标识 */ public Integer getLogo() { return logo; } }
上述的注解什么意思,为什么用,我在第一篇MP实战中提过也加以描述说过。不过,今天我还是要强调一下,@TableName该注解用英文翻译就是"表名"的意思,通常在这里面写实体对应的表名,方便映射。大家还记得hibernate吗?hibernate中有个配置文件叫:hibernater-cfg.xml,该配置文件主要配置hibernate的数据源和实体映射扫描等等,这个实体映射扫描,就是通过注解,之前是每个实体对应的xml文件,而在这个xml文件中配置实体对应表的属性。后来hibernate进行升级了,如果是每一个实体对应一个xml文件,随着项目越来越大,xml文件管理起来也是个问题,于是注解,应需而生。
大家可以参考我的关于spring+hibernate+springmvc框架搭建的例子,看其中有一个实体就是通过注解的形式映射。这个与mybatis plus实体注解也是一个意思,并无多大差异。下面贴个代码方便讲解:
package com.ssh.entity; import lombok.Data; import javax.persistence.*; /** * Created by XRog * On 2/2/2017.2:03 PM */ @Data @Entity @Table(name = "Person") public class Person { @Id @GeneratedValue private Long id; @Column(name = "created") private Long created = System.currentTimeMillis(); @Column(name = "username") private String username; @Column(name = "address") private String address; @Column(name = "phone") private String phone; @Column(name = "remark") private String remark; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Long getCreated() { return created; } public void setCreated(Long created) { this.created = created; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } }
这是hibernate的实体注解, @Data : 注解在类上, 为类提供读写属性, 此外还提供了 equals()、hashCode()、toString() 方法
@Entity 对实体注释。任何Hibernate映射对象都要有这个注释
@Table 的意思与mybatis plus中的@TableName意思是一样的
@Id和@GeneratedValue是对主键的标识,这里与mybatis plus中的@TableId意思也是一样的
@Column 意思就如其名,标识列的,在属性上打上该注解,使其与数据表中的字段名进行映射,而mybatis plus这里的@TableField虽有映射的意思,但是还有其他的意思,
比如mybatis中,为什么要用resultType?为什么要用resultMap?原因很简单,一句话即可解释,当实体属性与表的字段名一致时,自动映射,当不一致时,需手动映射。
@TableFiled在此就有这个作用
@TableName的源码如下:
/** * Copyright (c) 2011-2014, hubin (jobob@qq.com). * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * <p> * 数据库表相关 * </p> * * @author hubin * @since 2016-01-23 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface TableName { /** * <p> * 实体对应的表名 * </p> */ String value() default ""; /** * <p> * 实体映射结果集 * </p> */ String resultMap() default ""; }
这里对@Retention和@Target做简单的说明:
元数据
也叫元注解,是放在被定义的一个注解类的前面 ,是对注解一种限制。
谈下这两个: @Retention 和 @Target
@Retention :用来说明该注解类的生命周期。它有以下三个参数:
RetentionPolicy.SOURCE : 注解只保留在源文件中
RetentionPolicy.CLASS : 注解保留在class文件中,在加载到JVM虚拟机时丢弃
RetentionPolicy.RUNTIME : 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。
@Target : 用来说明该注解可以被声明在那些元素之前。
ElementType.TYPE:说明该注解只能被声明在一个类前。
ElementType.FIELD:说明该注解只能被声明在一个类的字段前。
ElementType.METHOD:说明该注解只能被声明在一个类的方法前。
ElementType.PARAMETER:说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR:说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE:说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE:说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE:说明该注解只能声明在一个包名前。
@TableId源码:
/** * Copyright (c) 2011-2014, hubin (jobob@qq.com). * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.baomidou.mybatisplus.enums.IdType; /** * <p> * 表主键标识 * </p> * * @author hubin * @since 2016-01-23 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface TableId { /** * <p> * 字段值(驼峰命名方式,该值可无) * </p> */ String value() default ""; /** * <p> * 主键ID * </p> * {@link IdType} */ IdType type() default IdType.NONE; }
@TableId中有这么几个属性,其中用的比较多的就是属Type
Type常用的枚举有:
AUTO 主键自增
ID_WORKER
INPUT 主键以输入的方式
NONE 默认一般是自增
UUID UUID方式
我个人用的比较多,一个是AUTO,另外一个是UUID,这两个能满足大多数的项目需求
@TableFied源码:
/** * Copyright (c) 2011-2014, hubin (jobob@qq.com). * <p> * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.baomidou.mybatisplus.annotations; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.baomidou.mybatisplus.enums.FieldFill; import com.baomidou.mybatisplus.enums.FieldStrategy; /** * <p> * 表字段标识 * </p> * * @author hubin sjy tantan * @since 2016-09-09 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface TableField { /** * <p> * 字段值(驼峰命名方式,该值可无) * </p> */ String value() default ""; /** * <p> * 当该Field为类对象时, 可使用#{对象.属性}来映射到数据表. * </p> * <p> * 支持:@TableField(el = "role, jdbcType=BIGINT)<br> * 支持:@TableField(el = "role, typeHandler=com.baomidou.xx.typehandler.PhoneTypeHandler") * </p> */ String el() default ""; /** * <p> * 是否为数据库表字段 * </p> * <p> * 默认 true 存在,false 不存在 * </p> */ boolean exist() default true; /** * <p> * 字段验证策略 * </p> * <p> * 默认 非 null 判断 * </p> */ FieldStrategy strategy() default FieldStrategy.NOT_NULL; /** * <p> * 字段自动填充策略 * </p> */ FieldFill fill() default FieldFill.DEFAULT; }
@TableField是比较常用的注解,有的时候觉得ajax要传实体(这个实体包含其它实体类属性),通常规范的做法是写个dto,直接传输,一来复用方便,二来简洁清楚,我们开发中通常不推荐一个实体中嵌入其他实体的属性,但是有的时候,人有点懒,需求有点变态,为了加快速度,就直接在一个实体中,写其他实体属性。
这就好比,在一个html页面中,建议写外部js和css,一来规范,二来方便管理,三来复用,不过规范通常是让人痛苦的,不过对于时常变更的项目需求而言是快乐的,因为代码规范不乱,但是对于小公司而言,有的时候为了加快项目进度,不管三七二十一,行内样子能解决,绝对不用外部,最后一个页面内置一大堆css,js,而且html标签中又嵌入一大堆css标签或者js函数等,等到要优化时,有种想骂人的冲动。
题外话就不多说了,总而言之,规范开发虽然是痛苦,但是对于未来是很有益的,坚持规范,做一个代码洁癖的人!目前我还是不符合。至少我还是与前者所说的,占了一半。深刻的体会到,代码不规范,乱七八糟带来的影响。@TableField中的exist属性就是为了当你的实体包含其它实体时,可以指定它,默认为true,需要指定为false,true的意思是该字段在数据表中存在,false是不存在,通常的话,如果你的自动生成查,查所有,而不使用setSqlSelect()方法指定具体的字段时,如果你的实体中包含其它属性,但又没有使用exist时,会出现找不到列异常。避免该异常的方法就是指定exist为false,不过开发过程中,不管怎么样建议使用setSqlSelect()方法,因为sql优化。调用该方法指明需要字段就是进行最简单的sql优化。一个个简单的sql优化,对于一个系统还是很有威力的。
当然使用@TableField的场景是当你的实体属性与数据表不一致时,进行手动映射,如果一致,就会自动映射。