1 项目创建
创建一个SpringBoot项目即可,创建是勾选 web jpa mysql 这三个依赖就可
2 MySQL数据库连接
技巧01:如果在创建项目时勾选了MySQL和jpa相关依赖就必须进行mysql连接配置,如果不配置就会报错,错误信息如下
技巧02:连接MySQL数据是时最好设置 useSSL=false
server: port: 9999 servlet: context-path: /sell spring: datasource: driver-class-name: com.mysql.jdbc.Driver username: dev password: Dev-182838 url: jdbc:mysql://47.104.74.9/sell_demo?characterEncoding=utf-8&useSSL=false jpa: show-sql: true
3 日志配置
3.1 引入lombok依赖
引入改以来只是为了在进行编写日志输出时更加方便
3.2 编写yam配置文件或者xml配置文件
参考博文:点击前往
3.3 在类级别添加@Slf4j 注解
4 开发热部署
4.1 引入devtools依赖
<!--项目热部署相关--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> </dependency>
4.2 插件配置
4.3 其他配置
参考博文:点击前往
5 自动生成代码
5.1 导入相关依赖
<!-- Mybatis-Plus 自动生成实体类--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <version>2.0.6</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency>
5.2 编写代码生成器
package cn.xiangxu.demo.sell_demo.util.genderateCode; import com.baomidou.mybatisplus.generator.AutoGenerator; import com.baomidou.mybatisplus.generator.config.DataSourceConfig; import com.baomidou.mybatisplus.generator.config.GlobalConfig; import com.baomidou.mybatisplus.generator.config.PackageConfig; import com.baomidou.mybatisplus.generator.config.StrategyConfig; import com.baomidou.mybatisplus.generator.config.rules.DbType; import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; /** * @author 王杨帅 * @create 2018-04-21 11:37 * @desc 根据数据库表自动生成代码 **/ public class AutoGenerateCode { public static void main(String[] args) throws InterruptedException { AutoGenerator mpg = new AutoGenerator(); // 全局配置定义 GlobalConfig gc = new GlobalConfig(); gc.setOutputDir("F:\\javaProgramming\\springBoot\\testDemo\\sell_demo\\src\\main\\java\\cn\\xiangxu\\demo\\sell_demo\\util\\genderateCode"); // 设置存储路径 gc.setFileOverride(true); gc.setActiveRecord(true); // gc.setEnableCache(true);// XML 二级缓存 gc.setBaseResultMap(true);// XML ResultMap gc.setBaseColumnList(true);// XML columList gc.setAuthor("王杨帅"); // 作者信息 // 自定义文件命名,注意 %s 会自动填充表实体属性! gc.setMapperName("%sDao"); gc.setXmlName("%sMapper"); gc.setServiceName("%sService"); gc.setServiceImplName("%sServiceImpl"); gc.setControllerName("%sController"); mpg.setGlobalConfig(gc); // 设置全局配置 // 数据源配置定义 DataSourceConfig dsc = new DataSourceConfig(); dsc.setDbType(DbType.MYSQL); /*dsc.setTypeConvert(new MySqlTypeConvert(){ // 自定义数据库表字段类型转换【可选】 @Override public DbColumnType processTypeConvert(String fieldType) { System.out.println("转换类型:" + fieldType); return super.processTypeConvert(fieldType); } });*/ dsc.setDriverName("com.mysql.jdbc.Driver"); dsc.setUrl("jdbc:mysql://47.104.74.9:3306/sell_demo?useUnicode=true&characterEncoding=UTF-8&generateSimpleParameterMetadata=true&useSSL=false"); dsc.setUsername("dev"); dsc.setPassword("Dev-182838"); mpg.setDataSource(dsc); // 设置数据源 // 策略配置 StrategyConfig strategy = new StrategyConfig(); // strategy.setCapitalMode(true);// 全局大写命名 ORACLE 注意 // strategy.setTablePrefix(new String[] { "tlog_", "tsys_" });// 此处可以修改为您的表前缀 strategy.setNaming(NamingStrategy.underline_to_camel);// 表名生成策略 // strategy.setInclude(new String[] { "user" }); // 需要生成的表 // strategy.setExclude(new String[]{"test"}); // 排除生成的表 mpg.setStrategy(strategy); // 包配置 PackageConfig pc = new PackageConfig(); pc.setParent("cn.xiangxu.demo"); pc.setModuleName("sell_demo"); mpg.setPackageInfo(pc); // 执行生成 mpg.execute(); } }
5.3 参考博文
5.4 实体类相关
技巧01:利用该代码生成器生成的实体,使用的注解都是mybatis先关的,开发者必须进行手动更改
技巧02:实体类先关注解
》@Entity -> 设置该类是一个持久实体类
》@Table(name = "order_master") -> 设置数据库表名
》@Column -> 别名设置,如果数据表字段和实体类属性不一致时需要用到这个注解
》@DynamicUpdate -> 设置动态更新;更新一条记录时,更新时间如果是由数据库端进行自动更新,那么就必须在实体类上添加这个注解,否则数据库端的自动更新时间会失效;
坑01:利用JPA更新一条记录时会将该记录更新后的结果返回,但是在客户端进行更新的数据不会及时返回,还是返回上一次操作后的数据
》@Id -> 设置数据库表主键对应的实体类属性,每个持久实体类必须有一个字段拥有 @Id 注解;如果数据库表的主键是设置了自动生成还可一个添加 @GeneratedValue 来实现自动生成(即:在进行数据插入的时候如果标有 @Id 字段没有传值进来就会自动创建一个对应的值)
坑01:@GeneratedValue 注解由4中策略,这一点从源码中可以看出,其中默认是AUTO;如果使用的是MySQL数据库而且在创建数据库表时为主键这顶了自增功能就只能使用 IDENTITY 策略,否则会报错,错误信息如下;参考博文:点击前往
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) // package javax.persistence; public enum GenerationType { TABLE, SEQUENCE, IDENTITY, AUTO; private GenerationType() { } }
6 商品类目模块
6.0 数据库表
技巧01:主键使用了MySQL的自动生成,所以在创建实体类时必须制定主键生成策略为 IDENTITY
技巧02:创建时间和更新时间都设定了默认值
技巧03:更新时间设定了自动更新,如果想要这个设置生效,那么实体类就必须使用动态更新注解 @DynamicUpdate
DROP TABLE IF EXISTS `product_category`; CREATE TABLE `product_category` ( `category_id` int(11) NOT NULL AUTO_INCREMENT, `category_name` varchar(64) NOT NULL COMMENT '类目名字', `category_type` int(11) NOT 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 (`category_id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4;
6.1 实体类
各种实体的区别:点击前往
具体代码请参见码云:点击前往
package cn.xiangxu.demo.sell_demo.entity.entityPO; import com.baomidou.mybatisplus.annotations.TableField; import com.baomidou.mybatisplus.annotations.TableId; import lombok.Data; import org.hibernate.annotations.DynamicUpdate; import javax.persistence.*; import java.util.Date; /** * @author 王杨帅 * @create 2018-04-21 13:30 * @desc 商品类型实体类 **/ @Table(name = "product_category") @Data @Entity @DynamicUpdate public class ProductCategoryPO { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "category_id") private Integer categoryId; /** * 类目名字 */ @Column(name = "category_name") private String categoryName; /** * 类目编号 */ @Column(name = "category_type") private Integer categoryType; /** * 创建时间 */ @Column(name = "create_time") private Date createTime; /** * 修改时间 */ @Column(name = "update_time") private Date updateTime; public ProductCategoryPO() { } public ProductCategoryPO(String categoryName, Integer categoryType) { this.categoryName = categoryName; this.categoryType = categoryType; } }
6.2 持久层
参考博文:点击前往
package cn.xiangxu.demo.sell_demo.dao; import cn.xiangxu.demo.sell_demo.entity.entityPO.ProductCategoryPO; import org.springframework.data.jpa.repository.JpaRepository; import java.util.List; /** * @author 王杨帅 * @create 2018-04-21 13:43 * @desc 商品类目持久层接口 **/ public interface ProductCategoryDao extends JpaRepository<ProductCategoryPO, Integer> { /** 根据类目列表查询商品类目信息 */ List<ProductCategoryPO> findByCategoryTypeIn(List<Integer> categoryTypeList); }
编程技巧:利用Arrays提供的方法实例化集合;asList 方法支持多参数传入,返回对象时这些参数组成的集合
List<Integer> typeList = Arrays.asList(0,1);
6.2.1 持久层测试类
技巧01:新增记录时不用指定记录ID、创建时间、更新时间,因为他们都是由数据库自动生成
技巧01:必须提供记录ID,因为JPA的save方法时根据记录ID进行更新操作的,如果数据库中存在该记录ID就会进行更新操作,如果不存在就会进行新增操作;但是如果数据库表设定了主键自增,那么这里即使是新增操作也不会使用传入的记录ID
package cn.xiangxu.demo.sell_demo.dao; import cn.xiangxu.demo.sell_demo.entity.entityPO.ProductCategoryPO; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.List; import java.util.Optional; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class ProductCategoryDaoTest { @Autowired private ProductCategoryDao productCategoryDao; /** * 新增商品类目 * 技巧01:不用指定记录ID、创建时间、更新时间,因为他们都是由数据库自动生成 */ @Test public void create() { ProductCategoryPO productCategoryPO = new ProductCategoryPO("腌菜食品", 3); ProductCategoryPO result = productCategoryDao.save(productCategoryPO); log.info("===/" + getClass().getName() + "/===新增后返回的结果为:{}", result); Assert.assertNotEquals(null, result); } /** * 根据商品类目ID查询对应的商品类目信息 */ @Test public void findById() { Integer id = 7; Optional<ProductCategoryPO> productCategoryPOOptional = productCategoryDao.findById(id); log.info("/===" + getClass().getName() + "/findById===根据ID获取到结果为:{}", productCategoryPOOptional.get()); Assert.assertNotEquals(null, productCategoryPOOptional.get()); } /** * 更新商品类目 * 技巧01:必须提供记录ID,因为JPA的save方法时根据记录ID进行更新操作的,如果数据库中存在该记录ID就会进行更新操作,如果不存在就会进行 * 新增操作;但是如果数据库表设定了主键自增,那么这里即使是新增操作也不会使用传入的记录ID */ @Test public void update() { Optional<ProductCategoryPO> productCategoryPOOptional = productCategoryDao.findById(7); Assert.assertNotEquals(null, productCategoryPOOptional.get()); ProductCategoryPO old = productCategoryPOOptional.get(); log.info("====原始值:{}", old.toString()); old.setCategoryName("垃圾商品"); log.info("===修改值:{}", old.toString()); ProductCategoryPO result = productCategoryDao.save(old); log.info("===更新结果值:{}", result.toString()); Assert.assertNotEquals(null, result); } /** * 根据类目列表查询商品类目信息 */ @Test public void findByCategoryTypeIn() { List<Integer> typeList = Arrays.asList(0,1); List<ProductCategoryPO> productCategoryPOList = productCategoryDao.findByCategoryTypeIn(typeList); log.info("===/" + getClass().getName() + "/===获取到的数据为:{}", productCategoryPOList.toString()); Assert.assertNotEquals(0, productCategoryPOList.size()); } }
6.3 服务层
6.3.1 服务层接口
package cn.xiangxu.demo.sell_demo.service; import cn.xiangxu.demo.sell_demo.entity.entityPO.ProductCategoryPO; import java.util.List; /** * 商品类目服务层接口 * @author 王杨帅 * @since 2018-04-21 */ public interface ProductCategoryService { /** 新增商品类目信息 */ ProductCategoryPO create(ProductCategoryPO productCategoryPO); /** 更新商品类目信息 */ ProductCategoryPO update(ProductCategoryPO productCategoryPO); /** 根据商品类目类型ID查询商品类目信息 */ ProductCategoryPO findByCategoryType(Integer categoryType); /** 根据记录ID查询 */ ProductCategoryPO findById(Integer id); /** 查询所有商品类目信息 */ List<ProductCategoryPO> findAll(); /** 根据商品类目类型ID列表查询商品类目信息 */ List<ProductCategoryPO> findByCategoryTypeIn(List<Integer> categoryTypeList); }
6.3.2 服务层实现类
编程技巧01:SpringBoot项目从2.0开始后利用JPA通过ID进行查询时返回的数据类型时Optional类型
Optional常用的方法有:
get() -> 获取数据,如果Optional对象中没有数据调用get方法时就会报错
isPresent() -> 判断Optional对象是否包含数据,如果有返回true,否则返回false
ifPresent() -> 判断Optional对象是否有数据,如果有就进行一些操作;这些操作通常是利用接口或者lambda表达式实现
参考文档:点击前往
编程技巧02:如果数据库表的时间字段设置了时间自动更新,那么必须在实体类中添加 @DynamicUpdate , 而且在进行数据插入或者数据更新时设置了时间自动更新的实体类属性值最好设置成null (PS:个人觉得更新时间应该由系统产生,然后再存放到数据库中去;而不是由数据进行时间自动更新操作)
package cn.xiangxu.demo.sell_demo.service.impl; import cn.xiangxu.demo.sell_demo.dao.ProductCategoryDao; import cn.xiangxu.demo.sell_demo.entity.entityPO.ProductCategoryPO; import cn.xiangxu.demo.sell_demo.enums.ResultEnum; import cn.xiangxu.demo.sell_demo.exceptions.SellException; import cn.xiangxu.demo.sell_demo.service.ProductCategoryService; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Arrays; import java.util.List; import java.util.Optional; /** * 商品类目服务层实现类 */ @Service public class ProductCategoryServiceImpl implements ProductCategoryService { @Autowired private ProductCategoryDao productCategoryDao; @Override public ProductCategoryPO create(ProductCategoryPO productCategoryPO) { // 01 判断商品类目类型是否存在; List<ProductCategoryPO> result = productCategoryDao.findByCategoryTypeIn(Arrays.asList(productCategoryPO.getCategoryType())); // 02 如果存在就抛出异常:商品类目类型ID已经存在 if (result.size() != 0) { throw new SellException(ResultEnum.PRODUCT_CATEGORY_TYPE_IS_EXIST); } // 03 进行插入或更新操作 ProductCategoryPO createResult = productCategoryDao.save(productCategoryPO); // 04 返回新增对象 return createResult; } @Override public ProductCategoryPO update(ProductCategoryPO productCategoryPO) { // 01 判断商品类目ID是否存在;如果不存在抛出异常:商品类目不存在 ProductCategoryPO result = findById(productCategoryPO.getCategoryId()); // 02 进行更新操作 productCategoryPO.setCreateTime(result.getCreateTime()); // 获取创建时间 // productCategoryPO.setUpdateTime(result.getCreateTime()); BeanUtils.copyProperties(productCategoryPO, result); // 将更新信息复制给result,此时result的更新时间为null;只有为null时数据库才会自动更新时间,否者会按照给定的值进行更新 ProductCategoryPO updateResult = productCategoryDao.save(result); // 03 返回更新结果 return updateResult; } @Override public ProductCategoryPO findByCategoryType(Integer categoryType) { return null; } @Override public ProductCategoryPO findById(Integer id) { // 01 调用持久层方法进行查询 Optional<ProductCategoryPO> result = productCategoryDao.findById(id); // 02 判断查询结果是否为空,如果为空就抛出异常:商品类目信息不存在 if (!result.isPresent()) { throw new SellException(ResultEnum.PRODUCT_CATEGORY_IS_NULL); } // 03 返回查询结果 return result.get(); } @Override public List<ProductCategoryPO> findAll() { return productCategoryDao.findAll(); } @Override public List<ProductCategoryPO> findByCategoryTypeIn(List<Integer> categoryTypeList) { // 01 调用持久层进行查询 List<ProductCategoryPO> result = productCategoryDao.findByCategoryTypeIn(categoryTypeList); // 02 判断查询结果,如果结果为空,就抛出异常:商品类目信息不存在 if (result.size() == 0) { throw new SellException(ResultEnum.PRODUCT_CATEGORY_IS_NULL); } // 03 返回查询结果 return result; } }
6.3.3 服务层测试类
package cn.xiangxu.demo.sell_demo.service.impl; import cn.xiangxu.demo.sell_demo.entity.entityPO.ProductCategoryPO; import cn.xiangxu.demo.sell_demo.service.ProductCategoryService; import lombok.extern.slf4j.Slf4j; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.util.Arrays; import java.util.List; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @SpringBootTest @Slf4j public class ProductCategoryServiceImplTest { @Autowired private ProductCategoryService productCategoryService; @Test public void create() throws Exception { ProductCategoryPO productCategoryPO = new ProductCategoryPO("罐头类食品", 5); ProductCategoryPO result = productCategoryService.create(productCategoryPO); log.info("===/" + getClass().getName() + "/create===新增后返回结果为:{}", result); Assert.assertNotEquals(null, result); } @Test public void update() throws Exception { ProductCategoryPO productCategoryPO = new ProductCategoryPO("罐头类食品_修改02", 5); productCategoryPO.setCategoryId(16); ProductCategoryPO result = productCategoryService.update(productCategoryPO); log.info("===/" + getClass().getName() + "/update===更新后返回结果为:{}", result); Assert.assertNotEquals(null, result); } @Test public void findByCategoryType() throws Exception { } @Test public void findById() throws Exception { Integer id = 16; ProductCategoryPO result = productCategoryService.findById(id); log.info("===/" + getClass().getName() + "/findById===根据类目ID查询得到的结果为:{}", result); Assert.assertNotEquals(null, result); } @Test public void findAll() throws Exception { List<ProductCategoryPO> result = productCategoryService.findAll(); log.info("===/" + getClass().getName() + "/findAll===根据类目ID查询得到的结果为:{}", result); Assert.assertNotEquals(0, result.size()); } @Test public void findByCategoryTypeIn() throws Exception { List<ProductCategoryPO> result = productCategoryService.findByCategoryTypeIn(Arrays.asList(0,1,2)); log.info("===/" + getClass().getName() + "/findByCategoryTypeIn===根据类目ID查询得到的结果为:{}", result); Assert.assertNotEquals(0, result.size()); } }