【后端】Maven 体系(四)

Maven 体系

五、其他核心概念

1. 生命周期

1.1 作用
  • 为了让构建过程自动化完成,Maven 设定了三个生命周期,生命周期中的每一个环节对应构建过程中的一个操作。
1.2 三个生命周期
生命周期名称作用各个环节
Clean清理操作相关pre-clean
clean
post-clean
Site生成站点相关pre-site
site
post-site
deploy-site
Default主要构建过程validate
generate-sources
process-sources
generate-resources
process-resources 复制并处理资源文件,至目标目录,准备打包。
compile 编译项目 main 目录下的源代码。
process-classes
generate-test-sources
process-test-sources
generate-test-resources
process-test-resources 复制并处理资源文件,至目标测试目录。
test-compile 编译测试源代码。
process-test-classes
test 使用合适的单元测试框架运行测试。这些测试代码不会被打包或部署。
prepare-package
package 接受编译好的代码,打包成可发布的格式,如JAR。
pre-integration-test
integration-test
post-integration-test
verify
install将包安装至本地仓库,以让其它项目依赖。
deploy将最终的包复制到远程的仓库,以让其它开发人员共享;或者部署到服务器上运行(需借助插件,例如:cargo)。
1.3 特点
  • 前面三个生命周期彼此是独立的。
  • 在任何一个生命周期内部,执行任何一个具体环节的操作,都是从本周期最初的位置开始执行,直到指定的地方。(本节记住这句话就行了,其他的都不需要记)
  • Maven 之所以这么设计其实就是为了提高构建过程的自动化程度:让使用者只关心最终要干的即可,过程中的各个环节是自动执行的。

2. 插件和目标

2.1 插件
  • Maven 的核心程序仅仅负责宏观调度,不做具体工作。具体工作都是由 Maven 插件完成的。例如:编译就是由 maven-compiler-plugin-3.1.jar 插件来执行的。
2.2 目标
  • 一个插件可以对应多个目标,而每一个目标都和生命周期中的某一个环节对应。
  • Default 生命周期中有 compile 和 test-compile 两个和编译相关的环节,这两个环节对应 compile 和 test-compile 两个目标,而这两个目标都是由 maven-compiler-plugin-3.1.jar 插件来执行的。

3. 仓库

  • 本地仓库:在当前电脑上,为电脑上所有 Maven 工程服务
  • 远程仓库:需要联网
    • 局域网:我们自己搭建的 Maven 私服,例如使用 Nexus 技术。
    • Internet
      • 中央仓库
      • 镜像仓库:内容和中央仓库保持一致,但是能够分担中央仓库的负载,同时让用户能够就近访问提高下载速度,例如:Nexus aliyun
  • 建议:不要中央仓库和阿里云镜像混用,否则 jar 包来源不纯,彼此冲突。
  • 专门搜索 Maven 依赖信息的网站:https://mvnrepository.com/

六、单一架构案例

1. 创建工程,引入依赖

1.1 架构
1.1.1 架构的概念
  • 『架构』其实就是『项目的结构』,只是因为架构是一个更大的词,通常用来形容比较大规模事物的结构。
1.1.2 单一架构
  • 单一架构也叫『all-in-one』结构,就是所有代码、配置文件、各种资源都在同一个工程。
    • 一个项目包含一个工程
    • 导出一个 war 包
    • 放在一个 Tomcat 上运行
1.2 创建工程

在这里插入图片描述

1.3 引入依赖
1.3.1 搜索依赖信息的网站
  • 到哪儿找?
    • https://mvnrepository.com/
  • 怎么选择?
    • 确定技术选型:确定我们项目中要使用哪些技术
    • 到 mvnrepository 网站搜索具体技术对应的具体依赖信息
      在这里插入图片描述
    • 确定这个技术使用哪个版本的依赖
      • 考虑因素1:看是否有别的技术要求这里必须用某一个版本
      • 考虑因素2:如果没有硬性要求,那么选择较高版本或下载量大的版本
        在这里插入图片描述
        在这里插入图片描述
    • 在实际使用中检验所有依赖信息是否都正常可用
TIP

确定技术选型、组建依赖列表、项目划分模块……等等这些操作其实都属于架构设计的范畴。

项目本身所属行业的基本特点
项目具体的功能需求
项目预计访问压力程度
项目预计将来需要扩展的功能
设计项目总体的体系结构
1.3.2 持久化层所需依赖
  • mysql:mysql-connector-java:5.1.37
  • com.alibaba:druid:1.2.8
  • commons-dbutils:commons-dbutils:1.6
1.3.3 表述层所需依赖
  • javax.servlet:javax.servlet-api:3.1.0
  • org.thymeleaf:thymeleaf:3.0.11.RELEASE
1.3.4 辅助功能所需依赖
  • junit:junit:4.12
  • ch.qos.logback:logback-classic:1.2.3
1.3.5 最终完整依赖信息
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.37</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.8</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-dbutils/commons-dbutils -->
<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
    <groupId>org.thymeleaf</groupId>
    <artifactId>thymeleaf</artifactId>
    <version>3.0.11.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic -->
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
    <scope>test</scope>
</dependency>
1.4 建包
package 功能package 名称
主包com.alex.imperial.court
子包[实体类]com.alex.imperial.court.entity
子包[Servlet基类包]com.alex.imperial.court.servlet.base
子包[Servlet模块包]com.alex.imperial.court.servlet.module
子包[Service接口包]com.alex.imperial.court.service.api
子包[Service实现类包]com.alex.imperial.court.service.impl
子包[Dao接口包]com.alex.imperial.court.dao.api
子包[Dao实现类包]com.alex.imperial.court.dao.impl
子包[Filter]com.alex.imperial.court.filter
子包[异常类包]com.alex.imperial.court.exception
子包[工具类]com.alex.imperial.court.util
子包[测试类]com.alex.imperial.court.test

2. 搭建环境:持久化层

2.1 数据建模
2.1.1 物理建模
create database db_imperial_court;

use db_imperial_court;

create table t_emp
(
    emp_id         int primary key auto_increment,
    emp_name       char(100) not null,
    emp_position   char(100) not null,
    login_account  char(100) not null unique,
    login_password char(100) not null
);

insert into t_emp(emp_name, emp_position, login_account, login_password)
values ('爱新觉罗·玄烨', 'emperor', 'xiaoxuanzi1654', '25325C896624D444B2E241807DCAC98B'), # 16540504
       ('纳兰明珠', 'minister', 'brightball1635', 'A580D0EF93C22036C859E194C14CB777'),   # 16351119
       ('赫舍里·索额图', 'minister', 'tutu1636', 'E40FD7D49B8B7EF46F47407D583C3538'); # 17030921

create table t_memorials
(
    memorials_id          int primary key auto_increment,
    memorials_title       char(100)     not null,
    memorials_content     varchar(5000) not null,
    memorials_emp         int           not null,
    memorials_create_time char(100),
    feedback_time       char(100),
    feedback_content    varchar(1000),
    memorials_status      int           not null
);

insert into t_memorials(memorials_title,
                      memorials_content,
                      memorials_emp,
                      memorials_create_time,
                      feedback_time,
                      feedback_content,
                      memorials_status)
values ('浙江巡抚奏钱塘堤决口疏', '皇上啊,不好啦!钱塘江发大水啦!堤坝冲毁啦!您看这咋弄啊!', 2, '1690-05-07', null, null, 0),
       ('左都御史参鳌拜圈地疏', '皇上啊,鳌拜这厮不是东西呀!占老百姓的地哇!还打人呀!您看咋弄啊!', 3, '1690-04-14', null, null, 0),
       ('都察院劾吴三桂不臣疏', '皇上啊,不得了啦!吴三桂那孙子想造反呀!', 2, '1693-11-18', null, null, 0),
       ('兵部奏准噶尔犯境疏', '皇上啊,不得了啦!葛尔丹要打过来了呀!', 3, '1693-11-18', null, null, 0),
       ('朝鲜使臣朝拜事宜呈皇上御览', '皇上啊!朝鲜国的人要来啦!咱们请他们吃猪肉炖粉条子吧!', 2, '1680-06-11', null, null, 0),
       ('英吉利炮舰购买事宜疏', '皇上啊!英国的小船船咱们买多少啊?', 3, '1680-06-12', null, null, 0),
       ('劾杭州织造贪墨疏', '皇上啊!杭州织造有问题啊!', 2, '1680-06-13', null, null, 0),
       ('禀畅春园落成疏', '皇上啊!畅春园修好了哇!您啥时候过来看看呀!', 3, '1680-06-14', null, null, 0),
       ('请旨木兰秋狝疏', '皇上啊!秋天到啦,又该打猎啦!', 2, '1680-06-15', null, null, 0),
       ('核准西北军饷银两疏', '皇上啊!您看看这钱数算的对不对呀!', 3, '1680-06-16', null, null, 0),
       ('请旨裁撤三藩疏', '皇上啊!咱们不裁撤三藩就芭比Q了哇!', 2, '1680-06-17', null, null, 0),
       ('蒙古王公进京朝拜疏', '皇上啊!蒙古王公要来啦!咱们请他们吃猪肉炖粉条子吧!', 3, '1680-06-18', null, null, 0),
       ('礼部请旨九阿哥赐名疏', '皇上啊!您看九阿哥该叫什么名字呀?', 2, '1680-06-19', null, null, 0),
       ('户部尚书请旨告老还乡疏', '皇上啊!臣想回家养老啦!您看看啥时候给臣把俸禄结一下啊!', 3, '1680-06-20', null, null, 0),
       ('查江宁织造贪墨疏', '皇上啊!江宁织造有问题啊!', 2, '1680-06-21', null, null, 0)
       ;
2.1.2 逻辑建模
  • Emp 实体类
public class Emp {

    private Integer empId;
    private String empName;
    private String empPosition;
    private String loginAccount;
    private String loginPassword;
    
}
  • Memorials 实体类
public class Memorials {

    private Integer memorialsId;
    private String memorialsTitle;
    private String memorialsContent;
    
    // 奏折摘要数据库没有,这里是为了配合页面显示
    private String memorialsContentDigest;
    private Integer memorialsEmp;

    // 员工姓名数据库没有,这里是为了配合页面显示
    private String memorialsEmpName;
    private String memorialsCreateTime;
    private String feedbackTime;
    private String feedbackContent;
    private Integer memorialsStatus;
    
}
2.2 数据库连接信息
  • 说明:这是我们第一次用到 Maven 约定目录结构中的 resources 目录,这个目录存放各种配置文件。
    在这里插入图片描述
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.198.100:3306/db_imperial_court
username=root
password=root
initialSize=10
maxActive=20
maxWait=10000
2.3 获取数据库连接
2.3.1 创建 JDBCUtils 工具类

在这里插入图片描述

2.3.2 创建 javax.sql.DataSource 对象
/**
	功能1:从数据源获取数据库连接
	功能2:从数据源获取数据库连接后,绑定到本地线程(借助 ThreadLocal)
	功能3:释放线程时和本地线程解除绑定
*/
public class JDBCUtils {
	// 将数据源对象设置为静态属性,保证大对象的单一实例;同时保证静态方法中可以访问
	private static DataSource dataSource;
	
	// 在静态代码块中初始化数据源
	static {
	
		// 操作思路分析:
		// 从 jdbc.properties 文件中读取连接数据库的信息
		// 为了保证程序代码的可移植性,需要基于一个确定的基准来读取这个文件
		// 确定的基准:类路径的根目录。resources 目录下的内容经过构建操作中的打包操作后悔确定放在 WEB-INF/classes 目录下。
		// WEB-INF/classes 目录存放编译好的*.class字节码文件,所以这个目录我们称之为类路径。 
		// 类路径无论在本地运行还是在服务器端运行都是一个确定的基准。
		
		// 操作具体代码:
	    // 1.创建一个用于存储外部属性文件信息的Properties对象
	    Properties properties = new Properties();
	
	    // 2.使用当前类的类加载器加载外部属性文件:jdbc.properties
	    InputStream inputStream = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
	
	    try {
	
	        // 3.将外部属性文件jdbc.properties中的数据加载到properties对象中
	        properties.load(inputStream);
	
	        // 4.创建数据源对象
	        dataSource = DruidDataSourceFactory.createDataSource(properties);
	
	    } catch (Exception e) {
	        e.printStackTrace();
	    }
	
	}
}
2.3.3 创建 ThreadLocal 对象
  • 提出需求

    • 在一个方法内控制事务
      • 如果在每一个 Service 方法中都写下面代码,那么代码重复性就太高了:
    try{
    
    	// 1、获取数据库连接
    	// 重要:要保证参与事务的多个数据库操作(SQL 语句)使用的是同一个数据库连接
    	Connection conn = JDBCUtils.getConnection();
    	
    	// 2、核心操作
    	// ...
    	
    	// 3、核心操作成功结束,可以提交事务
    	conn.commit();
    
    }catch(Exception e){
    
    	// 4、核心操作抛出异常,必须回滚事务
    	conn.rollBack();
    
    }finally{
    
    	// 5、释放数据库连接
    	JDBCUtils.releaseConnection(conn);
    	
    }
    
    • 将重复代码抽取到 Filter
      • 所谓『当前请求覆盖的 Servlet 方法、Service 方法、Dao 方法』其实就是 chain.doFilter(request, response) 间接调用的方法。
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain){
    
    	try{
    
    		// 1、获取数据库连接
    		// 重要:要保证参与事务的多个数据库操作(SQL 语句)使用的是同一个数据库连接
    		Connection conn = JDBCUtils.getConnection();
            
            // 重要操作:关闭自动提交功能
            connection.setAutoCommit(false);
    		
    		// 2、核心操作:通过 chain 对象放行当前请求
    		// 这样就可以保证当前请求覆盖的 Servlet 方法、Service 方法、Dao 方法都在同一个事务中。
    		// 同时各个请求都经过这个 Filter,所以当前事务控制的代码在这里只写一遍就行了,
    		// 避免了代码的冗余。
    		chain.doFilter(request, response);
    		
    		// 3、核心操作成功结束,可以提交事务
    		conn.commit();
    
    	}catch(Exception e){
    
    		// 4、核心操作抛出异常,必须回滚事务
    		conn.rollBack();
    
    	}finally{
    
    		// 5、释放数据库连接
    		JDBCUtils.releaseConnection(conn);
    		
    	}
    
    }
    
    • 数据的跨方法传递
      • 通过 JDBCUtils 工具类获取到的 Connection 对象需要传递给 Dao 方法,让事务涉及到的所有 Dao 方法用的都是同一个 Connection 对象。
      • 但是 Connection 对象无法通过 chain.doFilter() 方法以参数的形式传递过去。
      • 所以从获取到 Connection 对象到使用 Connection 对象中间隔着很多不是我们自己声明的方法——我们无法决定它们的参数。

在这里插入图片描述

  • ThreadLocal 对象的功能
    在这里插入图片描述
    • 全类名:java.lang.ThreadLocal
    • 泛型 T:要绑定到当前线程的数据的类型
    • 具体三个主要的方法:
方法名功能
set(T value)将数据绑定到当前线程
get()从当前线程获取已绑定的数据
remove()将数据从当前线程移除
  • Java 代码
// 由于 ThreadLocal 对象需要作为绑定数据时 k-v 对中的 key,所以要保证唯一性
// 加 static 声明为静态资源即可保证唯一性
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
2.3.4 声明方法:获取数据库连接
/**
 * 工具方法:获取数据库连接并返回
 * @return
 */
public static Connection getConnection() {

    Connection connection = null;

    try {
        // 1、尝试从当前线程检查是否存在已经绑定的 Connection 对象
        connection = threadLocal.get();

        // 2、检查 Connection 对象是否为 null
        if (connection == null) {
            
            // 3、如果为 null,则从数据源获取数据库连接
            connection = dataSource.getConnection();

            // 4、获取到数据库连接后绑定到当前线程
            threadLocal.set(connection);
            
        }
    } catch (SQLException e) {
        e.printStackTrace();
        
        // 为了调用工具方法方便,编译时异常不往外抛
        // 为了不掩盖问题,捕获到的编译时异常封装为运行时异常抛出
        throw new RuntimeException(e);
    }

    return connection;
}
2.3.5 声明方法:释放数据库连接
/**
 * 释放数据库连接
 */
public static void releaseConnection(Connection connection) {

    if (connection != null) {

        try {
            // 在数据库连接池中将当前连接对象标记为空闲
            connection.close();

            // 将当前数据库连接从当前线程上移除
            threadLocal.remove();

        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

    }

}
2.3.6 初步测试

在这里插入图片描述

public class ImperialCourtTest {

    @Test
    public void testGetConnection() {

        Connection connection = JDBCUtils.getConnection();
        System.out.println("connection = " + connection);

        JDBCUtils.releaseConnection(connection);

    }

}
2.3.7 完整代码
/**
 * 功能1:从数据源获取数据库连接
 * 功能2:从数据库获取到数据库连接后,绑定到本地线程(借助 ThreadLocal)
 * 功能3:释放线程时和本地线程解除绑定
 */
public class JDBCUtils {

    // 数据源成员变量设置为静态资源,保证大对象的单例性;同时保证静态方法中可以访问
    private static DataSource dataSource;

    // 由于 ThreadLocal 对象需要作为绑定数据时 k-v 对中的 key,所以要保证唯一性
    // 加 static 声明为静态资源即可保证唯一性
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

    // 在静态代码块中初始化数据源
    static {

        try {
            // 操作思路分析:
            // 从 jdbc.properties 文件中读取连接数据库的信息
            // 为了保证程序代码的可移植性,需要基于一个确定的基准来读取这个文件
            // 确定的基准:类路径的根目录。resources 目录下的内容经过构建操作中的打包操作后会确定放在 WEB-INF/classes 目录下。
            // WEB-INF/classes 目录存放编译好的 *.class 字节码文件,所以这个目录我们就称之为类路径。
            // 类路径无论在本地运行还是在服务器端运行都是一个确定的基准。
            // 操作具体代码:
            // 1、获取当前类的类加载器
            ClassLoader classLoader = JDBCUtils.class.getClassLoader();

            // 2、通过类加载器对象从类路径根目录下读取文件
            InputStream stream = classLoader.getResourceAsStream("jdbc.properties");

            // 3、使用 Properties 类封装属性文件中的数据
            Properties properties = new Properties();
            properties.load(stream);

            // 4、根据 Properties 对象(已封装了数据库连接信息)来创建数据源对象
            dataSource = DruidDataSourceFactory.createDataSource(properties);
        } catch (Exception e) {
            e.printStackTrace();

            // 为了避免在真正抛出异常后,catch 块捕获到异常从而掩盖问题,
            // 这里将所捕获到的异常封装为运行时异常继续抛出
            throw new RuntimeException(e);
        }
    }

    /**
     * 工具方法:获取数据库连接并返回
     * @return
     */
    public static Connection getConnection() {

        Connection connection = null;

        try {
            // 1、尝试从当前线程检查是否存在已经绑定的 Connection 对象
            connection = threadLocal.get();

            // 2、检查 Connection 对象是否为 null
            if (connection == null) {

                // 3、如果为 null,则从数据源获取数据库连接
                connection = dataSource.getConnection();

                // 4、获取到数据库连接后绑定到当前线程
                threadLocal.set(connection);
            }
        } catch (SQLException e) {
            e.printStackTrace();

            // 为了调用工具方法方便,编译时异常不往外抛
            // 为了不掩盖问题,捕获到的编译时异常封装为运行时异常抛出
            throw new RuntimeException(e);
        }

        return connection;
    }

    /**
     * 释放数据库连接
     */
    public static void releaseConnection(Connection connection) {

        if (connection != null) {

            try {
                // 在数据库连接池中将当前连接对象标记为空闲
                connection.close();

                // 将当前数据库连接从当前线程上移除
                threadLocal.remove();

            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }

        }

    }

}
2.4 BaseDao

在这里插入图片描述

2.4.1 泛型的说明

在这里插入图片描述

2.4.2 创建 QueryRunner 对象
// DBUtils 工具包提供的数据库操作对象
private QueryRunner runner = new QueryRunner();
2.4.3 通用增删改方法
  • 特别说明:在 BaseDao 方法中获取数据库连接但是不做释放,因为我们要在控制事务的 Filter 中统一释放。
/**
 * 通用的增删改方法,insert、delete、update 操作都可以用这个方法
 * @param sql 执行操作的 SQL 语句
 * @param parameters SQL 语句的参数
 * @return 受影响的行数
 */
public int update(String sql, Object ... parameters) {

    try {
        Connection connection = JDBCUtils.getConnection();

        int affectedRowNumbers = runner.update(connection, sql, parameters);
        
        return affectedRowNumbers;
    } catch (SQLException e) {
        e.printStackTrace();

        // 如果真的抛出异常,则将编译时异常封装为运行时异常抛出
        new RuntimeException(e);
        
        return 0;
    }

}
2.4.4 查询单个对象
/**
 * 查询单个对象
 * @param sql 执行查询的 SQL 语句
 * @param entityClass 实体类对应的 Class 对象
 * @param parameters 传给 SQL 语句的参数
 * @return 查询到的实体类对象
 */
public T getSingleBean(String sql, Class<T> entityClass, Object ... parameters) {

    try {
        // 获取数据库连接
        Connection connection = JDBCUtils.getConnection();

        return runner.query(connection, sql, new BeanHandler<>(entityClass), parameters);

    } catch (SQLException e) {
        e.printStackTrace();

        // 如果真的抛出异常,则将编译时异常封装为运行时异常抛出
        new RuntimeException(e);
    }

    return null;
}
2.4.5 查询多个对象
/**
 * 查询返回多个对象的方法
 * @param sql 执行查询操作的 SQL 语句
 * @param entityClass 实体类的 Class 对象
 * @param parameters SQL 语句的参数
 * @return 查询结果
 */
public List<T> getBeanList(String sql, Class<T> entityClass, Object ... parameters) {
    try {
        // 获取数据库连接
        Connection connection = JDBCUtils.getConnection();

        return runner.query(connection, sql, new BeanListHandler<>(entityClass), parameters);

    } catch (SQLException e) {
        e.printStackTrace();

        // 如果真的抛出异常,则将编译时异常封装为运行时异常抛出
        new RuntimeException(e);
    }

    return null;
}
2.4.6 测试

在这里插入图片描述

private BaseDao<Emp> baseDao = new BaseDao<>();

@Test
public void testGetSingleBean() {

    String sql = "select emp_id empId,emp_name empName,emp_position empPosition,login_account loginAccount,login_password loginPassword from t_emp where emp_id=?";

    Emp emp = baseDao.getSingleBean(sql, Emp.class, 1);

    System.out.println("emp = " + emp);

}

@Test
public void testGetBeanList() {

    String sql = "select emp_id empId,emp_name empName,emp_position empPosition,login_account loginAccount,login_password loginPassword from t_emp";

    List<Emp> empList = baseDao.getBeanList(sql, Emp.class);

    for (Emp emp : empList) {
        System.out.println("emp = " + emp);
    }

}

@Test
public void testUpdate() {

    String sql = "update t_emp set emp_position=? where emp_id=?";

    String empPosition = "minister";
    String empId = "3";

    int affectedRowNumber = baseDao.update(sql, empPosition, empId);

    System.out.println("affectedRowNumber = " + affectedRowNumber);

}
2.5 子类Dao
  • 创建接口和实现类如下:
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬小帽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值