說明
嘗試做一個(gè)springboot的框架demo誓焦,不足之處,請留言指出,不勝感謝杂伟。
篇幅比較長移层,可以通過目錄來找到需要的部分代碼。
相關(guān)參考
以下是我的開發(fā)環(huán)境赫粥,僅做參考
開發(fā)
先做一個(gè)簡單的增刪改查,使用mybatis框架越平。之前寫過注解版的《Mybatis 簡單應(yīng)用》频蛔,這里用的是xml配置版。
數(shù)據(jù)庫
CREATE TABLE `db_laowu`.`Order` (
`OrderId` INT NOT NULL AUTO_INCREMENT,
`Code` VARCHAR(45) NOT NULL,
`Name` VARCHAR(45) NOT NULL,
`CreateTime` VARCHAR(45) NOT NULL,
`LeftAmount` VARCHAR(45) NOT NULL,
PRIMARY KEY (`OrderId`));
CREATE TABLE `db_laowu`.`OrderDetail` (
`OrderDetailId` INT NOT NULL AUTO_INCREMENT,
`OrderId` INT NOT NULL,
`ProductId` INT NOT NULL,
`ProductCount` INT NOT NULL,
PRIMARY KEY (`OrderDetailId`));
創(chuàng)建項(xiàng)目
我是用vscode創(chuàng)建的秦叛,通過命令行:
- "spring Initializr: generate a maven project" 使用springboot工具創(chuàng)建maven項(xiàng)目
- "java" 選擇語言
- "com.it_laowu.springboot-study" groupId
- "springboot-study-demo" artifactId
- "2.0.6" version晦溪,這個(gè)可以在pom里面改
依賴初步選擇了以下,有缺的后面再加:
- spring web
- lombok
- mysql
- mybatis
項(xiàng)目分層
使用vscode的話挣跋,現(xiàn)在可以先分一下層三圆,建一下文件夾,大致如下:
在這里插入圖片描述
配置
application.properties改為application.yml
spring:
application:
name: springboot-study-demo
profiles:
active: dev
server:
port: 1111
servlet:
context-path: /springboot-study-demo
新增application-dev.yml避咆,包含一些環(huán)境特有的信息舟肉,這里存放下數(shù)據(jù)庫鏈接
itlaowu:
datasource:
type: mysql
//由于是容器內(nèi)的mysql,這里用端口33060
jdbc-url: jdbc:mysql://localhost:33060/db_laowu?useUnicode=true&characterEncoding=UTF-8
username: it_laowu
password: "!Aa123456"
driver-class-name: com.mysql.jdbc.Driver
core
core/base下查库,我們存放一些基類路媚。
BaseCondition 基礎(chǔ)查詢條件,包含排序和分頁樊销,分頁放到下一章
package com.it_laowu.springbootstudy.springbootstudydemo.core.base;
import lombok.Data;
/**
* 查詢條件基類 不能直接指定sortSql磷籍,而是要校驗(yàn)安全性,再拼接sortType sortFields
*/
@Data
public abstract class BaseCondition {
private String sortType;
private String sortFields;
private String sortSql;
private int pageSize;
private int currPageIndex;
public String getSortSql() {
if (sortFields != null && !sortFields.equals("") && !sortFields.equals("null")) {
if ("asc,desc".indexOf(sortType.toLowerCase()) == -1) {
return "";
}
final Class<?> childClass = getChildClass();
for (String field : sortFields.split(",")) {
try {
// 這里可以根據(jù)字段名现柠,找到數(shù)據(jù)庫對應(yīng)的列名院领。本例簡單認(rèn)為兩者相等。
childClass.getDeclaredField(field);
} catch (NoSuchFieldException | SecurityException e) {
return "";
}
}
return String.format(" ORDER BY %s %s ", sortFields, sortType);
} else {
return "";
}
}
abstract public Class<?> getChildClass();
}
IBaseDao Dao層接口
package com.it_laowu.springbootstudy.springbootstudydemo.core.base;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface IBaseDao<Bean,Condition> {
//全部查詢够吩,可以加入分頁
public List<Bean> findAll(@Param("conditionQC") Condition conditionQC);
//該方法不對外比然,容易出錯(cuò)
Bean findOne(@Param("keyId") int keyId);
public int insert(Bean entity);
public int update(Bean entity);
public int delete(@Param("keyId") int keyId);
}
IBaseService Service接口
package com.it_laowu.springbootstudy.springbootstudydemo.core.base;
import java.util.List;
import org.apache.ibatis.annotations.Param;
public interface IBaseService<Bean,Condition> {
//內(nèi)部方法
IBaseDao<Bean,Condition> getBaseDao();
default List<Bean> findAll(@Param("conditionQC") Condition condition){
return getBaseDao().findAll(condition);
}
//內(nèi)部方法
default Bean findOne(@Param("keyId") int keyId){
return getBaseDao().findOne(keyId);
}
default int insert(Bean entity){
return getBaseDao().insert(entity);
}
default int update(Bean entity){
return getBaseDao().update(entity);
}
default int delete(@Param("keyId") int keyId){
return getBaseDao().delete(keyId);
}
}
ResultBody 用于Controller返回?cái)?shù)據(jù)
package com.it_laowu.springbootstudy.springbootstudydemo.core.base;
import lombok.Data;
@Data
public class ResultBody {
private String code;
private String message;
private String detailMessage;
public ResultBody() {
}
public ResultBody(String code, String message) {
this.code = code;
this.message = message;
}
public ResultBody(String code, String message, String detailMessage) {
this.code = code;
this.message = message;
this.detailMessage = detailMessage;
}
}
bean condition mybatis dao service impl
上面是基類,接下來實(shí)現(xiàn)Order表的相關(guān)類周循。
OrderCondition 查詢條件
package com.it_laowu.springbootstudy.springbootstudydemo.bean.condition;
import java.math.BigDecimal;
import java.sql.Timestamp;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.OrderBean;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.BaseCondition;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class OrderCondition extends BaseCondition {
private int orderId;
private String code;
private String name;
private Timestamp createTime;
private BigDecimal leftAmount;
@Override
public Class<?> getChildClass() {
return OrderBean.class;
}
}
OrderBean 完全按照數(shù)據(jù)字段翻過來强法,用于新增修改、數(shù)據(jù)校驗(yàn)
package com.it_laowu.springbootstudy.springbootstudydemo.bean;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.Timestamp;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@SuppressWarnings("serial")
public class OrderBean implements Serializable{
@NotNull
private int orderId;
@NotNull
@Size(min = 5, max = 12,message = "長度在5-12")
private String code;
@NotNull
private String name;
@NotNull
private Timestamp createTime;
@Min(value=0,message="必須大于0")
private BigDecimal leftAmount;
}
OrderDao 繼承IBaseDao的方法即可湾笛。若有擴(kuò)展饮怯,在子接口定義
package com.it_laowu.springbootstudy.springbootstudydemo.dao;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.OrderBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.OrderCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.IBaseDao;
public interface OrderDao extends IBaseDao<OrderBean,OrderCondition> {
}
IOrderService 集成IBaseService即可。若有擴(kuò)展嚎研,在子接口定義
package com.it_laowu.springbootstudy.springbootstudydemo.service;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.OrderBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.OrderCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.IBaseService;
public interface IOrderService extends IBaseService<OrderBean,OrderCondition> {
}
OrderServiceImpl 實(shí)現(xiàn)IOrderService
package com.it_laowu.springbootstudy.springbootstudydemo.service.impl;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.OrderBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.OrderCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.core.base.IBaseDao;
import com.it_laowu.springbootstudy.springbootstudydemo.dao.OrderDao;
import com.it_laowu.springbootstudy.springbootstudydemo.service.IOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderServiceImpl implements IOrderService {
@Autowired
public OrderDao orderDao;
@Override
public IBaseDao<OrderBean, OrderCondition> getBaseDao() {
return orderDao;
}
}
mybatis.xml 在resource中建個(gè)目錄 mybatisMappers
蓖墅,寫下mybatis.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">
<!-- 匹配哪個(gè)dao -->
<mapper namespace="com.it_laowu.springbootstudy.springbootstudydemo.dao.OrderDao">
<!-- 定義一個(gè)返回結(jié)構(gòu) resultMap-->
<resultMap id="orderResultMap" type="com.it_laowu.springbootstudy.springbootstudydemo.bean.OrderBean">
<result column="orderId" property="orderId"/>
<result column="code" property="code"/>
<result column="name" property="name"/>
<result column="createTime" property="createTime"/>
<result column="leftAmount" property="leftAmount"/>
</resultMap>
<select id="findAll" resultMap="orderResultMap">
select orderId,code,`name`,createTime,leftAmount
from `Order`
<where>
<if test="conditionQC.orderId != 0">
and orderId = #{conditionQC.orderId}
</if>
<if test="conditionQC.code != null and '' != conditionQC.code">
and code like concat('%',#{conditionQC.code},'%')
</if>
<if test="conditionQC.name != null and '' != conditionQC.name">
and name like concat('%',#{conditionQC.name},'%')
</if>
<if test="conditionQC.createTime != null and '' != conditionQC.createTime">
and createTime like concat('%',#{conditionQC.createTime},'%')
</if>
<if test="conditionQC.leftAmount != null and '' != conditionQC.leftAmount">
and leftAmount like concat('%',#{conditionQC.leftAmount},'%')
</if>
</where>
<choose>
<when test="conditionQC.sortSql == null">
order by orderId
</when>
<otherwise>
${conditionQC.sortSql}
</otherwise>
</choose>
</select>
<select id="findOne" resultMap="orderResultMap">
select orderId,code,`name`,createTime,leftAmount
from `Order`
where orderId = #{keyId}
</select>
<insert id="insert" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.OrderBean">
insert into `Order`(orderId,`code`,`name`,createTime,leftAmount)
values(#{orderId},#{code},#{name},#{createTime},#{leftAmount})
</insert>
<update id="update" parameterType="com.it_laowu.springbootstudy.springbootstudydemo.bean.OrderBean">
update `Order`
<set>
<if test="code!=null"> `code`=#{code}, </if>
<if test="name!=null"> `name`=#{name}, </if>
<if test="createTime!=null"> createTime=#{createTime}, </if>
<if test="leftAmount!=null"> leftAmount=#{leftAmount}, </if>
</set>
where orderId = #{orderId}
</update>
<delete id="delete" parameterType="int">
delete from `Order` where orderId = #{keyId}
</delete>
</mapper>
controller
package com.it_laowu.springbootstudy.springbootstudydemo.controller;
import java.util.List;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.OrderBean;
import com.it_laowu.springbootstudy.springbootstudydemo.bean.condition.OrderCondition;
import com.it_laowu.springbootstudy.springbootstudydemo.service.IOrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/order")
public class OrderController {
@Autowired
public IOrderService orderService;
@RequestMapping(value = "/", method = RequestMethod.GET)
public String hello() {
return "hello world:";
}
@RequestMapping(value = "/list", method = RequestMethod.GET)
public List<OrderBean> orders(OrderCondition orderCondition) {
return orderService.findAll(orderCondition);
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public OrderBean order(@PathVariable(value = "id") int id) {
return orderService.findOne(id);
}
//新增時(shí)數(shù)據(jù)校驗(yàn)
@RequestMapping(value = "/add", method = RequestMethod.POST)
public int add(@Validated @RequestBody OrderBean orderBean) {
return orderService.insert(orderBean);
}
//修改時(shí)不校驗(yàn)數(shù)據(jù),當(dāng)然高級一點(diǎn)會(huì)指定要更新哪些字段,然后同樣校驗(yàn)數(shù)據(jù)
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
public int update(@PathVariable(value = "id") int id, @RequestBody OrderBean orderBean) {
return orderService.update(orderBean.setOrderId(id));
}
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public int delete(@PathVariable(value = "id") int id) {
return orderService.delete(id);
}
}
建立代碼和xml之間的映射關(guān)系
AppConfig 主要config文件论矾,指定了Component和MybatisMapper的掃描路徑
package com.it_laowu.springbootstudy.springbootstudydemo.core.Config;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan(AppConfig.COMPONENTSCAN)
@MapperScan(basePackages = AppConfig.MAPPERSCAN)
public class AppConfig {
protected static final String COMPONENTSCAN="com.it_laowu";
protected static final String MAPPERSCAN="com.it_laowu.**.dao";
protected static final String MAPPERPATH="classpath*:mybatisMappers/**/*.mybatis.xml";
public AppConfig(){
System.out.println("AppConfig start");
}
}
DataSourceConfig教翩,Mybatis配置信息,及數(shù)據(jù)源
package com.it_laowu.springbootstudy.springbootstudydemo.core.Config;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.session.LocalCacheScope;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
@Configuration
@ConditionalOnClass(DataSource.class)
@AutoConfigureAfter(AppConfig.class)
public class DataSourceConfig {
public DataSourceConfig(){
System.out.println("DataSourceConfig start");
}
//從application-dve中讀取數(shù)據(jù)庫配置
@Autowired
private DSConfigProperties dSConfigProperties;
@Bean(name="mybatisDataSource")
@Primary
@ConfigurationProperties(prefix="itlaowu.datasource")
public DataSource dataSource() throws Exception {
return DataSourceBuilder.create().build();
}
@Bean(name = "mybatisSqlSessionFactory")
@Primary
@DependsOn(value = "springContextUtil")
public SqlSessionFactory sqlSessionFactory(@Qualifier("mybatisDataSource") DataSource dataSource) throws Exception {
final SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
// mybatis.xml路徑
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(AppConfig.MAPPERPATH));
// 配置mybatis config
bean.setConfiguration(createMybatisConfig());
// 配置數(shù)據(jù)庫
bean.setDatabaseIdProvider(new DatabaseIdProvider() {
@Override
public String getDatabaseId(final DataSource dataSource) throws SQLException {
return dSConfigProperties.getType();
}
});
return bean.getObject();
}
// mybatis配置
private org.apache.ibatis.session.Configuration createMybatisConfig() {
final org.apache.ibatis.session.Configuration config = new org.apache.ibatis.session.Configuration();
config.setCacheEnabled(true);
config.setLazyLoadingEnabled(true);
config.setLogPrefix("dao.");
config.setLocalCacheScope(LocalCacheScope.SESSION);
return config;
}
@Bean(name = "mybatisSqlSessionTemplate")
@Primary
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("mybatisSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(name = "mybatisTransactionManager")
public DataSourceTransactionManager mybatisMasterTransactionManager(@Qualifier("mybatisDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
DSConfigProperties贪壳,讀取application-dev中的數(shù)據(jù)庫配置
package com.it_laowu.springbootstudy.springbootstudydemo.core.Config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;
@Component
@ConfigurationProperties(prefix="itlaowu.datasource")
@Data
public class DSConfigProperties {
private String type;
private String jdbcUrl;
private String username;
private String password;
private String driverClassName;
}