new-Bee-Mall项目学习
首先我们先了解项目的编写顺序
数据库表—>实体层—>DAO层写接口—>Mapper层写DAO层对应的映射规则,即编写SQL语句—>Service层—>ServiceImpl—>Controller层
Service层应该既调用DAO层的接口,接收DAO层返回的数据,又要提供接口给Controller层的类来进行调用。
项目框架
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kzJnNqYf-1626693187290)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210718193011019.png)]
项目的测试账户和管理员账户:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7WfL5I7s-1626693187302)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210718193701370.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eUGFwZoJ-1626693187310)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210718195148549.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hqvOL5Ze-1626693187315)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210718195233661.png)]
启动类增加 Mapper 扫描
在启动类中添加对 Mapper 包扫描 @MapperScan,Spring Boot 启动的时候会自动加载包路径下的 Mapper 接口:
@SpringBootApplication
@MapperScan("ltd.newbee.mall.dao") //添加 @Mapper 注解
public class NewbeeMallApplication {
public static void main(String[] args) {
System.out.println("启动 Spring Boot...");
SpringApplication.run(Application.class, args);
}
}
当然也可以直接在每个 Mapper 接口上面添加 @Mapper 注解,但是如果 Mapper 接口数量较多,在每个 Mapper 加注解是挺繁琐的,建议使用扫描注解。
entity层代码分析
在进行entity层代码编写之前,我们首先将数据库的表单创建好,我们在定义数据库字段名是应该使用:word1_word2_word3的命名方式进行命名:例如:admin_user_id,这样命名的好处就是:我们在编写实体层的代码时,我们只需要在application.xml中加入开启驼峰命名的指令
#开启驼峰命名
mybatis.configuration.mapUnderscoreToCamelCase=true
即可在entity层定义变量为word1Word2Word3,例如:adminUserId,此外:我们需要注意的是,一般数据库中的变量类型为int时,我们在实体层中使用Integer,例如:private Integer adminUserId;
一般在实体层定义的参数都是用对象类型:Long,Integer,Byte等,而不是用基本类型:long、int、byte。主要原因是:对象类型可以让对象为null,而基本类型的对象不能为null,一旦我们在数据库中选择了可以为null,使用基本类型定义的参数出现null时就会报错,为了避免这种情况,我们一般都会选择用对象类型去定义参数。
此外,当我们在数据库中用tinyint定义的类型参数并且进行了注释说明时时,在entity层中使用boolean类型或者Byte类型,一般如果我们定义的参数:例如sex:只有男女两种类型,那么就可以用boolean类型去定义,例如在数据库中我们用0表示女:用1表示:男,那么在entity层中我们就用fasle表示女,true表示男。但是当我们定义的参数不止两种方式时:如animals:0:rabbit 1:dog 2:pig 时,我们则使用Byte类型去定义,这个时候0:rabbit 1:dog 2:pig。
还有就是我们在数据库中定义一个create_time时使用的类型是datetime,在entity层使用Date createTime,并且我们可以定义这个日期格式:
使用注解JsonFormat
具体格式如下:
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
此外,我们在使用toString方法时,可以利用字符串缓冲区StringBuilder进行优化。
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", adminUserId=").append(adminUserId);
sb.append(", loginUserName=").append(loginUserName);
sb.append(", loginPassword=").append(loginPassword);
sb.append(", nickName=").append(nickName);
sb.append(", locked=").append(locked);
sb.append("]");
return sb.toString();
}
主要是由于String在拼接时,尤其在含有常量值的时候会产生大量的对象垃圾。因此不使用下列方法:
@Override
public String toString() {
return "AdminUser{" +
"adminUserId=" + adminUserId +
", loginUserName='" + loginUserName + '\'' +
", loginPassword='" + loginPassword + '\'' +
", nickName='" + nickName + '\'' +
", locked=" + locked +
'}';
}
dao层代码分析
dao层对应为entity层的接口,主要定义一些接口方法,
@Param注解的作用是给参数命名,参数命名后就能根据名字得到参数值,正确的将参数传入sql语句中(一般通过#{}的方式,${}会有sql注入的问题)。
使用@Param注解
@Param(“orId”) String orId 这里orId是前端传过来的参数名,不加@Param(“orId”) 默认就是找orId, 也可以@Param(“orId”) String nb,前端传入的orId参数的值就赋值到nb中。
如果接口方法的参数只有一个,并且没有使用@Parma注解sql语句直接使用任何名称均可。
如果接口的方法有多个参数,并且没有使用@Parma注解,sql语句使用param1…paramn是不会错的。但是为了规范写法,当接口方法中有多个参数时,我们一般都会使用@Param注解
一般我们在写增删改时在dao层定义的返回类型一般为int,
例如:
int deleteByPrimaryKey(Integer carouselId);
int insert(Carousel record);
int updateByPrimaryKey(Carousel record);
用查询时,如果是查询的结果只有一个就返回类型为该类,如果查询的结果为多个返回类型就为一个List集合
例如:
Carousel selectByPrimaryKey(Integer carouselId);
AdminUser selectByPrimaryKey(Integer adminUserId);
mapper层代码分析
首先,mapper层主要对应dao层接口的xml配置文件,通过sql语句实现dao层的增删改查,select、insert、delete、update。
其中id对应dao层接口的方法名,parameterType对应dao层接口方法中传的参数的类型,resultType对应dao层接口返回参数的类型。resultMap是返回集合映射。
传给后端的的参数一般用**#{参数}**
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6OU6ESHV-1626693187320)(C:\Users\ZYH.LAPTOP-32L7159T\AppData\Roaming\Typora\typora-user-images\image-20210718101117946.png)]
<resultMap id="BaseResultMap" type="ltd.newbee.mall.entity.NewBeeMallGoods">
<id column="goods_id" jdbcType="BIGINT" property="goodsId"/>
<result column="goods_name" jdbcType="VARCHAR" property="goodsName"/>
<result column="goods_intro" jdbcType="VARCHAR" property="goodsIntro"/>
<result column="update_time" jdbcType="TIMESTAMP" property="updateTime"/>
</resultMap>
如果你搜索只是返回一个值,比如说String ,或者是int,那你直接用resultType就行了。
但是你如果是返回一个复杂的对象,就必须定义好这个对象的resultMap的result map。
1.你要获取id为123的nameString name = (String) queryForObject("getUserNameByID", id); <select id="getUserNameByID" resultType="java.lang.String"> Select name from User where id =#id# </select> 2.你要获取整个User对象User user = (User) queryForObject("getUserByID", id); <resultMap class="包.User" id="User"> <result property="id" column="ID" /> <result property="name" column="NAME" /> </resultMap> <select id="getUserByID" resultMap="User"> Select ID,NAME from User where id =#id# </select>追问但是,resultType 也可以返回一个对象 <select id="getUserNameByID" resultType="com.bean.User">Select * from User where id =#id#</select>也可以返回一个封装的对象啊这个跟resultMap是一样的效果那什么时候是用resultType解决不了的呢?只能用resultMap追答你要是反回这个对象用result type,就必须返回这个对象所有信息了,而且没有任何设置,适用用普通的完整返回。 但你用resultmap,因为resultmap,因为resultmap那段是我们自己指定的,可能指定的属性只是User的一部分,而且还可以设置默认值,这是result type做不到的:resultMap里面只定义 name<resultMap class="包.User" id="User"> <result property="name" column="NAME" /> </resultMap> <select id="getUserByID" resultMap="User"> Select NAME from User where id =#id# </select>
此外,还有一个sql标签做映射效果,
<sql id="Base_Column_List"> category_id, category_level, parent_id, category_name, category_rank, is_deleted, create_time, create_user, update_time, update_user</sql>
将里面的参数映射为Base_Column_List,
<select id="findGoodsCategoryList" parameterType="Map" resultMap="BaseResultMap"> select <include refid="Base_Column_List"/> from tb_newbee_mall_goods_category <where> <if test="categoryLevel!=null and categoryLevel!=''"> and category_level = #{categoryLevel} </if> <if test="parentId!=null and parentId!=''"> and parent_id = #{parentId} </if> and is_deleted = 0 </where> order by category_rank desc <if test="start!=null and limit!=null"> limit #{start},#{limit} </if></select>
例如其中的
<include refid="Base_Column_List"/>
就等价于
category_id, category_level, parent_id, category_name, category_rank, is_deleted, create_time, create_user, update_time, update_user
这个sql标签就是做了一个映射效果,简化了代码量。