基于jsp+servlet图书管理系统之后台用户信息插入操作
简易图书管理系统(主要是jsp+servlet的练习):https://blog.csdn.net/Biexiansheng/article/details/70240413
免费提供源码,有偿提供服务,支持项目定制。
前奏:
刚开始接触博客园写博客,就是写写平时学的基础知识,慢慢发现大神写的博客思路很清晰,知识很丰富,非常又价值,反思自己写的,顿时感觉非常low,有相当长一段时间没有分享自己的知识。于是静下心来钻研知识,趁着这学期的结束(马上就要放寒假了),写写最近练习的基于jsp+servlet+前台模板写的图书管理系统,一点一点写吧,详细的注释已经在代码上说明,希望对学习基于java开发的web方向的童鞋有所帮助。今天先把写的用户信息管理的添加(插入)操作分享一下,使用了一些特殊的知识,也会做一下说明。更多代码和功能会持续更新,完整可直接运行的。
开发环境:Eclipse Version: Mars.2 Release (4.5.2)
JDK Version:1.8
tomcat Vsersion:7.0
(由于个人掌握知识有限,如若有不足的地方,还请多多交流。)
开始修改原型设计:
1:设计好数据库(当然我这里设计是简单的数据库),见上面的链接,下载导入自己的数据库即可使用。
2:我先准备好了原型模板,然后将原型模板修改好,然后才进行开发。原型模板修改如下。
将登录界面的原型html修改为jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//获取绝对路径路径
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html>
<html lang="en">
<head>
<base href="<%=basePath %>" />
<title>用户登录</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="resource/assets/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="resource/assets/css/font-awesome.min.css" />
<link rel="stylesheet" href="resource/assets/css/ace.min.css" />
<link rel="stylesheet" href="resource/assets/css/ace-rtl.min.css" />
</head>
<body class="login-layout">
<div class="main-container">
<div class="main-content">
<div class="row">
<div class="col-sm-10 col-sm-offset-1">
<div class="login-container">
<div class="center">
<h1>
<i class="icon-leaf green"></i> <span class="red">别先生</span> <span
class="white">图书管理系统</span>
</h1>
<h4 class="blue"></h4>
</div>
<div class="space-6"></div>
<div class="position-relative">
<div id="login-box"
class="login-box visible widget-box no-border">
<div class="widget-body">
<div class="widget-main">
<h4 class="header blue lighter bigger">
<i class="icon-coffee green"></i> 请输入您的账号和密码
</h4> <div class="space-6"></div>
<form action="view/system/main/index.jsp" method="post" onsubmit="return check()">
<fieldset>
<label class="block clearfix"> <span
class="block input-icon input-icon-right"> <input id="userId"
name="userId" type="text" class="form-control"
placeholder="请输入您的账号" /> <i class="icon-user"></i>
</span>
</label> <label class="block clearfix"> <span
class="block input-icon input-icon-right"> <input id="userPw"
name="passWord" type="password" class="form-control"
placeholder="请输入您的密码" /> <i class="icon-lock"></i>
</span>
</label> <div class="clearfix"> <button type="submit"
class="width-35 pull-right btn btn-sm btn-primary">
<i class="icon-key"></i> 登陆
</button>
</div> <div class="space-4"></div>
</fieldset>
</form> </div> </div> </div> </div> </div>
</div> </div>
</div>
</div> </body>
</html>
登陆界面
将主界面的原型html修改为jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//获取绝对路径路径
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html>
<html lang="en">
<head>
<base href="<%=basePath %>" />
<title>首页</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<link href="resource/assets/css/bootstrap.min.css" rel="stylesheet" />
<link rel="stylesheet" href="resource/assets/css/font-awesome.min.css" />
<link rel="stylesheet" href="resource/assets/css/ace.min.css" />
<link rel="stylesheet" href="resource/assets/css/ace-rtl.min.css" />
<link rel="stylesheet" href="resource/assets/css/ace-skins.min.css" />
<script src="resource/assets/js/ace-extra.min.js"></script>
<script src="resource/assets/jquery.min.js"></script>
</head>
<body>
<div class="navbar navbar-default" id="navbar">
<script type="text/javascript">
try {
ace.settings.check('navbar', 'fixed')
} catch (e) {
}
</script> <div class="navbar-container" id="navbar-container">
<div class="navbar-header pull-left">
<a href="view/system/main/index.jsp" class="navbar-brand"> <small> <i
class="icon-leaf"></i> 别先生后台图书管理系统
</small>
</a>
<!-- /.brand -->
</div>
<!-- /.navbar-header --> <div class="navbar-header pull-right" role="navigation">
<ul class="nav ace-nav">
<li class="green"><a data-toggle="dropdown"
class="dropdown-toggle" href="#"> <i
class="icon-envelope icon-animated-vertical"></i> <span
class="badge badge-success">5</span>
</a> <ul
class="pull-right dropdown-navbar dropdown-menu dropdown-caret dropdown-close">
<li class="dropdown-header"><i class="icon-envelope-alt"></i>
5条消息</li>
</ul></li> <li class="light-blue"><a data-toggle="dropdown" href="#"
class="dropdown-toggle"> <img class="nav-user-photo"
src="resource/assets/avatars/user.jpg" alt="Jason's Photo" /> <span
class="user-info"> <small>欢迎光临,</small>
</span> <i class="icon-caret-down"></i>
</a>
<ul
class="user-menu pull-right dropdown-menu dropdown-yellow dropdown-caret dropdown-close"> <li><a href="#" target="mainframe"> <i class="icon-user"></i> 个人资料
</a></li> <li class="divider"></li> <li><a href="view/system/main/login.jsp"> <i class="icon-off"></i> 退出
</a></li>
</ul></li>
</ul>
<!-- /.ace-nav --> </div>
<!-- /.navbar-header -->
</div>
<!-- /.container -->
</div> <div class="main-container" id="main-container">
<script type="text/javascript">
try {
ace.settings.check('main-container', 'fixed')
} catch (e) {
}
</script> <div class="main-container-inner">
<a class="menu-toggler" id="menu-toggler" href="#"> <span
class="menu-text"></span>
</a> <div class="sidebar" id="sidebar">
<script type="text/javascript">
try {
ace.settings.check('sidebar', 'fixed')
} catch (e) {
}
</script> <div class="sidebar-shortcuts" id="sidebar-shortcuts">
<div class="sidebar-shortcuts-large" id="sidebar-shortcuts-large">
<button class="btn btn-success">
<i class="icon-signal"></i>
</button> <button class="btn btn-info">
<i class="icon-pencil"></i>
</button> <button class="btn btn-warning">
<i class="icon-group"></i>
</button> <button class="btn btn-danger">
<i class="icon-cogs"></i>
</button>
</div> <div class="sidebar-shortcuts-mini" id="sidebar-shortcuts-mini">
<span class="btn btn-success"></span> <span class="btn btn-info"></span> <span class="btn btn-warning"></span> <span class="btn btn-danger"></span>
</div>
</div>
<!-- #sidebar-shortcuts --> <ul class="nav nav-list">
<li class="active"><a
href="view/system/main/main.jsp" target="mainframe">
<i class="icon-dashboard"></i> <span class="menu-text">
控制台 </span>
</a></li>
<li><a href="javascript:void(0)" target="mainframe"
class="dropdown-toggle"> <i class="icon-desktop"></i> <span
class="menu-text"> 系统管理 </span> <b class="arrow icon-angle-down"></b>
</a> <ul class="submenu"> <li><a href="view/system/userinfo/userinfo_list.jsp" target="mainframe"> <i
class="icon-double-angle-right"></i> 用户管理
</a></li> </ul>
</li>
<li><a href="javascript:void(0)" target="mainframe"
class="dropdown-toggle"> <i class="icon-desktop"></i> <span
class="menu-text">图书管理 </span> <b class="arrow icon-angle-down"></b>
</a> <ul class="submenu"> <li><a href="view/crm/customer/customer_list.html" target="mainframe"> <i
class="icon-double-angle-right"></i> 图书管理
</a></li>
<li><a href="view/crm/customer/customer_list.html" target="mainframe"> <i
class="icon-double-angle-right"></i> 图书记录管理
</a></li> </ul>
</li>
</ul>
<!-- /.nav-list --> <div class="sidebar-collapse" id="sidebar-collapse">
<i class="icon-double-angle-left"
data-icon1="icon-double-angle-left"
data-icon2="icon-double-angle-right"></i>
</div> <script type="text/javascript">
try {
ace.settings.check('sidebar', 'collapsed')
} catch (e) {
}
</script>
</div> <div class="main-content" id="mains">
<iframe id="mainframe" name="mainframe" src="view/system/main/main.jsp"
style="width: 100%;border: 0px;"> </iframe> </div> <script type="text/javascript">
var height = jQuery(window).height() - 50;
jQuery("#mainframe").attr("height", "" + height + "px;");
</script> <div class="ace-settings-container" id="ace-settings-container">
<div class="btn btn-app btn-xs btn-warning ace-settings-btn"
id="ace-settings-btn">
<i class="icon-cog bigger-150"></i>
</div> <div class="ace-settings-box" id="ace-settings-box">
<div>
<div class="pull-left">
<select id="skin-colorpicker" class="hide">
<option data-skin="default" value="#438EB9">#438EB9</option>
<option data-skin="skin-1" value="#222A2D">#222A2D</option>
<option data-skin="skin-2" value="#C6487E">#C6487E</option>
<option data-skin="skin-3" value="#D0D0D0">#D0D0D0</option>
</select>
</div>
<span> 选择皮肤</span>
</div> <div>
<input type="checkbox" class="ace ace-checkbox-2"
id="ace-settings-navbar" /> <label class="lbl"
for="ace-settings-navbar"> 固定导航条</label>
</div> <div>
<input type="checkbox" class="ace ace-checkbox-2"
id="ace-settings-sidebar" /> <label class="lbl"
for="ace-settings-sidebar"> 固定滑动条</label>
</div> <div>
<input type="checkbox" class="ace ace-checkbox-2"
id="ace-settings-breadcrumbs" /> <label class="lbl"
for="ace-settings-breadcrumbs">固定面包屑</label>
</div> <div>
<input type="checkbox" class="ace ace-checkbox-2"
id="ace-settings-rtl" /> <label class="lbl"
for="ace-settings-rtl">切换到左边</label>
</div> <div>
<input type="checkbox" class="ace ace-checkbox-2"
id="ace-settings-add-container" /> <label class="lbl"
for="ace-settings-add-container"> 切换窄屏 <b></b>
</label>
</div>
</div>
</div>
<!-- /#ace-settings-container -->
</div>
<!-- /.main-container-inner --> <a href="#" id="btn-scroll-up"
class="btn-scroll-up btn btn-sm btn-inverse"> <i
class="icon-double-angle-up icon-only bigger-110"></i>
</a>
</div>
<!-- /.main-container -->
<!-- basic scripts --> <script type="text/javascript">
if ("ontouchend" in document)
document
.write("<script src='resource/assets/js/jquery.mobile.custom.min.js'>"
+ "<"+"script>");
</script>
<script src="resource/assets/js/bootstrap.min.js"></script>
<script src="resource/assets/js/typeahead-bs2.min.js"></script> <!-- page specific plugin scripts --> <!--[if lte IE 8]>
<script src="resource/assets/js/excanvas.min.js"></script>
<![endif]--> <script src="resource/assets/js/jquery-ui-1.10.3.custom.min.js"></script>
<script src="resource/assets/js/jquery.ui.touch-punch.min.js"></script>
<script src="resource/assets/js/jquery.slimscroll.min.js"></script>
<script src="resource/assets/js/jquery.easy-pie-chart.min.js"></script>
<script src="resource/assets/js/jquery.sparkline.min.js"></script>
<script src="resource/assets/js/flot/jquery.flot.min.js"></script>
<script src="resource/assets/js/flot/jquery.flot.pie.min.js"></script>
<script src="resource/assets/js/flot/jquery.flot.resize.min.js"></script> <!-- ace scripts --> <script src="resource/assets/js/ace-elements.min.js"></script>
<script src="resource/assets/js/ace.min.js"></script> <!-- inline scripts related to this page --> <script type="text/javascript">
jQuery(function($) {
$('.easy-pie-chart.percentage')
.each(
function() {
var $box = $(this).closest('.infobox');
var barColor = $(this).data('color')
|| (!$box.hasClass('infobox-dark') ? $box
.css('color')
: 'rgba(255,255,255,0.95)');
var trackColor = barColor == 'rgba(255,255,255,0.95)' ? 'rgba(255,255,255,0.25)'
: '#E2E2E2';
var size = parseInt($(this).data('size')) || 50;
$(this)
.easyPieChart(
{
barColor : barColor,
trackColor : trackColor,
scaleColor : false,
lineCap : 'butt',
lineWidth : parseInt(size / 10),
animate : /msie\s*(8|7|6)/
.test(navigator.userAgent
.toLowerCase()) ? false
: 1000,
size : size
});
}) $('.sparkline').each(
function() {
var $box = $(this).closest('.infobox');
var barColor = !$box.hasClass('infobox-dark') ? $box
.css('color') : '#FFF';
$(this).sparkline('html', {
tagValuesAttribute : 'data-values',
type : 'bar',
barColor : barColor,
chartRangeMin : $(this).data('min') || 0
});
}); var placeholder = $('#piechart-placeholder').css({
'width' : '90%',
'min-height' : '150px'
});
var data = [ {
label : "social networks",
data : 38.7,
color : "#68BC31"
}, {
label : "search engines",
data : 24.5,
color : "#2091CF"
}, {
label : "ad campaigns",
data : 8.2,
color : "#AF4E96"
}, {
label : "direct traffic",
data : 18.6,
color : "#DA5430"
}, {
label : "other",
data : 10,
color : "#FEE074"
} ]
function drawPieChart(placeholder, data, position) {
$.plot(placeholder, data, {
series : {
pie : {
show : true,
tilt : 0.8,
highlight : {
opacity : 0.25
},
stroke : {
color : '#fff',
width : 2
},
startAngle : 2
}
},
legend : {
show : true,
position : position || "ne",
labelBoxBorderColor : null,
margin : [ -30, 15 ]
},
grid : {
hoverable : true,
clickable : true
}
})
}
drawPieChart(placeholder, data); /**
we saved the drawing function and the data to redraw with different position later when switching to RTL mode dynamically
so that's not needed actually.
*/
placeholder.data('chart', data);
placeholder.data('draw', drawPieChart); var $tooltip = $(
"<div class='tooltip top in'><div class='tooltip-inner'></div></div>")
.hide().appendTo('body');
var previousPoint = null; placeholder.on('plothover', function(event, pos, item) {
if (item) {
if (previousPoint != item.seriesIndex) {
previousPoint = item.seriesIndex;
var tip = item.series['label'] + " : "
+ item.series['percent'] + '%';
$tooltip.show().children(0).text(tip);
}
$tooltip.css({
top : pos.pageY + 10,
left : pos.pageX + 10
});
} else {
$tooltip.hide();
previousPoint = null;
} }); var d1 = [];
for (var i = 0; i < Math.PI * 2; i += 0.5) {
d1.push([ i, Math.sin(i) ]);
} var d2 = [];
for (var i = 0; i < Math.PI * 2; i += 0.5) {
d2.push([ i, Math.cos(i) ]);
} var d3 = [];
for (var i = 0; i < Math.PI * 2; i += 0.2) {
d3.push([ i, Math.tan(i) ]);
} var sales_charts = $('#sales-charts').css({
'width' : '100%',
'height' : '220px'
});
$.plot("#sales-charts", [ {
label : "Domains",
data : d1
}, {
label : "Hosting",
data : d2
}, {
label : "Services",
data : d3
} ], {
hoverable : true,
shadowSize : 0,
series : {
lines : {
show : true
},
points : {
show : true
}
},
xaxis : {
tickLength : 0
},
yaxis : {
ticks : 10,
min : -2,
max : 2,
tickDecimals : 3
},
grid : {
backgroundColor : {
colors : [ "#fff", "#fff" ]
},
borderWidth : 1,
borderColor : '#555'
}
}); $('#recent-box [data-rel="tooltip"]').tooltip({
placement : tooltip_placement
});
function tooltip_placement(context, source) {
var $source = $(source);
var $parent = $source.closest('.tab-content')
var off1 = $parent.offset();
var w1 = $parent.width(); var off2 = $source.offset();
var w2 = $source.width(); if (parseInt(off2.left) < parseInt(off1.left)
+ parseInt(w1 / 2))
return 'right';
return 'left';
} $('.dialogs,.comments').slimScroll({
height : '300px'
}); //Android's default browser somehow is confused when tapping on label which will lead to dragging the task
//so disable dragging when clicking on label
var agent = navigator.userAgent.toLowerCase();
if ("ontouchstart" in document && /applewebkit/.test(agent)
&& /android/.test(agent))
$('#tasks').on('touchstart', function(e) {
var li = $(e.target).closest('#tasks li');
if (li.length == 0)
return;
var label = li.find('label.inline').get(0);
if (label == e.target || $.contains(label, e.target))
e.stopImmediatePropagation();
}); $('#tasks').sortable({
opacity : 0.8,
revert : true,
forceHelperSize : true,
placeholder : 'draggable-placeholder',
forcePlaceholderSize : true,
tolerance : 'pointer',
stop : function(event, ui) {//just for Chrome!!!! so that dropdowns on items don't appear below other items after being moved
$(ui.item).css('z-index', 'auto');
}
});
$('#tasks').disableSelection();
$('#tasks input:checkbox').removeAttr('checked').on('click',
function() {
if (this.checked)
$(this).closest('li').addClass('selected');
else
$(this).closest('li').removeClass('selected');
}); })
</script> </body>
</html>
主界面
将其他界面的原型html修改为jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//获取绝对路径路径
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath %>" />
<meta charset="utf-8">
<title>工作台</title>
<!-- 新 Bootstrap 核心 CSS 文件 -->
<link rel="stylesheet" href="resource/css/bootstrap.min.css">
<!-- jQuery文件。务必在bootstrap.min.js 之前引入 -->
<script src="resource/js/jquery.min.js"></script>
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="resource/js/bootstrap.min.js"></script>
</head>
<body>
<div style="padding:0px; margin:0px;">
<ul class="breadcrumb" style=" padding:0px; padding-left:20px;" >
<li ><a href="#">首页</a></li>
<li>工作台</li>
</ul>
</div>
<div class="row">
<div class="col-xs-6" >
<div class="panel panel-default" >
<div class="panel-heading" >
<span class="glyphicon glyphicon-refresh"></span>图形报表
</div>
<div class="panel-body">
<img src="resource/img/test.png" height="200" width="100%">
</div>
</div>
</div>
<div class="col-xs-6" >
<div class="panel panel-default" >
<div class="panel-heading" >
<span class="glyphicon glyphicon-refresh"></span>图形报表
</div>
<div class="panel-body">
<img src="resource/img/test.png" height="200" width="100%">
</div>
</div>
</div> </div> </body>
</html>
其他界面
这里插一句,我使用的是servlet3.0,但是配置文件修为了默认访问登录界面login.jsp
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>book</display-name>
<welcome-file-list>
<!-- 默认模仿的是登录界面login.jsp页面 -->
<welcome-file>login.jsp</welcome-file>
</welcome-file-list>
</web-app>
servlet3.0-xml
将用户信息列表界面的原型html修改为jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//获取绝对路径路径
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath %>" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>用户管理-用户查询</title>
<link href="resource/css/bootstrap.min.css" rel="stylesheet" />
<script type="text/javascript" src="resource/js/jquery.min.js"></script>
<script type="text/javascript"
src="resource/js/bootstrap.min.js"></script>
</head>
<body>
<div>
<ul class="breadcrumb" style="margin: 0px;">
<li>系统管理</li>
<li>用户管理</li>
<li>用户查询</li>
</ul>
</div>
<form action="" class="form-inline">
<div class="row alert alert-info" style="margin: 0px; padding: 5px;">
<div class="form-group">
<label>条件:</label> <select class="form-control">
<option>姓名</option>
<option>性别</option>
</select> <input type="text" class="form-control" placeholder="请输入查询条件" />
</div>
<input type="button" class="btn btn-danger" value="查询"> <a
href="view/system/userinfo/userinfo_add.jsp" class="btn btn-success">添加用户</a>
</div>
<div class="row" style="padding: 15px;">
<table class="table table-hover table-condensed">
<tr>
<th>用户编号</th>
<th>用户账号</th>
<th>用户密码</th>
<th>用户姓名</th>
<th>用户性别</th>
<th>用户年龄</th>
<td>操作</td>
</tr>
<tr>
<td>1001</td>
<td>admin123</td>
<td>123456</td>
<td>高富帅</td>
<td>男</td>
<td>20</td>
<td><a href="view/system/userinfo/userinfo_update.jsp">修改</a> 删除</td>
</tr>
<tr>
<td>1001</td>
<td>admin123</td>
<td>123456</td>
<td>白富美</td>
<td>女</td>
<td>20</td>
<td>修改 删除</td>
</tr>
<tr>
<td>1001</td>
<td>admin123</td>
<td>123456</td>
<td>高富帅</td>
<td>男</td>
<td>20</td>
<td>修改 删除</td>
</tr>
<tr>
<td>1001</td>
<td>admin123</td>
<td>123456</td>
<td>白富美</td>
<td>女</td>
<td>20</td>
<td>修改 删除</td>
</tr>
</table>
</div>
</form>
</body>
</html>
用户信息的主页面
将用户信息插入界面的原型html修改为jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//获取绝对路径路径
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath %>" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>用户管理-用户添加</title>
<link href="resource/css/bootstrap.min.css" rel="stylesheet" />
<script type="text/javascript" src="resource/js/jquery.min.js"></script>
<script type="text/javascript"
src="resource/js/bootstrap.min.js"></script>
</head>
<body>
<div>
<ul class="breadcrumb" style="margin: 0px;">
<li>系统管理</li>
<li>用户管理</li>
<li>用户添加</li>
</ul>
</div>
<form action="system/userinfoinsert" class="form-horizontal" method="post">
<h5 class="page-header alert-info"
style="margin: 0px; padding: 10px; margin-bottom: 10px;">基本信息</h5>
<!-- 开始1 -->
<div class="row">
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户学号</label>
<div class="col-xs-9 ">
<input type="text" name="userNumber" class="form-control" placeholder="请输入用户学号" />
</div>
</div>
</div>
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户姓名</label>
<div class="col-xs-9 ">
<input type="text" name="userName" class="form-control" placeholder="请输入用户姓名" />
</div>
</div>
</div>
</div>
<!--结束1 -->
<!-- 开始2 -->
<div class="row">
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户年龄</label>
<div class="col-xs-9 ">
<input type="text" name="userAge" class="form-control" placeholder="请输入用户年龄" />
</div>
</div>
</div>
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户性别</label>
<div class="col-xs-3 ">
<select class="form-control" name="userSex">
<option>保密</option>
<option>男</option>
<option>女</option>
</select>
</div>
</div>
</div> </div>
<!--结束2 --> <h5 class="page-header alert-info"
style="margin: 0px; padding: 10px; margin-bottom: 10px;">账号信息</h5>
<!-- 开始5 -->
<div class="row">
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户账号</label>
<div class="col-xs-9">
<input type="text" name="userAccount" class="form-control" placeholder="请输入用户账号" />
</div>
</div>
</div>
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户密码</label>
<div class="col-xs-9 ">
<input type="text" name="userPw" class="form-control" placeholder="请输入用户密码" />
</div>
</div>
</div>
</div>
<!--结束5 --> <div class="row">
<div class="col-xs-3 col-xs-offset-4">
<input type="submit" class="btn btn-success" value="保存用户" /> <input
type="reset" class="btn btn-danger" value="重置信息" />
</div> </div> </form>
</body>
</html>
用户信息的插入界面
将用户信息更改界面的原型html修改为jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//获取绝对路径路径
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html>
<html>
<head>
<base href="<%=basePath %>" />
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>用户管理-用户修改</title>
<link href="resource/css/bootstrap.min.css" rel="stylesheet" />
<script type="text/javascript" src="resource/js/jquery.min.js"></script>
<script type="text/javascript"
src="resource/js/bootstrap.min.js"></script>
</head>
<body>
<div>
<ul class="breadcrumb" style="margin: 0px;">
<li>系统管理</li>
<li>用户管理</li>
<li>用户修改</li>
</ul>
</div>
<form action="" class="form-horizontal">
<h5 class="page-header alert-info"
style="margin: 0px; padding: 10px; margin-bottom: 10px;">基本信息</h5>
<!-- 开始1 -->
<div class="row">
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户姓名</label>
<div class="col-xs-9 ">
<input type="text" class="form-control" placeholder="请输入用户姓名" value="高富帅" />
</div>
</div>
</div>
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户年龄</label>
<div class="col-xs-9 ">
<input type="text" class="form-control" placeholder="请输入用户年龄" value="20" />
</div>
</div>
</div>
</div>
<!--结束1 -->
<!-- 开始2 -->
<div class="row">
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户性别</label>
<div class="col-xs-3 ">
<select class="form-control">
<option>保密</option>
<option>男</option>
<option>女</option>
</select>
</div>
</div>
</div> </div>
<!--结束2 --> <h5 class="page-header alert-info"
style="margin: 0px; padding: 10px; margin-bottom: 10px;">账号信息</h5>
<!-- 开始5 -->
<div class="row">
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户账号</label>
<div class="col-xs-9">
<input type="text" class="form-control" placeholder="请输入用户账号" value="admin123" />
</div>
</div>
</div>
<div class="col-xs-5">
<div class="form-group ">
<label class="col-xs-3 control-label">用户密码</label>
<div class="col-xs-9 ">
<input type="text" class="form-control" placeholder="请输入用户密码" value="123456" />
</div>
</div>
</div>
</div>
<!--结束5 --> <div class="row">
<div class="col-xs-3 col-xs-offset-4">
<input type="submit" class="btn btn-success" value="保存用户" /> <input
type="reset" class="btn btn-danger" value="重置信息" />
</div> </div> </form>
</body>
</html>
用户信息的更新界面
用户信息提示信息界面的jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
//获取绝对路径路径
String path = request.getContextPath();
String basePath = request.getScheme() + "://"
+ request.getServerName() + ":" + request.getServerPort()
+ path + "/";
%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<base href="<%=basePath %>" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<script type="text/javascript">
//此页面用来提示添加,修改,删除信息的成功或者失败的信息
alert('${info}');//提示信息,错误or成功
//跳转到user_info.jsp查询页面,后面会修改为servlet
window.location="view/system/userinfo/userinfo_list.jsp";
</script>
</body>
</html>
用户信息的提示界面
至此用户信息的登录界面,主页面基本设计的差不多了,接下来就开始进行开发了。
开始基于后台逻辑代码进行开发:
1:这里我使用的是将数据库的驱动driver,数据库连接字符串,数据库账号,密码写到db.properties配置文件中,代码如下:
drivername=com.mysql.jdbc.Driver
url=jdbc:mysql:///book
user=root
password=123456
2:写好配置文件,接下来写utils工具类,代码如下,写好连接数据库的工具类,个人习惯测试了一下,避免连接数据库就发生错误,那就糗大了。
顺便提一下,添加信息和更改信息我直接也封装到了这个工具类中,方便使用,减少重复代码的书写,当然对于新手,多写重复代码是一件好事,
加强记忆和理解
package com.bie.utils; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ResourceBundle; /***
* 1.1:写DbUtils的工具类
* :utils是工具类,方便以后调用
* 在main方法测试的时候出现一个错误,
* 瞄了一眼立马想到了没有添加mysql的驱动,
* 所以我感觉测试还是很有必要的
* @author biehongli
*
*/
public class DbUtils { private static String drivername;//数据库驱动,为了加载数据库驱动
private static String url;//数据库连接字符串,只要是找到自己的数据库,需要和自己的数据库一致
private static String user;//数据库账号,需要和自己的一致
private static String password;//数据库密码,需要和自己的一致 static{
drivername=ResourceBundle.getBundle("db").getString("drivername");
url=ResourceBundle.getBundle("db").getString("url");
user=ResourceBundle.getBundle("db").getString("user");
password=ResourceBundle.getBundle("db").getString("password");
} /***
* 加载数据库驱动和连接到数据库,我一般是加载和连接的时候分别输出,可以快速找到哪里出错
* @return
* @throws Exception
*/
public static Connection getCon() throws Exception{
Class.forName(drivername);//记载数据库驱动
System.out.println("测试加载数据库驱动");
//连接到数据库
Connection con=DriverManager.getConnection(url, user, password);
System.out.println("测试连接到数据库");
return con;
} /***
* 这个用来判断关闭数据库的方法
* @param con 关闭Connection的连接
* @param ps 关闭PreparedStatement
* @param rs 关闭ResultSet
*/
public static void getClose(Connection con,PreparedStatement ps,ResultSet rs){
//关闭数据库,注意关闭的顺序。养成好习惯
try{
if(rs!=null){
rs.close();
}
if(ps!=null){
ps.close();
}
if(con!=null){
con.close();
}
}catch(Exception e){
e.printStackTrace();
}
} /***
* 添加(插入)和更新(更改)可以提取公共的方法写在工具类中
* 删除一般使用伪删除,这样删除就是更新(更改)操作,
* 所以只有查询(查找)需要写更多的代码
* @param sql 外面传来的sql语句
* @param arr 外面传来的数组类型的,是用户信息封装到集合传递进来
* @return 返回的是一个整形的数据类型
*/
public static int addAndUpdate(String sql,Object[] arr){
Connection con=null;
PreparedStatement ps=null;
try{
con=DbUtils.getCon();//第一步连接数据库
ps=con.prepareStatement(sql);//第二步预编译
//第三步给sql语句中的参数复制
for(int i=0;i<arr.length;i++){
ps.setObject(i+1, arr[i]);
}
//第四步执行sql并且返回。
return ps.executeUpdate();
}catch(Exception e){
e.printStackTrace();
}finally{
//关闭资源,如果没有ResultSet类型的,加上null即可
DbUtils.getClose(con, ps, null);
}
return 0;
}
/*public static void main(String[] args) {
//我一般在写好连接数据库的工具类时先测试一下,避免连接数据库都失败,测试后可注释即可
try {
DbUtils.getCon();
System.out.println("测试连接数据库终极版!!!");
} catch (Exception e) {
e.printStackTrace();
}
}*/
}
3:开始写实体类po层。这个实体类里面是用户信息的成员变量。
package com.bie.po;
/***
* 1.2:写用户信息的实体类
* 需要和自己设计好的数据库信息相对应。
* @author biehongli
*
*/
public class UserInfo { private Integer userId;//用户编号
private String userAccount;//用户账号
private String userPw;//用户密码
private String userNumber;//用户学号
private String userName;//用户姓名
private Integer userAge;//用户年龄
private String userSex;//用户性别
private String userMark;//用户标识,可以使用一张表,完成管理员和用户
//如果对setxxx,getxxx灰常熟练了,自动生成即可
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUserAccount() {
return userAccount;
}
public void setUserAccount(String userAccount) {
this.userAccount = userAccount;
}
public String getUserPw() {
return userPw;
}
public void setUserPw(String userPw) {
this.userPw = userPw;
}
public String getUserNumber() {
return userNumber;
}
public void setUserNumber(String userNumber) {
this.userNumber = userNumber;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getUserAge() {
return userAge;
}
public void setUserAge(Integer userAge) {
this.userAge = userAge;
}
public String getUserSex() {
return userSex;
}
public void setUserSex(String userSex) {
this.userSex = userSex;
}
public String getUserMark() {
return userMark;
}
public void setUserMark(String userMark) {
this.userMark = userMark;
}
//重写toString()方法
@Override
public String toString() {
return "UserInfo [userId=" + userId + ", userAccount=" + userAccount + ", userPw=" + userPw + ", userNumber="
+ userNumber + ", userName=" + userName + ", userAge=" + userAge + ", userSex=" + userSex
+ ", userMark=" + userMark + "]";
} }
4:写好实体类,接着写处理业务逻辑,这里是写的插入,所以省去了dao层,如果是查询还需要先写dao层(数据交互层),这里先写接口,然后写实现接口的类。
package com.bie.system.service; import com.bie.po.UserInfo; /***
* 1.3:如果没有在工具类DbUtils中封装addAndUpdate,
* 那么需要现在dao层(数据访问层)写和数据库相关操作的代码
* 如果封装好了,直接在service(业务逻辑层)写代码即可。
* 我的习惯实现写接口再写实现接口的类。
* @author biehongli
*
*/
public interface UserInfoInsertService { /***
* 向数据库插入(添加)数据(用户的信息)
* @param user 用户的信息
* @return true表示插入成功,false表示插入失败
*/
public boolean insertUser(UserInfo user); }
5:写好接口,写实现接口的类,作为一个新手,最好写好service业务逻辑层进行junit测试,避免后期写一大推代码,错误多的不知道哪里错了。
package com.bie.system.service.impl; import java.util.ArrayList;
import java.util.List; import com.bie.po.UserInfo;
import com.bie.system.service.UserInfoInsertService;
import com.bie.utils.DbUtils;
import com.bie.utils.MarkUtils;
/***
* 1.4:这是业务逻辑层的实现类,实现用户信息的接口
*
* 切忌新手写好service业务逻辑层需要test测试(junit)
* @author biehongli
*
*/
public class UserInfoInsertServiceImpl implements UserInfoInsertService{ @Override
public boolean insertUser(UserInfo user) {
try{
//System.out.println(user);//测试传来的UserInfo里面是否够存在用户信息
if(user!=null && user.getUserAccount()!=null){
String sql="INSERT INTO user_info(user_account,user_pw,"
+ "user_number,user_name,user_age,user_sex,user_mark)"
+ " VALUES(?,?,?,?,?,?,?)";
List<Object> list=new ArrayList<Object>();
//可以理解位将实体类中get到的信息放到数据库中,因为set设置的信息就是为了查到数据库中
list.add(user.getUserAccount());//将设置好的账号信息保存到集合中
list.add(user.getUserPw());//将设置好的账号信息保存到集合中
list.add(user.getUserNumber());//将设置好的密码信息保存到集合中
list.add(user.getUserName());//将设置好的姓名信息保存到集合中
list.add(user.getUserAge());//将设置好的年龄信息保存到集合中
list.add(user.getUserSex());//将设置好的性别信息保存到集合中
//list.add(user.getUserMark());//将设置好的标识信息保存到集合中
//后台只可以添加管理员
user.setUserMark(MarkUtils.USER_MARK_MANAGER);
//将设置为默认的管理员添加到数据库
list.add(user.getUserMark()); //将封装到集合list中的信息和sql语句传递到DbUtils封装好的 方法中
//这里sql转化位String语句,list转化位数组类型
int count=DbUtils.addAndUpdate(sql.toString(), list.toArray());
//System.out.println(count);//测试返回值是0还是1
if(count>0){
return true;//成功返回true
}else{
return false;//失败返回false
}
}
}catch(Exception e){
e.printStackTrace();
}
return false;
} }
6:写好service,业务逻辑处理层,开始使用junit进行测试。测试结果就不粘贴了,遇到的测试错误已经在代码中注释了。
package com.bie.system.service.impl; import org.junit.Test; import com.bie.po.UserInfo;
import com.bie.system.service.UserInfoInsertService; /***
* 1.5:测试的类
* @author biehongli
*
*/
public class UserInfoInsertServiceImplTest { private UserInfoInsertService service=new UserInfoInsertServiceImpl(); //测试的时候出点错,String sql="INSERT INTO user_info(user_account,user_pw,user_number,user_name,user_age,user_sex,user_mark) VALUES(?,?,?,?,?,?,?)";
//插入语句写字段的时候加了''导致出错,找了一会,所以写好service业务逻辑层测试还是很重要的
@Test
public void insert(){
UserInfo user=new UserInfo();
user.setUserAccount("别先生");
user.setUserPw("666666");
user.setUserNumber("123456789");
user.setUserName("小别同志");
user.setUserAge(23);
user.setUserSex("爷们");
user.setUserMark("1");
boolean mark=service.insertUser(user);
if(mark){
System.out.println("插入成功!!!");
}else{
System.out.println("明天考试了,今天还不复习,失败了吧!!!");
}
}
}
7:测试好,修改好,没啥问题开始进行servlet层写代码。这里使用了RequestBeanUtils,用法和3个jar包都在上面连接里面都有。
需要注意的是userinfo_add.jsp的form的action路径和method="post"方法,具体的实现细节就不做多叙述了。代码写的很详细了。
package com.bie.system.servlet; import java.io.IOException; import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import com.bie.po.UserInfo;
import com.bie.system.service.UserInfoInsertService;
import com.bie.system.service.impl.UserInfoInsertServiceImpl;
import com.my.web.servlet.RequestBeanUtils; /***
* 1.7:这里使用servlet3.0,说明一下,
* 同时使用了RequestBeanUtils,这个要求表单的name属性必须和实体类UserInfo的成员变量名称一致
* 使用方法如下
* 注解的要求是模块名称加功能比如/system/userinfoinsert,避免后面发生错误
* @author biehongli
*
*/
@WebServlet("/system/userinfoinsert")
public class UserInfoInsertServlet extends HttpServlet{ private static final long serialVersionUID = 1L;//序列号 @Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//RequestBeanUtils的使用方法,需要导入三个包。
//commons-beanutils-1.8.3.jar commons-logging-1.1.1.jar
//commy-web-0.0.1.jar
UserInfo user=RequestBeanUtils.requestToSimpleBean(request, UserInfo.class);
System.out.println(user);//测试到这里是否出现bug
//然后在servlet层调用service逻辑处理层
UserInfoInsertService service=new UserInfoInsertServiceImpl();
//调用service逻辑处理层的插入方法,返回布尔类型
boolean mark=service.insertUser(user);
//返回提示信息到页面
if(mark){
request.setAttribute("info", "用户信息添加成功!!!");
}else{
request.setAttribute("info", "用户信息添加失败!!!");
}
//转发到页面(重定向)user_info.jsp提示信息,成功或者失败
request.getRequestDispatcher("/view/system/userinfo/user_info.jsp").forward(request, response);
} }
8:在写servlet的时候需要考虑乱码问题,这里在工具类写了公共的方法,过滤所有请求,设置字符集为utf-8。
package com.bie.utils; import java.io.IOException; import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
/****
* 1.8:处理乱码的过滤器
* @WebFilter("/*")过滤所有的请求
* @author biehongli
*
*/
@WebFilter("/*")
public class UtfFilter implements Filter{ @Override
public void destroy() {
// TODO Auto-generated method stub } @Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain)throws IOException, ServletException {
//处理乱码,设置字符集为utf-8
servletRequest.setCharacterEncoding("utf-8");
filterChain.doFilter(servletRequest, servletResponse);
} @Override
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub } }
9:最后我使用一张表完成管理员和普通用户的区别,所有有一个user_mark的字段,这里设置一下,使用0和1代表管理员和普通用户,需要注意的是这里开发的是后台,所以只可以添加管理员,所以设置固定即可。
package com.bie.utils;
/***
* 1.9用户管理标识的工具类,用来写管理员和普通用户的标识
* @author biehongli
*
*/
public class MarkUtils { //用户信息的的标识
public static final String USER_MARK_MEMBER="0";//0代表普通会员
public static final String USER_MARK_MANAGER="1";//1代表管理员 }
最后这里暂时还没有直接将插入的信息直接显示在页面上,下篇博客将实现此功能,
下面演示一下实现的效果,如果想看代码结构和源码可以去上面的连接下载即可。
提示信息如下所示:
最后可以先去数据库查看测试数据是否插入成功:
基于jsp+servlet图书管理系统之后台用户信息插入操作的更多相关文章
- 基于jsp+servlet图书管理系统之后台用户信息修改操作
上一篇的博客写的是查询操作,且附有源码和数据库,这篇博客写的是修改操作,附有从头至尾写的代码(详细的注释)和数据库! 此次修改操作的源码和数据库:http://download.csdn.net/de ...
- 基于jsp+servlet图书管理系统之后台用户信息查询操作
上一篇的博客写的是插入操作,且附有源码和数据库,这篇博客写的是查询操作,附有从头至尾写的代码(详细的注释)和数据库! 此次查询操作的源码和数据库:http://download.csdn.net/de ...
- 基于jsp+servlet图书管理系统之后台用户信息删除操作
上一篇的博客写的是修改操作,且附有源码和数据库,这篇博客写的是删除操作,附有从头至尾写的代码(详细的注释)和数据库! 此次删除操作的源码和数据库:http://download.csdn.net/de ...
- 基于jsp+servlet图书管理系统之后台万能模板
前奏: 刚开始接触博客园写博客,就是写写平时学的基础知识,慢慢发现大神写的博客思路很清晰,知识很丰富,非常又价值,反思自己写的,顿时感觉非常low,有相当长一段时间没有分享自己的知识.于是静下心来钻研 ...
- 基于JSP+Servlet开发高校社团管理系统(前台+后台) 源码
基于JSP+Servlet开发高校社团管理系统(前台+后台): 开发环境: Windows操作系统 开发工具:Eclipse/MyEclipse+Jdk+Tomcat+MYSQL数据库 运行效果 ...
- 基于JSP+Servlet开发手机销售购物商城系统(前台+后台)源码
基于JSP+Servlet开发手机销售购物商城系统(前台+后台) 开发环境: Windows操作系统 开发工具:Eclipse/MyEclipse+Jdk+Tomcat+MySQL数据库 运行效果图: ...
- 基于JSP+SERVLET的新闻发布系统(一)
本系统使用的是基于JSP+SERVLET+TOMCAT6 数据库使用的是MYSQL IDE是MYECLIPSE8.5,页面编辑使用的是百度的ueditor,比较适合咱国人 采用MVC模式,使用的关键技 ...
- 11 基于django的图书管理系统 多表
1.需求 作业需求:1.列出图书列表.出版社列表.作者列表2.点击作者,会列出其出版的图书列表3.点击出版社,会列出旗下图书列表4.可以创建.修改.删除 图书.作者.出版社 踩分点:1.满足需求1,2 ...
- ORACLE查询当前连接的用户信息及操作的SQL语句
ORACLE--查询当前连接的用户信息及操作的SQL语句 select sid, status, v$session.username 用户名, last_call ...
随机推荐
- vue实现一个简单的选项卡
用vue来实现一个小的选项卡切换,比之前要简单.方便很多. <!DOCTYPE html> <html lang="en"> <head> &l ...
- Python基础(正则、序列化、常用模块和面向对象)-day06
写在前面 上课第六天,打卡: 天地不仁,以万物为刍狗: 一.正则 - 正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法: - 在线正则工具:http://tool ...
- HDU1233 还是畅通工程【最小生成树】
题意: 求出连接各个村庄最小的公路总长度,把最小公路总长度求出来. 思路: 最小生成树原理,带入数据求得. 代码: prim: #include<iostream> #include< ...
- HashMap原理分析(JDK1.7.x之前)
HashMap 实现Map.Cloneable.Serializable接口,继承AbstractMap基类. HashMap map = new HashMap<String,String&g ...
- python3爬虫二
1.获取列表页文章url集合: scrapy shell http://blog.jobbole.com/all-posts/ response.css('div.post-meta a.archiv ...
- java 多线程一
java 多线程一 java 多线程二 java 多线程三 java 多线程四 java 多线程实现的几种方式: 1.extends Thread 2.implements Runnable 3.im ...
- 深层揭密extern "C"
一. extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的:其次,被它修饰的目标是“C”的.让我们来详细解读这两重含义. (1) 被ext ...
- JavaScript对象复制(二)
<script> function copy(a) { ret = {}; for (sth in a) { temp = a[sth]; if (temp instanceof Arra ...
- 【C++】面试题目:从尾到头打印链表
通过<剑指offer 名企面试官精讲典型编程题>看到一道讲解链表的题目. 题目:输入一个链表的头结点,从尾到头反过来打印出每个结点的值 链表定义如下: typedef struct _NO ...
- python f-string
文章目录 1. 主要内容 1.1. 旧时代的格式化字符串 1.1.1. Option #1: %-formatting 1.1.2. 怎样使用 %-formatting 1.1.3. 为什么 %-fo ...