01 mybatis

說明

嘗試做一個(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)建的秦叛,通過命令行:

  1. "spring Initializr: generate a maven project" 使用springboot工具創(chuàng)建maven項(xiàng)目
  2. "java" 選擇語言
  3. "com.it_laowu.springboot-study" groupId
  4. "springboot-study-demo" artifactId
  5. "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;
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末饱亿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子闰靴,更是在濱河造成了極大的恐慌彪笼,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,729評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蚂且,死亡現(xiàn)場離奇詭異杰扫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)膘掰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,226評論 3 399
  • 文/潘曉璐 我一進(jìn)店門章姓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人识埋,你說我怎么就攤上這事凡伊。” “怎么了窒舟?”我有些...
    開封第一講書人閱讀 169,461評論 0 362
  • 文/不壞的土叔 我叫張陵系忙,是天一觀的道長。 經(jīng)常有香客問我惠豺,道長银还,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,135評論 1 300
  • 正文 為了忘掉前任洁墙,我火速辦了婚禮蛹疯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘热监。我一直安慰自己捺弦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,130評論 6 398
  • 文/花漫 我一把揭開白布孝扛。 她就那樣靜靜地躺著列吼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苦始。 梳的紋絲不亂的頭發(fā)上寞钥,一...
    開封第一講書人閱讀 52,736評論 1 312
  • 那天,我揣著相機(jī)與錄音陌选,去河邊找鬼理郑。 笑死蹄溉,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的香浩。 我是一名探鬼主播,決...
    沈念sama閱讀 41,179評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼臼勉,長吁一口氣:“原來是場噩夢啊……” “哼邻吭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起宴霸,我...
    開封第一講書人閱讀 40,124評論 0 277
  • 序言:老撾萬榮一對情侶失蹤囱晴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后瓢谢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體畸写,經(jīng)...
    沈念sama閱讀 46,657評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,723評論 3 342
  • 正文 我和宋清朗相戀三年氓扛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了枯芬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,872評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡采郎,死狀恐怖千所,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蒜埋,我是刑警寧澤淫痰,帶...
    沈念sama閱讀 36,533評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站整份,受9級特大地震影響待错,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜烈评,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,213評論 3 336
  • 文/蒙蒙 一火俄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧讲冠,春花似錦烛占、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,700評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至德迹,卻和暖如春芽卿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胳搞。 一陣腳步聲響...
    開封第一講書人閱讀 33,819評論 1 274
  • 我被黑心中介騙來泰國打工卸例, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留称杨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,304評論 3 379
  • 正文 我出身青樓筷转,卻偏偏與公主長得像姑原,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子呜舒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,876評論 2 361

推薦閱讀更多精彩內(nèi)容