不知不觉,我在乐字节已经学了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;
/**
用户模块 接口定义
- 添加账户 添加账户记录,返回受影响的行数 添加账户记录,返回记录的主键 批量添加账户记录,返回受影响的行数
- 查询账户 查询指定用户的账户总记录数,返回记录数 查询指定账户记录详情,返回账户对象 多条件查询指定用户的账户列表,返回账户集合
- 更新账户 更新账户记录,返回受影响的行数 批量更新账户记录,返回受影响的行数
- 删除账户 删除账户记录,返回受影响的行数 批量删除账户记录,返回受影响的行数 */ 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); }