我在乐字节学习的第十三天

127 阅读11分钟

不知不觉,我在乐字节已经学了13天了,什么时候我能变成大手呢,唉还是一步步来吧,把基础打好~ 今天学的是Spring JDBC 主要内容 Spring 整合 JDBC 环境 ​ Spring 框架除了提供 IOC 与 AOP 核心功能外,同样提供了基于JDBC 的数据访问功能,使得访问持久层数据更加方便。使用 Spring JDBC 环境,首先需要一套 Spring 整合 JDBC 的环境。

添加依赖坐标

org.springframework spring-context 5.2.4.RELEASE org.springframework spring-test 5.2.4.RELEASE test org.aspectj aspectjweaver 1.9.5 org.springframework spring-jdbc 5.2.4.RELEASE org.springframework spring-tx 5.2.4.RELEASE mysql mysql-connector-java 8.0.19 com.mchange c3p0 0.9.5.5 添加 jdbc 配置文件 在src/main/resources目录下新建jdbc.properties配置文件,并设置对应的配置信息 驱动名 jdbc.driver=com.mysql.cj.jdbc.Driver

数据库连接 jdbc.url=jdbc:mysql://localhost:3306/(数据库名称)?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false

数据库用户名称 jdbc.user=(数据库账号)

数据库用户密码 jdbc.password=(数据库密码) 以下为可选配置

指定连接池的初始化连接数。取值应在minPoolSize 与 maxPoolSize 之间.Default:3 initialPoolSize=20

指定连接池中保留的最大连接数. Default:15 maxPoolSize=100

指定连接池中保留的最小连接数 minPoolSize=10

最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。 Default:0 maxIdleTime=600

当连接池中的连接耗尽的时候c3p0一次同时获取的连接数. Default:3 acquireIncrement=5

JDBC的标准,用以控制数据源内加载的PreparedStatements数量。 maxStatements=5

每60秒检查所有连接池中的空闲连接.Default:0 idleConnectionTestPeriod=60 修改 spring 配置文件

<context:property-placeholder location=“jdbc.properties” /> spring.xml

<context:component-scan base-package="com.xxxx" />

<context:property-placeholder location="jdbc.properties" /> 配置数据源​ 由于建立数据库连接是一个非常耗时耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后再放回去。 C3P0 与 DBCP 二选一即可

​ DBCP(DataBase connection pool),数据库连接池。是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件。单独使用dbcp需要2个包:commons-dbcp.jar,commons-pool.jar dbcp,没有自动回收空闲连接的功能。

​ C3P0是一个开源的JDBC连接池,它实现了数据源,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。c3p0有自动回收空闲连接功能。

C3P0 数据源配置

C3P0 其他额外配置(对应的值在jdbc.properties文件中指定) DBCP 数据源配置 模板类配置​ Spring把 JDBC 中重复的操作建立成了一个模板类:org.springframework.jdbc.core.JdbcTemplate 。 JDBC 测试 创建指定数据库 选择连接,右键选择"新建数据库",设置数据库的名称和编码格式 创建数据表 使用 JUnit 测试 通过 junit 测试 jdbcTemplate bean 是否获取到

JUnit 测试 public class SpringJdbcTest01 {

@Test public void testQueryCount() { // 获取spring上下文环境 ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); // 得到模板类 JdbcTemplate对象 JdbcTemplate jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");

// 定义sql语句
String sql = "select count(1) from tb_account";
// 执行查询操作(无参数)
Integer total= jdbcTemplate.queryForObject(sql, Integer.class);
System.out.println("总记录数:" + total);

}

@Test public void testQueryCountByUserId() { // 获取spring上下文环境 ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml"); // 得到模板类 JdbcTemplate对象 JdbcTemplate jdbcTemplate = (JdbcTemplate) ctx.getBean("jdbcTemplate");

// 定义sql语句
String sql = " select count(1) from tb_account where user_id = ?";
// 执行查询操作(有参数)
Integer total = jdbcTemplate.queryForObject(sql, Integer.class, 1);
System.out.println("总记录数:" + total);

} } 简单封装 public class SpringJdbcTest02 {

private JdbcTemplate jdbcTemplate;

@Before public void init() { // 得到Spring上下文环境 ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml"); // 得到模板类 JdbcTemplate对象 jdbcTemplate = (JdbcTemplate) ac.getBean("jdbcTemplate"); }

@Test public void testQueryCount() { // 定义sql语句 String sql = "select count(1) from tb_account"; // 执行查询操作(无参数) Integer total= jdbcTemplate.queryForObject(sql, Integer.class); System.out.println("总记录数:" + total); }

@Test public void testQueryCountByUserId() { // 定义sql语句 String sql = " select count(1) from tb_account where user_id = ?"; // 执行查询操作(有参数) Integer total = jdbcTemplate.queryForObject(sql, Integer.class, 1); System.out.println("总记录数:" + total); } } 注解封装 @RunWith 就是一个运行器 @RunWith(JUnit4.class) 就是指用JUnit4来运行 @RunWith(SpringJUnit4ClassRunner.class) 让测试运行于Spring测试环境 @ContextConfiguration Spring整合JUnit4测试时,使用注解引入多个配置文件 @ContextConfiguration(Locations=“classpath:applicationContext.xml”) @ContextConfiguration(locations = {“classpath:spring.xml”, “classpath:bean.xml”}) @RunWith(SpringJUnit4ClassRunner.class) // 将junit测试加到spring环境中 @ContextConfiguration(locations = {“classpath:spring.xml”}) // 设置要加载的资源文件 public class SpringJdbcTest03 {

@Resource private JdbcTemplate jdbcTemplate;

@Test public void testQueryCount() { // 定义sql语句 String sql = "select count(1) from tb_account"; // 执行查询操作(无参数) Integer total= jdbcTemplate.queryForObject(sql, Integer.class); System.out.println("总记录数:" + total); } } 通用封装 定义一个父类,设置通用的配置信息

/**

通用的测试环境,需要使用环境的直接继承类即可 */ @RunWith(SpringJUnit4ClassRunner.class) // 将junit测试加到spring环境中 @ContextConfiguration(locations = {“classpath:spring.xml”}) // 设置要加载的资源文件 public class BaseTest { } 继承通用的测试类

public class SpringJdbcTest04 extends BaseTest {

@Resource private JdbcTemplate jdbcTemplate;

@Test public void testQueryCount() { // 定义sql语句 String sql = "select count(1) from tb_account"; // 执行查询操作(无参数) Integer total= jdbcTemplate.queryForObject(sql, Integer.class); System.out.println("总记录数:" + total); }
} 持久层账户模块操作 ​ 当完成 Spring Jdbc 环境集成后,这里使用spring jdbc 完成账户单表crud 操作。

账户接口方法定义 定义实体类 Account.java

package com.xxxx.entity;

import java.util.Date;

/**

用户账户类 */ public class Account {

private Integer accountId; // 账户ID,主键 private String accountName; // 账户名称 private String accountType; // 账户类型 private Double money; // 账户金额 private String remark; // 账户备注 private Integer userId; // 用户ID,账户所属用户 private Date createTime; // 创建时间 private Date updateTime; // 修改时间

public Account() {

}

public Account(String accountName, String accountType, Double money, String remark, Integer userId) { this.accountName = accountName; this.accountType = accountType; this.money = money; this.remark = remark; this.userId = userId; }

@Override public String toString() { return “Account{” + “accountId=” + accountId + “, accountName=’” + accountName + ‘’’ + “, accountType=’” + accountType + ‘’’ + “, money=” + money + “, remark=’” + remark + ‘’’ + “, userId=” + userId + “, createTime=” + createTime + “, updateTime=” + updateTime + ‘}’; }

public Integer getAccountId() { return accountId; }

public void setAccountId(Integer accountId) { this.accountId = accountId; }

public String getAccountName() { return accountName; }

public void setAccountName(String accountName) { this.accountName = accountName; }

public String getAccountType() { return accountType; }

public void setAccountType(String accountType) { this.accountType = accountType; }

public Double getMoney() { return money; }

public void setMoney(Double money) { this.money = money; }

public String getRemark() { return remark; }

public void setRemark(String remark) { this.remark = remark; }

public Integer getUserId() { return userId; }

public void setUserId(Integer userId) { this.userId = userId; }

public Date getCreateTime() { return createTime; }

public void setCreateTime(Date createTime) { this.createTime = createTime; }

public Date getUpdateTime() { return updateTime; }

public void setUpdateTime(Date updateTime) { this.updateTime = updateTime; } } 定义接口类 IAccountDao.java

package com.xxxx.dao;

import com.xxxx.entity.Account; import java.util.List;

/**

用户模块 接口定义

  1. 添加账户 添加账户记录,返回受影响的行数 添加账户记录,返回记录的主键 批量添加账户记录,返回受影响的行数
  2. 查询账户 查询指定用户的账户总记录数,返回记录数 查询指定账户记录详情,返回账户对象 多条件查询指定用户的账户列表,返回账户集合
  3. 更新账户 更新账户记录,返回受影响的行数 批量更新账户记录,返回受影响的行数
  4. 删除账户 删除账户记录,返回受影响的行数 批量删除账户记录,返回受影响的行数 */ public interface IAccountDao {

/**

  • 添加账户
  •  添加账户记录,返回受影响的行数
    
  • @param account
  • @return */ public int addAccount(Account account) ;

/**

  • 添加账户
  •  添加账户记录,返回记录的主键
    
  • @param account
  • @return */ public int addAccountHasKey(Account account);

/**

  • 添加账户
  •  批量添加账户记录,返回受影响的行数
    
  • @param accounts
  • @return */ public int addAccountBatch(List accounts);

/**

  • 查询账户
  •  查询指定用户的账户总记录数,返回记录数
    
  • @param userId
  • @return */ public int queryAccountCount(Integer userId);

/**

  • 查询账户
  •  查询指定账户记录详情,返回账户对象
    
  • @param accountId
  • @return */ public Account queryAccountById(Integer accountId);

/**

  • 查询账户
  •  多条件查询指定用户的账户列表,返回账户集合
    
  • @param userId
  • @param accountName
  • @param accountType
  • @param createTime
  • @return */ public List queryAccountsByParams(Integer userId, String accountName, String accountType, String createTime);

/**

  • 更新账户
  •  更新账户记录,返回受影响的行数
    
  • @param account
  • @return */ public int updateAccountById(Account account);

/**

  • 更新账户
  •  批量更新账户记录,返回受影响的行数
    
  • @param accounts
  • @return */ public int updateAccountBatch(List accounts);

/**

  • 删除账户
  •  删除账户记录,返回受影响的行数
    
  • @param accountId
  • @return */ public Integer deleteAccoutById(Integer accountId);

/**

  • 删除用户
  •  批量删除账户记录,返回受影响的行数
    
  • @param ids
  • @return */ public int deleteAccountBatch(Integer[] ids); } 定义接口实现类 package com.xxxx.dao.impl;

import com.xxxx.dao.IAccountDao; import com.xxxx.entity.Account; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.List;

/**

账户模块接口实现类 */ @Repository public class AccountDaoImpl implements IAccountDao {

// JdbcTemplate 模板类注入 @Resource private JdbcTemplate jdbcTemplate;

@Override public int addAccount(Account account) { return 0; }

@Override public int addAccountHasKey(Account account) { return 0; }

@Override public int addAccountBatch(List accounts) { return 0; }

@Override public int queryAccountCount(Integer userId) { return 0; }

@Override public Account queryAccountById(Integer accountId) { return null; }

@Override public List queryAccountsByParams(Integer userId, String accountName, String accountType, String createTime) { return null; }

@Override public int updateAccountById(Account account) { return 0; }

@Override public int updateAccountBatch(List accounts) { return 0; }

@Override public Integer deleteAccoutById(Integer accountId) { return null; }

@Override public int deleteAccountBatch(Integer[] ids) { return 0; } } 账户记录添加实现 ​ 在企业项目开发时,对于记录的添加可能涉及到多种添加方式,比如添加单条记录,批量添加多条记录等情况。这里对于账户记录添加方式分为三种方式:添加单条记录返回受影响行数、添加单条记录返回主键、批量添加多条记录。

添加账户记录 /**

添加单条记录,返回受影响的行数 @param account @return / @Override public int addAccount(Account account) { String sql = “insert into tb_account(account_name,account_type,money,remark,” + “user_id,create_time,update_time) values (?,?,?,?,?,now(),now())”; Object[] objs = {account.getAccountName(),account.getAccountType(), account.getMoney(),account.getRemark(),account.getUserId()}; return jdbcTemplate.update(sql,objs); } 测试方法 /*

添加账户记录,得到受影响的行数 */ @Test public void testAddAccount() { // 准备要添加的数据 Account account = new Account(“张三”,“建设银行”,100.0,“零花钱”,1);

// 调用对象的添加方法,返回受影响的行数 int row = accountDao.addAccount(account); System.out.println(“添加账户受影响的行数:” + row); } 添加记录返回主键 /**

添加单条记录,返回主键

@param account

@return */ @Override public int addAccountHasKey(Account account) { String sql = “insert into tb_account(account_name,account_type,money,remark,” + “user_id,create_time,update_time) values (?,?,?,?,?,now(),now())”; // 定义keyHolder 对象 获取记录主键值 KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcTemplate.update(connection -> { // 预编译sql语句,并设置返回主键 PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); // 设置参数 ps.setString(1,account.getAccountName()); ps.setString(2,account.getAccountType()); ps.setDouble(3,account.getMoney()); ps.setString(4,account.getRemark()); ps.setInt(5,account.getUserId()); return ps; },keyHolder); // 得到返回的主键 Integer key = keyHolder.getKey().intValue();

return key; } 测试方法

/**

添加账户记录,返回主键 */ @Test public void testAddAccountHasKey() { // 准备要添加的数据 Account account = new Account(“李四”,“招商银行”,200.0,“兼职费”,2);

// 调用对象的添加方法,返回主键 int key = accountDao.addAccountHasKey(account); System.out.println(“添加账户返回的主键:” + key); } 批量添加账户记录 /**

添加多条记录,返回受影响的行数

@param accounts

@return */ @Override public int addAccountBatch(final List accounts) { String sql = “insert into tb_account(account_name,account_type,money,remark,” + “user_id,create_time,update_time) values (?,?,?,?,?,now(),now())”; int rows = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement preparedStatement, int i) throws SQLException { // 设置参数 preparedStatement.setString(1,accounts.get(i).getAccountName()); preparedStatement.setString(2,accounts.get(i).getAccountType()); preparedStatement.setDouble(3,accounts.get(i).getMoney()); preparedStatement.setString(4,accounts.get(i).getRemark()); preparedStatement.setInt(5,accounts.get(i).getUserId()); } @Override public int getBatchSize() { return accounts.size(); } }).length;

return rows; } 测试方法

/**

批量添加数据,返回受影响的行数 */ @Test public void testAddAccountBatch() { // 准备要添加的数据 Account account = new Account(“王五”,“农业银行”,2000.0,“工资”,3); Account account2 = new Account(“赵六”,“中国银行”,280.0,“奖金”,3); Account account3 = new Account(“田七”,“工商银行”,800.0,“零花钱”,3); List accountList = new ArrayList<>(); accountList.add(account); accountList.add(account2); accountList.add(account3);

// 调用对象的添加方法,返回主键 int rows = accountDao.addAccountBatch(accountList); System.out.println(“批量添加账户受影响的行数:” + rows); } 账户记录查询实现 ​ 账户记录查询这里提供了三种查询方式,查询指定用户所有账户记录数,查询单条账户记录详情,多条件查询指定用户账户记录。

查询用户的账户总记录数 /**

查询指定用户的账户总记录数,返回记录数 @param userId @return / @Override public int queryAccountCount(Integer userId) { String sql = “select count(1) from tb_account where user_id = ?”; int count = jdbcTemplate.queryForObject(sql,Integer.class,userId); return count; } 测试方法 /*

查询用户的账户总记录数,返回总记录数 / @Test public void testQueryAccountCount(){ // 查询ID为1的用户的账户总记录数 int total = accountDao.queryAccountCount(1); System.out.println(“总记录数:” + total); } 查询指定账户记录详情 /* 查询某个账户记录详情,返回账户对象 @param accountId @return / @Override public Account queryAccountById(Integer accountId) { String sql = “select * from tb_account where account_id = ?”; Account account = jdbcTemplate.queryForObject(sql, new Object[]{accountId}, (resultSet, i) -> { Account acc = new Account(); acc.setAccountId(resultSet.getInt(“account_id”)); acc.setMoney(resultSet.getDouble(“money”)); acc.setAccountName(resultSet.getString(“account_name”)); acc.setAccountType(resultSet.getString(“account_type”)); acc.setRemark(resultSet.getString(“remark”)); acc.setCreateTime(resultSet.getDate(“create_time”)); acc.setUpdateTime(resultSet.getDate(“update_time”)); acc.setUserId(resultSet.getInt(“user_id”)); return acc; }); return account; } 测试方法 /*

查询指定账户的记录详情,返回账户对象 / @Test public void testQueryAccountById(){ // 查询ID为1的账户记录的详情 Account account = accountDao.queryAccountById(1); System.out.println(“账户详情:” + account.toString()); } 多条件查询用户账户记录 /*

多条件查询指定用户的账户列表,返回账户集合

@param userId 用户Id

@param accountName 账户名称 (模糊查询)

@param accountType 账户类型

@param createTime 账户创建时间

@return */ @Override public List queryAccountsByParams(Integer userId, String accountName, String accountType, String createTime) { String sql = "select * from tb_account where user_id = ? "; List params = new ArrayList<>(); params.add(userId);

// 判断是否有条件查询 // 如果账户名称不为空,通过账户名称模糊匹配 if (StringUtils.isNotBlank(accountName)) { sql += " and account_name like concat(’%’,?,’%’) "; params.add(accountName); } // 如果账户类型不为空,通过指定类型名称查询 if (StringUtils.isNotBlank(accountType)) { sql += " and account_type = ? "; params.add(accountType); } // 如果创建时间不为空,查询创建时间大于指定时间的账户记录 if (StringUtils.isNotBlank(createTime)) { sql += " and create_time > ? "; params.add(createTime); }

// 将集合转换成数组 Object[] objs = params.toArray();

List accountList = jdbcTemplate.query(sql, objs, (resultSet, rowNum) -> { Account acc = new Account(); acc.setAccountId(resultSet.getInt(“account_id”)); acc.setMoney(resultSet.getDouble(“money”)); acc.setAccountName(resultSet.getString(“account_name”)); acc.setAccountType(resultSet.getString(“account_type”)); acc.setRemark(resultSet.getString(“remark”)); acc.setCreateTime(resultSet.getDate(“create_time”)); acc.setUpdateTime(resultSet.getDate(“update_time”)); acc.setUserId(resultSet.getInt(“user_id”)); return acc; });

return accountList; } 测试方法

/**

多条件查询用户的账户记录,返回账户集合 */ @Test public void testQueryAccountByParams(){ // 查询用户的账户列表 List accountList = accountDao.queryAccountsByParams(3,null,null,null); // 通过指定条件查询用户的账户列表 List accountList02 = accountDao.queryAccountsByParams(3,“张”,null,null);

System.out.println(accountList.toString()); System.out.println(accountList02.toString()); } 账户记录更新实现 更新账户记录 /**

更新指定账户记录,返回受影响的行数

@param account

@return */ @Override public int updateAccountById(Account account) { String sql = "update tb_account set account_name = ?, account_type = ?, " + " money = ? ,remark = ?,user_id = ? ,update_time = now() " + " where account_id = ? "; Object[] objs = {account.getAccountName(),account.getAccountType(), account.getMoney(), account.getRemark(),account.getUserId(), account.getAccountId()}; return jdbcTemplate.update(sql,objs); } 测试方法

/**

更新指定账户记录,返回受影响的行数 / @Test public void testUpdateAccount(){ // 准备要修改的数据 Account account = new Account(“张三1”,“建设银行1”,500.0,“零花钱加倍”,1); account.setAccountId(1); int row = accountDao.updateAccountById(account); System.out.println(“修改账户返回受影响的行数:” + row); } 批量更新账户记录 /* 批量新账户记录,返回受影响的行数 @param accounts @return / @Override public int updateAccountBatch(List accounts) { String sql = "update tb_account set account_name = ?, account_type = ?, " + " money = ? ,remark = ?,user_id = ? ,update_time = now() " + " where account_id = ? "; int rows = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { // 设置参数 ps.setString(1,accounts.get(i).getAccountName()); ps.setString(2,accounts.get(i).getAccountType()); ps.setDouble(3,accounts.get(i).getMoney()); ps.setString(4,accounts.get(i).getRemark()); ps.setInt(5,accounts.get(i).getUserId()); ps.setInt(6,accounts.get(i).getAccountId()); } @Override public int getBatchSize() { return accounts.size(); } }).length; return rows; } 测试方法 /*

批量更新账户记录,返回受影响的行数 */ @Test public void testUpdateAccountBatch(){ // 准备要修改的数据 Account account = new Account(“a3”,“建设银行3”,300.0,“零花钱加倍3”,3); account.setAccountId(3); Account account2 = new Account(“a4”,“建设银行4”,400.0,“零花钱加倍4”,3); account2.setAccountId(4); List accountList = new ArrayList<>(); accountList.add(account); accountList.add(account2);

int rows = accountDao.updateAccountBatch(accountList); System.out.println(“批量修改账户记录返回受影响的行数:” + rows); } 账户记录删除实现 删除账户记录 /**

删除账户记录,返回受影响的行数

@param accountId

@return */ @Override public Integer deleteAccoutById(Integer accountId) { String sql = "delete from tb_account where account_id= ? "; Object[] objs = {accountId}; return jdbcTemplate.update(sql,objs); } 测试方法

/**

删除账户记录,返回受影响的行数 / @Test public void testDeleteAccount(){ // 删除ID为1的账户记录 int row = accountDao.deleteAccoutById(1); System.out.println(“删除账户记录返回受影响的行数:” + row); } 批量删除账户记录 /*

批量删除账户记录,返回受影响的行数

@param ids

@return */ @Override public int deleteAccountBatch(Integer[] ids) { String sql = “delete from tb_account where account_id = ?”; int row = jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { @Override public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setInt(1,ids[i]); }

@Override public int getBatchSize() { return ids.length; } }).length; return row; } 测试方法

/**

批量删除账户记录,返回受影响的行数 */ @Test public void testDeleteAccountBatch(){ // 删除多个id的账户记录 Integer[] ids = new Integer[]{2,3}; int rows = accountDao.deleteAccountBatch(ids); System.out.println(“批量删除账户记录返回受影响的行数:” + rows); }