SpringBoot项目—个人记账笔记
这部分的学习内容主要在于维护我的个人笔记,将springboot步骤以博客的形式作为笔记。谢谢大家的阅读。
1、项目的简单介绍
首先是任务目标:
- 业务目标:使用 Spring Boot + Mybatis + tk Mybatis + Thymeleaf 完成个 人记账管理
- 技能目标: Spring Boot、tk Mybatis、Thymeleaf、MySQL
其中的任务过程如下:
1.账单分类查询
2.账单分页查询
3.账单添加
4.账单修改
5.账单删除
部分截图实现如下:
大致是三个页面,其中第三个页面是修改,跟添加账单的显示类似。
以上的项目加上数据库目前已经打包成gitee打包:小艾扶/记账管理系统 - 码云
其中注意,我的MySQL使用的是8以上的版本,使用5的伙伴们就需要注意编码格式。
2、数据库表示
首先是账单的内容,它里面还有包含账单分类。
这里稍微介绍一下数据库即可。
3、项目的环境构建&编写
首先就是创建一个Maven项目,项目导入如下:
<dependencies>
<!-- 通用Mapper启动器 -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.11</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
</dependencies>
添加依赖,其中包括分页、tkmybatis和springboot加上thymeleaf的前端编辑。
启动类加载:
@SpringBootApplication
@MapperScan("com.xaf.dao")
public class BillManagerApplication {
public static void main(String[] args) {
SpringApplication.run(BillManagerApplication.class, args);
}
}
将src/main/resource拷贝到resource下继续使用,稍微在配置文件稍作修改:
server:
port: 8001
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
url: jdbc:mysql://localhost:3306/bill-manager
thymeleaf:
cache: false #关闭thymeleaf缓存
#整合mybatis
mybatis:
type-aliases-package: com.xaf.entity #别名搜索
mapper-locations: classpath:/mybatis/*.xml
里面根据自己的需要进行修改。
实体类:
@Table(name = "bill_")
public class Bill implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id_")
private Long id;
@Column(name = "title_")
private String title;
@Column(name = "bill_time_")
private Date billTime;
@Column(name = "type_id_")
private Long typeId;
@Column(name = "price_")
private Double price;
@Column(name = "explain_")
private String explain;
/**
* 类别名称
*/
@Transient
private String typeName;
/**
* 开始时间:用于查询
*/
@Transient
private Date date1;
/**
* 结束时间:用于查询
*/
@Transient
private Date date2;
/**
*加入getter和setter方法
*/
@Table(name = "bill_type_")
public class BillType implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id_")
public Long id;
@Column(name = "name_")
public String name;
}
简单介绍一下:
@Table(name = "bill_"):将数据库表指向里面内容
@GeneratedValue(strategy = GenerationType.IDENTITY):设置表排列
@Column(name = "id_"):对应数据库表对应名称
@Transient:设置其中的属性不随着其他属性一样被直接显示或者调用
Dao层:
public interface BillMapper extends Mapper<Bill> {
public List<Bill> select(Bill b);
}
public interface TypeMapper extends Mapper<BillType> {
}
Service:
@Service
public class BillService {
@Autowired
private BillMapper billMapper;
public List<Bill> list(Bill b) {
return billMapper.select(b);
}
public int add(Bill b) {
return billMapper.insert(b);
}
public Bill get(Long id) {
return billMapper.selectByPrimaryKey(id);
}
public int update(Bill b) {
return billMapper.updateByPrimaryKey(b);
}
public int delete(Long id) {
return billMapper.deleteByPrimaryKey(id);
}
public PageInfo<Bill> listPage(Bill b, int pageNum, int pageSize) {
return PageHelper.startPage(pageNum, pageSize).doSelectPageInfo(() -> {
billMapper.select(b);
});
}
}
@Service
public class TypeService {
@Resource
private TypeMapper typeMapper;
public List<BillType> list() {
return typeMapper.selectAll();
}
}
跟前面学习spring一样。
Controller:
@Controller
@RequestMapping("/bill")
public class BillController {
@Resource
private TypeService typeService;
@Resource
private BillService billService;
/**
* 查询
* @param b
* @param model
* @return
*/
@RequestMapping("/list")
public String list(Bill b, Model model) {
List<BillType> types = typeService.list();
model.addAttribute("types", types);
List<Bill> list = billService.list(b);
model.addAttribute("list", list);
model.addAttribute("bill", b);
return "/bill/list";
}
/**
* 调到添加页面
* @return
*/
@GetMapping("/toAdd")
public String toAdd(Model model) {
List<BillType> types = typeService.list();
model.addAttribute("types", types);
return "/bill/add";
}
/**
* 添加
* @param b
* @return
*/
@GetMapping("/add")
public String add(Bill b) {
billService.add(b);
return "redirect:/bill/list";
}
/**
* 删除
* @param id
* @return
*/
@GetMapping("/delete/{id}")
public String delete(@PathVariable("id") Long id) {
billService.delete(id);
return "redirect:/bill/list";
}
/**
* 修改
* @param id
* @return
*/
@GetMapping("/toUpdate/{id}")
public String toUpdate(@PathVariable("id") Long id, Model model) {
List<BillType> types = typeService.list();
model.addAttribute("types", types);
Bill bill = billService.get(id);
model.addAttribute("bill", bill);
return "/bill/update";
}
/**
* 修改
* @param b
* @return
*/
@GetMapping("/update")
public String update(Bill b) {
billService.update(b);
return "redirect:/bill/list";
}
/**
* 查询
* @param b
* @param model
* @return
*/
@GetMapping("/list-page")
public String listPage(@RequestParam(defaultValue = "1") int pageNum, @RequestParam(defaultValue = "10") int pageSize, Bill b, Model model) {
List<BillType> types = typeService.list();
model.addAttribute("types", types);
PageInfo<Bill> pageInfo = billService.listPage(b, pageNum, pageSize);
model.addAttribute("page", pageInfo);
model.addAttribute("bill", b);
return "/bill/list-page";
}
}
最后就是resource下的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">
<mapper namespace="com.xaf.dao.BillMapper">
<sql id="selectSql">
SELECT
b.id_ as id,
b.title_ as title,
b.bill_time_ as billTime,
b.type_id_ as typeId,
b.price_ as price,
b.explain_ as `explain`,
t.name_ as typeName
FROM
bill_ as b
left join
bill_type_ as t
on
b.type_id_ = t.id_
</sql>
<select id="select" resultType="bill">
<include refid="selectSql"/>
<where>
<if test="typeId !=null">
b.type_id_ = #{typeId}
</if>
<if test="title !=null">
and b.title_ like '%${title}%'
</if>
<if test="date1 !=null">
and b.bill_time_ >= #{date1}
</if>
<if test="date2 !=null">
and b.bill_time_ <= #{date2}
</if>
</where>
</select>
</mapper>
其中如下代码要看一下是否对应自己的项目路径下。
<mapper namespace="com.xaf.dao.BillMapper">
剩下的就是静态资源了,里面的静态资源需要通过后端Controller中的model.addAttribute("bill", bill);
进行传值。目前我的项目时已经完成了这部分的内容。
4、附加 时间转换
@Component
public class DateConverterConfig implements Converter<String, Date> {
private static final List<String> formarts = new ArrayList<>(4);
static{
formarts.add("yyyy-MM");
formarts.add("yyyy-MM-dd");
formarts.add("yyyy-MM-dd hh:mm");
formarts.add("yyyy-MM-dd hh:mm:ss");
}
@Override
public Date convert(String source) {
String value = source.trim();
if ("".equals(value)) {
return null;
}
if(source.matches("^\\d{4}-\\d{1,2}$")){
return parseDate(source, formarts.get(0));
}else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")){
return parseDate(source, formarts.get(1));
}else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")){
return parseDate(source, formarts.get(2));
}else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")){
return parseDate(source, formarts.get(3));
}else {
throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
}
}
/**
* 格式化日期
* @param dateStr String 字符型日期
* @param format String 格式
* @return Date 日期
*/
public Date parseDate(String dateStr, String format) {
Date date=null;
try {
DateFormat dateFormat = new SimpleDateFormat(format);
date = dateFormat.parse(dateStr);
} catch (Exception e) {
}
return date;
}
}
就简单介绍一下,后面的Date parseDate方法是将String类型字符转换成Date类型字符;前面添加一个list放置格式内容,后面就对格式内容进行比较并返回。
5、前端回显(粗略)
这里稍微介绍一部分内容即可。
<div class="with:80%">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>ID</th>
<th>标题</th>
<th>时间</th>
<th>金额</th>
<th>类别</th>
<th>说明</th>
<th>操作</th>
</tr>
</thead>
<tbody >
<!-- TODO 回显查询结果-->
<tr th:each="b,status: ${list}" th:style="${status.odd} ? 'background-color:#A3C6C8'">
<td th:text="${b.id}">id</td>
<td th:text="${b.title}">name</td>
<td th:text="${b.billTime} ? ${#dates.format(b.billTime, 'yyyy-MM-dd')}">time</td>
<td th:text="${b.price}">price</td>
<td th:text="${b.typeName}">typeName</td>
<td th:text="${b.explain}">explain</td>
<td>
<a th:href="|/bill/delete/${b.id}|">删除</a>
<a th:href="|/bill/toUpdate/${b.id}|">修改</a>
</td>
</tr>
</tbody>
</table>
</div>
这里面显示内容时,我们使用的是thymeleaf,用th:each在行标签设置循环,将内容以行的形式呈现出来才能完成最上面展示的样式。然后设置b以list类型,将list中的各个属性输出到内容上。
List<Bill> list = billService.list(b);
model.addAttribute("list", list);
这样子就完成了基本的显示内容。
再举例一个地方:
<select name="typeId" id="typeId" class="form-control" >
<option value="">全部</option>
<option th:each="t: ${types}" th:value="${t.id}" th:text="${t.name}" th:selected="(${bill.typeId} == ${t.id})" ></option>
</select>
这里面就是进行下拉框的显示,也是在选择标签下设置循环,将value值传进去再显示文本。(前面的展示不需要value在于只需要能够显示即可,而这里设置value是因为还要将相关的值作为记录并回响数据。)
总结
完成了以上的步骤,就基本上离完成项目项目的基本构建不远了。这部分笔记稍微作为个人笔记使用,也希望能够被大佬批评指正。谢谢阅读。