话不多说,直接上代码:
-
表结构
house:create table house ( houseId int (11), title varchar (96), price int (11), area float , room int (11), floor int (11), totalFloor int (11), watchTimes int (11), buildYear int (4), status int (4), createTime datetime , lastUpdateTime datetime , cityEnName varchar (96), regionEnName varchar (765), cover varchar (96), direction int (11), distanceToSubway int (11), parlour int (11), district varchar (96), adminId int (11), bathroom int (11), street varchar (96), houseCode text , primary key(houseId) );
house_detail:
create table house_detail ( houseDetailId int (11), description varchar (765), layoutDesc varchar (765), traffic varchar (765), roundService varchar (765), rentWay int (2), address varchar (96), subwayId int (11), subwayName varchar (96), subwayStationId int (11), subwayStationName varchar (96), houseId int (11), primary key(houseDetailId) );
house_picture:
create table house_picture ( housePictureId int (10), houseId int (11), cdnPrefix varchar (765), width int (11), height int (11), location varchar (96), path varchar (765), primary key(housePictureId) );
house_tag:
create table house_tag ( houseId int (11), housetagId int (11), tagName varchar (96), primary key(housetagId) );
-
包依赖
<dependencies> <!--thymelea模板的包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--springboot前端的基础包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--导入mysql包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.12</version> </dependency> <!--导入mybatis包--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--导入jdbc包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--导入对象工具包--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--jpa包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <!--将java对象转换成json类型数据的依赖包--> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> <!--通过注解限制属性值--> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <!-- <version>2.1.0.Final</version> --> </dependency> <!--对象转换工具包--> <dependency> <groupId>org.modelmapper</groupId> <artifactId>modelmapper</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>org.jetbrains</groupId> <artifactId>annotations</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency> </dependencies>
-
配置文件
#设置端口号 server.port=9999 #配置数据源 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/housedemo spring.datasource.username=root spring.datasource.password=123456 #指定mybatis的数据文件xml目录 mybatis.mapper-locations=classpath:mapper/*.xml #配置mybatis日志文件框架输出信息,打印到控制台 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
-
实体类
房屋实体类:package com.kejizhentan.entity; import lombok.Data; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import javax.validation.constraints.Size; import java.util.Date; import java.util.List; /** * @Author: kejizhentan * @Date: 2019/5/21 17:34 * @Description: 房屋信息 */ @Data public class House { private Long houseId;//房屋ID //NotNull:当前对象在数据封装的时候,当前带有此注解,则不能为空,如果为空,则提示message信息 @NotNull(message = "大标题不允许为空!") //Size:字段长度限制 @Size(min = 1, max = 30, message = "标题长度必须在1~30之间") private String title;//大标题 @NotNull(message = "必须填写租赁价格") @Min(value = 1) private int price;//价格 @NotNull(message = "必须填写面积") private float area;//面积 @NotNull(message = "必须填写卧室数量") @Min(value = 1, message = "非法的卧室数量") private int room;//卧室数量 @NotNull(message = "必须填写所属楼层") private int floor;//楼层 @NotNull(message = "必须填写总楼层") private int totalFloor;//总楼层 private int watchTimes;//被看次数 @NotNull(message = "必须填写建筑起始时间") @Min(value = 1900, message = "非法的建筑起始时间") private int buildYear;//建立年限 private int status;//房屋状态 0-未审核 1-审核通过 2-已出租 3-逻辑删除 private Date createTime;//创建时间 private Date lastUpdateTime;//最近数据更新时间 @NotNull(message = "必须选中一个城市") @Size(min = 1, message = "非法的城市") private String cityEnName;//城市标记缩写 如:bj @NotNull(message = "必须选中一个地区") @Size(min = 1, message = "非法的地区") private String regionEnName;//地区英文简写 如:昌平区 cpq private String cover;//封面 @NotNull(message = "必须填写房屋朝向") private int direction;//房屋朝向 private int distanceToSubway;//距地铁距离 默认-1 附近无地铁 private int parlour;//客厅数量 @NotNull(message = "必须填写小区") private String district;//所在小区 private String adminId;//所属管理员id private int bathroom; @NotNull(message = "必须填写街道") @Size(min = 1, message = "非法的街道") private String street;//街道 private String houseCode;//房本(唯一认证标识) //房屋详细信息表houseDetail中的字段(这种方式很少用,一般是用其对象映射) @Size(max = 255) private String description;//详细描述 private String layoutDesc;//户型介绍 private String traffic;//交通出行 private String roundService;//周边配套 @NotNull(message = "必须选中一个租赁方式") @Min(value = 0) @Max(value = 1) private int rentWay;//租赁方式 @NotNull(message = "详细地址不允许为空!") @Size(min = 1, max = 30, message = "详细地址长度必须在1~30之间") private String address;//详细地址 private int subwayId;//附近地铁线id private String subwayName;//附近地铁线名称 private int subwayStationId;//地铁站id private String subwayStationName;//地铁站名 private List<HousePicture> photos;//房屋图片 private List<String> tags;//房屋标签名称 private int subscribeStatus;//预约状态 1-加入待看清单 2-已预约看房时间 3-看房完成 }
房屋明细实体类:
package com.kejizhentan.entity; import lombok.Data; /** * @Author: kejizhentan * @Date: 2019/5/21 17:58 * @Description: 房屋详细信息 */ @Data public class HouseDetail { private int houseDetailId;//房屋详细ID private String description;//详细描述 private String layoutDesc;//户型介绍 private String traffic;//交通出行 private String roundService;//周边配套 private int rentWay;//租赁方式 private String address;//详细地址 private int subwayId;//附近地铁线id private String subwayName;//附近地铁线名称 private int subwayStationId;//地铁站id private String subwayStationName;//地铁站名 private Long houseId;//对应house的id }
房屋图片实体类:
package com.kejizhentan.entity; import lombok.Data; /** * @Author: kejizhentan * @Date: 2019/5/21 18:02 * @Description: 房屋图片 */ @Data public class HousePicture { private int housePictureId;//房屋图片ID private Long houseId;//所属房屋ID private String cdnPrefix;//图片路径 private int width;//宽 private int height;//高 private String location;//所属房屋位置 private String path;//文件名 }
房屋标签实体类:
package com.kejizhentan.entity; import lombok.Data; /** * @Author: kejizhentan * @Date: 2019/5/22 11:40 * @Description: 房屋标签 */ @Data public class HouseTag { private int housetagId;//标签id private Long houseId;//房屋id private String tagName;//标签名称 }
-
DAO持久化层设计
package com.kejizhentan.dao; import com.kejizhentan.entity.House; import com.kejizhentan.entity.HouseDetail; import com.kejizhentan.entity.HousePicture; import com.kejizhentan.entity.HouseTag; import java.util.List; /** * @Author: kejizhentan * @Date: 2019/11/8 11:04 * @Description: 房屋信息---持久化层 */ public interface HouseDAO { /** * @Description: 保存房屋信息 * @Author: kejizhentan * @Date: 2019/11/8 11:06 * @Param: [house] * @Return: void * @Exception: */ void saveHouse(House house); /** * @Description: 保存房屋详情信息 * @Author: kejizhentan * @Date: 2019/11/8 11:55 * @Param: [houseDetail] * @Return: void * @Exception: */ void saveHouseDetail(HouseDetail houseDetail); /** * @Description: 保存房屋标签信息 * @Author: kejizhentan * @Date: 2019/11/8 14:09 * @Param: [houseTagList] * @Return: void * @Exception: */ void saveHouseTags(List<HouseTag> houseTagList); /** * @Description: 批量保存图片信息 * @Author: kejizhentan * @Date: 2019/11/8 14:26 * @Param: [housePhotos] * @Return: void * @Exception: */ void saveHousePictures(List<HousePicture> housePhotos); }
-
mapper.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.kejizhentan.dao.HouseDAO"> <!-- 保存房屋信息,加上useGeneratedKeys="true" keyColumn="houseId" keyProperty="houseId" 是的保存房屋信息之后立马将制定的keyProperty值赋值给对应的实体对象House useGeneratedKeys="true" 表示支持查询主键 keyColumn="houseId" 指定主键的名称(查询的主键字段名称) keyProperty="houseId" 指定查询到的主键值赋值给实体对象中的哪个属性 --> <insert id="saveHouse" parameterType="com.kejizhentan.entity.House" useGeneratedKeys="true" keyColumn="houseId" keyProperty="houseId"> insert into house(title,price,area,room,floor,totalFloor,watchTimes,buildYear,status, createTime,lastUpdateTime,cityEnName,regionEnName,cover,direction,distanceToSubway,parlour, district,adminId,bathroom,street,houseCode) values (#{title},#{price},#{area},#{room},#{floor},#{totalFloor},#{watchTimes},#{buildYear},#{status}, #{createTime},#{lastUpdateTime},#{cityEnName},#{regionEnName},#{cover},#{direction},#{distanceToSubway}, #{parlour},#{district},#{adminId},#{bathroom},#{street},#{houseCode}) </insert> <!--保存房屋详情信息--> <insert id="saveHouseDetail" parameterType="com.kejizhentan.entity.HouseDetail"> insert into house_detail (description,layoutDesc,traffic,roundService,rentWay,address,subwayId, subwayName,subwayStationId,subwayStationName,houseId) values (#{description},#{layoutDesc},#{traffic},#{roundService},#{rentWay},#{address},#{subwayId}, #{subwayName},#{subwayStationId},#{subwayStationName},#{houseId}) </insert> <!--保存房屋标签信息--> <insert id="saveHouseTags" parameterType="java.util.List"> insert into house_tag (houseId,tagName) values <foreach collection="list" item="tag" separator="," > (#{tag.houseId},#{tag.tagName}) </foreach> </insert> <!--批量保存图片信息--> <insert id="saveHousePictures" parameterType="java.util.List"> insert into house_picture (houseId,cdnPrefix,width,height,location,path) values <foreach collection="list" item="pri" separator=","> (#{pri.houseId},#{pri.cdnPrefix},#{pri.width},#{pri.height},#{pri.location},#{pri.path}) </foreach> </insert> </mapper>
-
服务层接口
package com.kejizhentan.service; import com.kejizhentan.entity.House; import com.kejizhentan.utils.ServiceResult; /** * @Author: kejizhentan * @Date: 2019/11/8 10:51 * @Description: 房屋信息接口---服务层 */ public interface IHouseService { /** * @Description: 保存房屋信息 * @Author: kejizhentan * @Date: 2019/11/8 10:57 * @Param: [house] * @Return: com.xdl.utils.ServiceResult * @Exception: */ ServiceResult saveHouse(House house); }
-
服务层接口实现类
package com.kejizhentan.service.impl; import com.kejizhentan.dao.HouseDAO; import com.kejizhentan.entity.House; import com.kejizhentan.entity.HouseDetail; import com.kejizhentan.entity.HousePicture; import com.kejizhentan.entity.HouseTag; import com.kejizhentan.service.IHouseService; import com.kejizhentan.utils.ServiceResult; import com.kejizhentan.utils.UserInfoUtil; import org.modelmapper.ModelMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.*; @Service @Transactional(rollbackFor = Exception.class)//数据库增删改操作都得加上事务 public class HouseServiceImpl implements IHouseService { @Autowired HouseDAO houseDAO; @Autowired ModelMapper modelMapper; @Value("${qiniu.cdnprefix}") String cdnprefix; @Override public ServiceResult saveHouse(House house) { //保存房屋信息--------house----------1 house.setCreateTime(new Date()); house.setLastUpdateTime(new Date()); house.setAdminId(String.valueOf(UserInfoUtil.getUserId()));//当前操作人的ID houseDAO.saveHouse(house); //保存房屋详情信息----house_detail---1 HouseDetail houseDetail = new HouseDetail(); //将house对象中的字段,按照名称匹配到houseDetail中 //modelMapper:在进行对象映射的时候 /** *参数1:源 *参数2:赋值的对象 * 要求:对象中的所有的属性,都需要去源对象中进行寻找,houseDetail中有一个主键ID,但是这个主键ID,在House中不存在 */ modelMapper.getConfiguration().setAmbiguityIgnored(true);//屏蔽不存在的字段 modelMapper.map(house, houseDetail); //保存房屋house之后,如何将主键返回并赋值到house中 houseDetail.setHouseId(house.getHouseId()); houseDAO.saveHouseDetail(houseDetail); //保存房屋标签信息----house_tag------n List<String> houseTags = house.getTags();//标签名称 if(houseTags != null && houseTags.size() > 0){ List<HouseTag> houseTagList = new ArrayList<>(); for (String tag : houseTags) { HouseTag houseTag = new HouseTag(); houseTag.setTagName(tag); houseTag.setHouseId(house.getHouseId()); houseTagList.add(houseTag); } houseDAO.saveHouseTags(houseTagList); } /** *mapper对应的xml文件中: * 需求:进行批量操作的时候,参数只能接收以下几种类型: * List----collection=list * ArrayList ----collection=list/arrayList * Map------collection=map */ //保存房屋图片信息----house_picture--n List<HousePicture> housePhotos = house.getPhotos(); if(housePhotos != null && housePhotos.size() > 0){ for (HousePicture picture : housePhotos) { picture.setHouseId(house.getHouseId()); picture.setCdnPrefix(cdnprefix); } houseDAO.saveHousePictures(housePhotos); } return new ServiceResult(true); } }
-
获取用户信息的工具类
package com.kejizhentan.utils; import com.kejizhentan.entity.User; import org.springframework.security.core.context.SecurityContextHolder; /** * @Author: kejizhentan * @Date: 2019/11/8 11:24 * @Description: 用户信息工具类 */ public class UserInfoUtil { /** * @Description: 获取用户主键ID * @Author: kejizhentan * @Date: 2019/11/8 11:25 * @Param: [] * @Return: java.lang.Long * @Exception: */ public static Long getUserId(){ return getUser().getUserId(); } /** * @Description: 自定义方法获取用户对象信息 * @Author: kejizhentan * @Date: 2019/11/8 11:27 * @Param: [] * @Return: com.xdl.entity.User * @Exception: */ public static User getUser(){ //getAuthorities:权限信息 //getCredentials:认证信息(证书信息) //getPrincipal:用户信息 Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal(); if(principal instanceof User){ return (User) principal; } return null; } }
-
将ModelMappe对象放入容器的配置类
package com.kejizhentan.utils; import org.modelmapper.ModelMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * @Description: ModelMapper 对象的配置类,将ModelMapper 对象放入容器中 * @Author: kejizhentan * @CreateDate: 2021/2/13 20:22 * @UpdateUser: kejizhentan * @UpdateDate: 2021/2/13 20:22 * @UpdateRemark: 无 * @Version: 1.0 */ @Configuration public class ModelMapperConfig { @Bean public ModelMapper modelMapperConfig(){ return new ModelMapper(); } }
-
写操作自定义返回值的封装
package com.kejizhentan.utils; import lombok.Data; /** * @Author: kejizhentan * @Date: 2019/11/8 10:55 * @Description: 接口的返回值封装对象---只应用于写操作 */ @Data public class ServiceResult<T> { private boolean success; private String message; private T result; public ServiceResult(boolean success) { this.success = success; } public ServiceResult(boolean success, String message) { this.success = success; this.message = message; } public ServiceResult(boolean success, String message, T result) { this.success = success; this.message = message; this.result = result; } }
package com.kejizhentan.utils; import lombok.Data; import lombok.Getter; import lombok.Setter; /** * @Description: 前端状态返回值的枚举类(用于封装状态码即对应的信息) * @Author: kejizhentan * @CreateDate: 2021/2/12 18:41 * @UpdateUser: kejizhentan * @UpdateDate: 2021/2/12 18:41 * @UpdateRemark: * @Version: 1.0 */ public enum StatusEnum { SUCCESS(200,"success"), PARAMS_NULL(10000, "params is null"), RESULR_BAD(1002,"result is bad"), RESULT_NULL(10001, "result is null"); @Getter @Setter private int code; @Getter @Setter private String message; StatusEnum(int code, String message) { this.code = code; this.message = message; } }
package com.kejizhentan.utils; import lombok.Getter; import lombok.Setter; /** * @Description: 接口返回前端通用JSON格式 * @Author: kejizhentan * @CreateDate: 2021/2/12 18:57 * @UpdateUser: kejizhentan * @UpdateDate: 2021/2/12 18:57 * @UpdateRemark: 无 * @Version: 1.0 */ public class ApiResponse{ @Getter @Setter private int code; //状态码 @Getter @Setter private String message; //状态码 @Getter @Setter private Object data; //返回结果 public ApiResponse(int code, String message, Object data) { this.code = code; this.message = message; this.data = data; } /** * @author kejizhentan * @params Object data * @return ApiResponse * @date 2021/2/12 21:15 * @Description 返回前端成功的API */ public static ApiResponse ofSuccessMessage(Object data){ return new ApiResponse(StatusEnum.SUCCESS.getCode(),StatusEnum.SUCCESS.getMessage(),data); } /** * @author kejizhentan * @params Object data * @return ApiResponse * @date 2021/2/12 21:15 * @Description 返回前端失败的API */ public static ApiResponse ofErrorMessage(int code,String message){ return new ApiResponse(code,message,null); } }
-
控制器层
/** * @Description: 保存房屋信息 * @Author: kejizhentan * @Date: 2019/11/8 10:45 * @Param: [house, bindingResult] * @Return: com.xdl.utils.ApiResponse * @Exception: * * BindingResult: * 是将请求中的参数提示信息进行匹配,如果正确则取消校验,如果错误,则会提示message(注解)信息 * * */ @Autowired IHouseService houseService; @PostMapping("/admin/houseAdd") public @ResponseBody ApiResponse addHouse(House house, BindingResult bindingResult){ //字符校验信息提示 if(bindingResult.hasErrors()){ return ApiResponse.ofErrorMessage(HttpStatus.BAD_REQUEST.value(), bindingResult.getAllErrors().toString()); } //保存房屋信息 ServiceResult result = houseService.saveHouse(house); //保存成功,则返回成功标识 if(result.isSuccess()){ return ApiResponse.ofSuccessMessage(null); } //保存失败,则提示错误信息 return ApiResponse.ofErrorMessage(StatusEnum.RESULR_BAD.getCode(),StatusEnum.RESULR_BAD.getMessage()); }
-
启动类
package com.kejizhentan; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @MapperScan(basePackages = {"com.kejizhentan.dao"}) public class RenthouseprojectApplication { public static void main(String[] args) { SpringApplication.run(RenthouseprojectApplication.class, args); } }
总结:
1.实体类对象中校验信息的设置和BindingResult校验信息在Controller中的使用
2.modelMapper在对象映射中的使用
3.mapper中useGeneratedKeys=“true” keyColumn=“houseId” keyProperty="houseId"的使用
4.mapper中针对mysql数据库中动态sql和批量数据保存的设计
5.写操作Service返回值一般都使用自己封装的
6.DAO中写操作的返回值一般都设计成void