Day4 Spring + Jdbc 练习:账户管理系统
需求
使用Spring实现一个基于命令行的账户管理系统。
功能点:
- 创建和删除账户;
- 账户余额查询、存入、取款;
- 用户间转账;
- 用户登录;
- 操作选项:
- 未登录时:
- 登录
- 退出
- 管理员账号:
- 退出
- 创建账户
- 删除账户
- 账户列表
- 普通用户账号:
- 退出
- 查询余额
- 取款
- 存款
- 转账
技术点:
- 使用Spring注解;
- 使用Druid数据源;
- 使用JdbcTemplate操作SQL语句;
实现
1. 创建实体类、Dao和Service
######### 数据模型:Account类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class Account {
private int id;
private String name;
private String password;
private double money;
private String role;
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
######### 数据库操作:AccountDao接口和AccountDaoImpl实现类
public interface AccountDao {
boolean create(Account account);
void update(Account account);
void delete(int id);
Account findById(int id);
Account findByName(String name);
Account accountLogin(String name, String password);
List<Account> findAll();
}
@Repository
public class AccountDaoImpl implements AccountDao {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 创建新用户
*/
@Override
public boolean create(Account account) {
String name = account.getName();
// 判断用户是否已存在
if (findByName(name) != null) {
System.out.println("账户 " + name + " 已存在。");
return false;
}
jdbcTemplate.update("insert into account(name, password, money) values(?, ?, ?)",
name, account.getPassword(), account.getMoney());
return true;
}
/**
* 更新账号余额
*/
@Override
public void update(Account account) {
jdbcTemplate.update("update account set money = ? where id = ?",
account.getMoney(), account.getId());
}
/**
* 删除用户
*/
@Override
public void delete(int id) {
jdbcTemplate.update("delete from account where id = ?", id);
}
/**
* account表字段和Account类映射关系类
*/
static class AccountRowMapping implements RowMapper<Account> {
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
int id = rs.getInt(1);
String name = rs.getString(2);
double money = rs.getDouble(3);
String role = rs.getString(4);
return new Account(id, name, "", money, role);
}
}
/**
* 根据id查找用户
*/
@Override
public Account findById(int id) {
try {
return jdbcTemplate.queryForObject("select id, name, money, role from account where id = ?",
new AccountDaoImpl.AccountRowMapping(), id);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
/**
* 根据用户名查找用户
*/
@Override
public Account findByName(String name) {
try {
return jdbcTemplate.queryForObject("select id, name, money, role from account where name = ?",
new AccountDaoImpl.AccountRowMapping(), name);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
/**
* 用户登录
*/
@Override
public Account accountLogin(String name, String password) {
try {
return jdbcTemplate.queryForObject("select id, name, money, role from account where name = ? and password = ?",
new AccountDaoImpl.AccountRowMapping(), name, password);
} catch (EmptyResultDataAccessException e) {
return null;
}
}
/**
* 返回用户列表
*/
@Override
public List<Account> findAll() {
try {
return jdbcTemplate.query("select id, name, money, role from account",
new AccountDaoImpl.AccountRowMapping());
} catch (EmptyResultDataAccessException e) {
return null;
}
}
}
######### 业务处理层:AccountService接口和AccountServiceImpl实现类
public interface AccountService {
boolean createAccount(Account account);
boolean deleteAccount(int id);
void updateAccount(Account account);
boolean saveMoney(Account account, double money);
boolean takeMoney(Account account, double money);
Account findById(int id);
Account login(String name, String password);
List<Account> list();
boolean transfer(int fromId, int toId, double money);
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
/**
* 创建账户
* @return flag 是否创建成功
*/
@Override
public boolean createAccount(Account account) {
if (account == null) {
System.out.println("非法账户。");
return false;
}
return accountDao.create(account);
}
/**
* 更新账户信息(余额)
*/
@Override
public void updateAccount(Account account) {
accountDao.update(account);
}
/**
* 存钱
*/
@Override
public boolean saveMoney(Account account, double money) {
if (account == null || money < 0) {
return false;
}
account.setMoney(account.getMoney() + money);
updateAccount(account);
return true;
}
/**
* 取钱
*/
@Override
public boolean takeMoney(Account account, double money) {
if (account == null || money < 0) {
return false;
}
if (account.getMoney() < money) {
System.out.println("余额不足");
return false;
}
account.setMoney(account.getMoney() - money);
updateAccount(account);
return true;
}
/**
* 根据id查找用户
*/
@Override
public Account findById(int id) {
return accountDao.findById(id);
}
/**
* 登录
* @return account 成功则返回账户
*/
@Override
public Account login(String name, String password) {
// 登录,并返回账户信息
return accountDao.accountLogin(name, password);
}
/**
* 删除账户
*/
@Override
public boolean deleteAccount(int id) {
if (id == 1) {
System.out.println("超级管理员账户不能删除。");
}
// 判断待账户是否存在
else if (accountDao.findById(id) == null) {
System.out.println("ID为 " + id + " 的账户不存在。");
} else {
System.out.println("ID为 " + id + " 的账户删除成功。");
accountDao.delete(id);
}
return true;
}
/**
* 查看用户列表,只有管理员才有此操作
*/
@Override
public List<Account> list() {
List<Account> accountList = accountDao.findAll();
if (accountList == null || accountList.size() == 0) {
System.out.println("没有用户。");
} else {
accountList.forEach(System.out::println);
}
return accountList;
}
/**
* 转账操作
* @param fromId 转出账户id
* @param toId 转入账户id
* @param money 转账金额
*/
@Override
public boolean transfer(int fromId, int toId, double money) {
Account fromAccount = findById(fromId);
Account toAccount = findById(toId);
if (fromAccount == null || money < 0 ||
money > fromAccount.getMoney() || toAccount == null) {
return false;
}
fromAccount.setMoney(fromAccount.getMoney() - money);
updateAccount(fromAccount);
toAccount.setMoney(toAccount.getMoney() + money);
updateAccount(toAccount);
return true;
}
}
######### 用于终端命令行操作的ClientAccountService类
- 对AccountService做了一些封装,主要添加了从终端获取用户输入的功能;
- 该类包含一个AccountService的成员属性;
/**
* 对AccountService做一些封装,主要添加了从终端获取用户输入的功能
* 该类包含一个AccountService的成员属性
*/
@Service
@Data
public class ClientAccountService {
@Autowired
private AccountService service;
/**
* 登录
* @return account 成功则返回账户
*/
public Account login() {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String name = scanner.next();
System.out.print("请输入密码:");
String password = scanner.next();
// 登录,并返回账户信息
Account account = service.login(name, password);
if (account == null) {
System.out.println("用户名或密码有误,请重新输入。");
} else {
System.out.println("登录成功,欢迎 " + account.getName() + " !");
}
return account;
}
/**
* 根据用户输入,创建账户
* @return flag 是否创建成功
*/
public boolean createAccount() {
boolean flag = true;
try {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入用户名:");
String name = scanner.next();
System.out.print("请输入密码:");
String password = scanner.next();
System.out.print("请输入初始金额:");
double money = scanner.nextDouble();
Account account = new Account(0, name, password, money, "");
flag = service.createAccount(account);
// 创建失败,返回false
if (!flag)
return false;
System.out.println("账户创建成功,账户信息:" + account);
} catch (Exception e) {
flag = false;
}
return flag;
}
/**
* 删除账户
*/
public boolean deleteAccount() {
boolean flag = true;
try {
Scanner scanner = new Scanner(System.in);
System.out.print("请输入待删除的账户ID:");
int id = scanner.nextInt();
service.deleteAccount(id);
} catch (Exception e) {
flag = false;
}
return flag;
}
/**
* 存钱
*/
public boolean saveMoney(Account account) {
boolean flag = true;
Scanner scanner = new Scanner(System.in);
System.out.print("请输入存款金额:");
try {
double inputMoney = scanner.nextDouble();
flag = service.saveMoney(account, inputMoney);
if (flag)
System.out.println("成功存入" + inputMoney + ",当前余额为:" + account.getMoney());
} catch (Exception e) {
flag = false;
}
return flag;
}
/**
* 取钱
*/
public boolean takeMoney(Account account) {
boolean flag = true;
Scanner scanner = new Scanner(System.in);
System.out.print("请输入取款金额:");
try {
double inputMoney = scanner.nextDouble();
flag = service.takeMoney(account, inputMoney);
if (flag)
System.out.println("成功取款" + inputMoney + ",当前余额为:" + account.getMoney());
} catch (Exception e) {
flag = false;
}
return flag;
}
/**
* 转账操作
*/
public boolean transfer(Account account) {
boolean flag = true;
Scanner scanner = new Scanner(System.in);
System.out.print("请输入转出金额:");
try {
double inputMoney = scanner.nextDouble();
// 判断余额是否充足
if (inputMoney > account.getMoney()) {
System.out.println("余额不足,当前余额为:" + account.getMoney());
return false;
}
System.out.print("请输入转账账户ID:");
int toId = scanner.nextInt();
// 判断转出账户是否存在
if (service.findById(toId) == null) {
System.out.println("ID为:" + toId + "的账户不存在。");
return false;
}
// 转账,更新数据库信息
flag = service.transfer(account.getId(), toId, inputMoney);
if (flag) {
// 更新余额
account.setMoney(account.getMoney() - inputMoney);
System.out.println("转账成功,当前余额为:" + account.getMoney());
}
} catch (Exception e) {
flag = false;
}
return flag;
}
}
2. 创建数据库表
添加一个账户表:account
create table account
(
id int auto_increment primary key,
name varchar(64) not null,
password varchar(64) not null,
money double default 0 null,
role varchar(32) default '普通用户' null,
constraint account_name_uindex
unique (name)
) comment '账户信息表';
INSERT INTO test.account (id, name, password, money, role) VALUES (1, 'admin', 'admin', 0, '管理员');
3. 创建数据源
- jdbc配置文件:jdbc.properties
jdbc.jdbcUrl=jdbc:mysql://localhost:3306/test?serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=123456
jdbc.driverClass=com.mysql.cj.jdbc.Driver
- 配置类
@Configuration // 表示当前类为一个配置类
@PropertySource("com/bailiban./day4/account_system/config/jdbc.properties") // 设置配置文件路径
public class JdbcConfig {
// 从jdbc配置文件获取数据库连接属性
@Value("${jdbc.jdbcUrl}")
private String jdbcUrl;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Value("${jdbc.driverClass}")
private String driverClass;
/**
* 创建数据源bean
*/
@Bean("dataSource")
public DataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(jdbcUrl);
dataSource.setUsername(username);
dataSource.setPassword(password);
dataSource.setDriverClassName(driverClass);
return dataSource;
}
/**
* 创建JdbcTemplate,用于执行SQL语句;
* @param dataSource 数据源参数,自动获取 @Bean("dataSource")
*/
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
4. 编写客户端类
成员变量:
@Autowired
private ClientAccountService service;
private boolean isLogin = false;
private Account account = null;
菜单选项
/**
* 创建操作选项,分三种情况;
* - 未登录时:
* 1. 登录
* 2. 退出
* - 管理员账号:
* 1. 退出
* 2. 创建账户
* 3. 删除账户
* 4. 账户列表
* - 普通用户账号:
* 1. 退出
* 2. 查询余额
* 3. 取款
* 4. 存款
* 5. 转账
*/
private String getClientInfo() {
return (!isLogin ? "******欢迎使用BLB账户管理系统,请选择:******\r\n" :
"-----------------------------------------\r\n请选择:\r\n") +
(!isLogin ? "\t1. 登录 \r\n\t2. 退出 \r\n" : "\t1. 退出\r\n") +
(!isLogin ? "" : account.getRole().equals("管理员") ?
("\t2. 创建账户\r\n" + "\t3. 删除账户\r\n" + "\t4. 账户列表\r\n") :
("\t2. 查询余额\r\n" + "\t3. 取款\r\n" + "\t4. 存款\r\n" +
"\t5. 转账\r\n")) +
"-----------------------------------------\r\n>> ";
}
主方法
/**
* 账户管理系统运行入口函数
*/
public void run() {
// while循环,用户可以重复操作
while (true) {
boolean flag = true;
Scanner scanner = new Scanner(System.in);
// 打印操作选项
System.out.print(getClientInfo());
try {
// 从终端输入获取操作选项
int step = scanner.nextInt();
// 判断是否为退出操作
if (!isQuit(step)) {
// 登录系统
if (!isLogin && step == 1) {
login();
} else {
// 根据用户权限,执行管理员操作,或者普通用户操作
flag = account.getRole().equals("管理员") ? doAdmin(step) : doNormal(step);
}
} else {
// 退出系统
System.out.println("~~Bye~~");
return;
}
} catch (Exception e) {
flag = false;
}
if (!flag) {
System.out.println("输入有误。");
}
}
}
管理员操作
/**
* 管理员操作
* @param step 操作选项
* @return flag 操作是否成功
*/
private boolean doAdmin(int step) {
boolean flag = true;
switch (step) {
case 2: // 2. 创建账户
flag = service.createAccount();
break;
case 3: // 3. 删除账户
flag = service.deleteAccount();
break;
case 4: // 4. 账户列表
service.getService().list();
break;
default: // 其他输入为非法选项
flag = false;
}
return flag;
}
普通用户操作
/**
* 普通用户操作
* @param step 操作选项
* @return flag 操作是否成功
*/
private boolean doNormal(int step) {
boolean flag = true;
Scanner scanner = new Scanner(System.in);
double inputMoney = 0; // 定义用户输入的金额
try {
switch (step) {
case 2: // 2. 查询余额
System.out.println("账户余额为:" + account.getMoney());
break;
case 3: // 3. 取款
flag = service.takeMoney(account);
break;
case 4: // 4. 存款
flag = service.saveMoney(account);
break;
case 5: // 5. 转账
flag = service.transfer(account);
break;
default: // 其他输入为非法选项
flag = false;
}
} catch (Exception e) {
flag = false;
}
return flag;
}
完整类
@Component
@Data
public class Client {
@Autowired
private ClientAccountService service;
private boolean isLogin = false;
private Account account = null;
/**
* 创建操作选项,分三种情况;
* - 未登录时:
* 1. 登录
* 2. 退出
* - 管理员账号:
* 1. 退出
* 2. 创建账户
* 3. 删除账户
* 4. 账户列表
* - 普通用户账号:
* 1. 退出
* 2. 查询余额
* 3. 取款
* 4. 存款
* 5. 转账
*/
private String getClientInfo() {
return (!isLogin ? "******欢迎使用BLB账户管理系统,请选择:******\r\n" :
"-----------------------------------------\r\n请选择:\r\n") +
(!isLogin ? "\t1. 登录 \r\n\t2. 退出 \r\n" : "\t1. 退出\r\n") +
(!isLogin ? "" : account.getRole().equals("管理员") ?
("\t2. 创建账户\r\n" + "\t3. 删除账户\r\n" + "\t4. 账户列表\r\n") :
("\t2. 查询余额\r\n" + "\t3. 取款\r\n" + "\t4. 存款\r\n" +
"\t5. 转账\r\n")) +
"-----------------------------------------\r\n>> ";
}
/**
* 账户管理系统运行入口函数
*/
public void run() {
// while循环,用户可以重复操作
while (true) {
boolean flag = true;
Scanner scanner = new Scanner(System.in);
// 打印操作选项
System.out.print(getClientInfo());
try {
// 从终端输入获取操作选项
int step = scanner.nextInt();
// 判断是否为退出操作
if (!isQuit(step)) {
// 登录系统
if (!isLogin && step == 1) {
login();
} else {
// 根据用户权限,执行管理员操作,或者普通用户操作
flag = account.getRole().equals("管理员") ? doAdmin(step) : doNormal(step);
}
} else {
// 退出系统
System.out.println("~~Bye~~");
return;
}
} catch (Exception e) {
flag = false;
}
if (!flag) {
System.out.println("输入有误。");
}
}
}
/**
* 普通用户操作
* @param step 操作选项
* @return flag 操作是否成功
*/
private boolean doNormal(int step) {
boolean flag = true;
Scanner scanner = new Scanner(System.in);
double inputMoney = 0; // 定义用户输入的金额
try {
switch (step) {
case 2: // 2. 查询余额
System.out.println("账户余额为:" + account.getMoney());
break;
case 3: // 3. 取款
flag = service.takeMoney(account);
break;
case 4: // 4. 存款
flag = service.saveMoney(account);
break;
case 5: // 5. 转账
flag = service.transfer(account);
break;
default: // 其他输入为非法选项
flag = false;
}
} catch (Exception e) {
flag = false;
}
return flag;
}
/**
* 管理员操作
* @param step 操作选项
* @return flag 操作是否成功
*/
private boolean doAdmin(int step) {
boolean flag = true;
switch (step) {
case 2: // 2. 创建账户
flag = service.createAccount();
break;
case 3: // 3. 删除账户
flag = service.deleteAccount();
break;
case 4: // 4. 账户列表
service.getService().list();
break;
default: // 其他输入为非法选项
flag = false;
}
return flag;
}
/**
* 账户登录
* @return 是否登录成功
*/
private boolean login() {
account = service.login();
isLogin = account != null;
return isLogin;
}
/**
* 判断当前是否为退出操作
* @param step 操作选项
* @return true 退出
*/
private boolean isQuit(int step) {
// 当已登录时,step-1为退出选项;
// 当未登录时,step-2为退出选项;
return isLogin && step == 1 || !isLogin && step == 2;
}
public static void main(String[] args) {
Client client = new AnnotationConfigApplicationContext(
"com.bailiban.day4.account_system").getBean(Client.class);
client.run();
}
}
测试
管理员操作
******欢迎使用BLB账户管理系统,请选择:******
1. 登录
2. 退出
-----------------------------------------
>> 1
请输入用户名:admin
请输入密码:admin
登录成功,欢迎 admin !
-----------------------------------------
请选择:
1. 退出
2. 创建账户
3. 删除账户
4. 账户列表
-----------------------------------------
>> 4
Account{id=1, name='admin', money=0.0}
-----------------------------------------
请选择:
1. 退出
2. 创建账户
3. 删除账户
4. 账户列表
-----------------------------------------
>> 2
请输入用户名:Jim
请输入密码:Jim
请输入初始金额:1000
账户创建成功,账户信息:Account{id=0, name='Jim', money=1000.0}
-----------------------------------------
请选择:
1. 退出
2. 创建账户
3. 删除账户
4. 账户列表
-----------------------------------------
>> 2
请输入用户名:Tom
请输入密码:Tom
请输入初始金额:50000
账户创建成功,账户信息:Account{id=0, name='Tom', money=50000.0}
-----------------------------------------
请选择:
1. 退出
2. 创建账户
3. 删除账户
4. 账户列表
-----------------------------------------
>> 2
请输入用户名:Lily
请输入密码:LiLy
请输入初始金额:6000
账户创建成功,账户信息:Account{id=0, name='Lily', money=6000.0}
-----------------------------------------
请选择:
1. 退出
2. 创建账户
3. 删除账户
4. 账户列表
-----------------------------------------
>> 4
Account{id=1, name='admin', money=0.0}
Account{id=10, name='Jim', money=1000.0}
Account{id=11, name='Tom', money=50000.0}
Account{id=12, name='Lily', money=6000.0}
-----------------------------------------
请选择:
1. 退出
2. 创建账户
3. 删除账户
4. 账户列表
-----------------------------------------
>> 3
请输入待删除的账户ID:11
ID为 11 的账户删除成功。
-----------------------------------------
请选择:
1. 退出
2. 创建账户
3. 删除账户
4. 账户列表
-----------------------------------------
>> 4
Account{id=1, name='admin', money=0.0}
Account{id=10, name='Jim', money=1000.0}
Account{id=12, name='Lily', money=6000.0}
-----------------------------------------
请选择:
1. 退出
2. 创建账户
3. 删除账户
4. 账户列表
-----------------------------------------
>> 1
~~Bye~~
普通用户操作
******欢迎使用BLB账户管理系统,请选择:******
1. 登录
2. 退出
-----------------------------------------
>> 1
请输入用户名:Jim
请输入密码:Jim
登录成功,欢迎 Jim !
-----------------------------------------
请选择:
1. 退出
2. 查询余额
3. 取款
4. 存款
5. 转账
-----------------------------------------
>> 2
账户余额为:1000.0
-----------------------------------------
请选择:
1. 退出
2. 查询余额
3. 取款
4. 存款
5. 转账
-----------------------------------------
>> 3
请输入取款金额:500
成功取款500.0,当前余额为:500.0
-----------------------------------------
请选择:
1. 退出
2. 查询余额
3. 取款
4. 存款
5. 转账
-----------------------------------------
>> 4
请输入存款金额:1000
成功存入1000.0,当前余额为:1500.0
-----------------------------------------
请选择:
1. 退出
2. 查询余额
3. 取款
4. 存款
5. 转账
-----------------------------------------
>> 5
请输入转出金额:100
请输入转账账户ID:12
转账成功,当前余额为:1400.0
-----------------------------------------
请选择:
1. 退出
2. 查询余额
3. 取款
4. 存款
5. 转账
-----------------------------------------
>> 2
账户余额为:1400.0
-----------------------------------------
请选择:
1. 退出
2. 查询余额
3. 取款
4. 存款
5. 转账
-----------------------------------------
>> 1
~~Bye~~