摘要: Java实现一个简单的格式化信函生成器。数据库+模板的简单应用例子。
难度: 初级。
童鞋们都应该见过各种各样的请柬咯, 请柬上面通常是一个模板,然后填上被邀请人及相关信息,在 Word 里面称之为 “邮件合并”特性。 今天,我们就来实现一个简单的格式化信函生成器。 我们将要做的东西, 包括一个非常简单的学生选课数据库,以及简单的模板, 利用数据库中的记录及模板生成格式化的通知信息。 关于C语言的实现, 请参考 《编程珠玑I》(第二版) 的第三章第二节。
别看这个东西简单, 其中也蕴涵了不变和变化的部分。 不变是信息模板内容, 变化部分是数据库中的记录信息, 所要做的,就是将不变和变化隔离开来。 基本的设计思想如下:
1、 一个非常简单的学生选课数据库, 包括学生信息表 students , 课程信息表 courses, 学生选课信息表 stus_cours;
2、 一个JDBC 实用工具类 JDBCUtil , 主要是从数据库中检索记录, 并将其转换为相应的记录对象 DBRecord;
3、 记录对象 DBRecord: 包含一个 String[] 数组, 用来存储每条记录中的数据项,一个 String 对应一条基本数据项。
4、 模板信息接口: interface Template , 包含一个 getText() 的方法声明;
5、 两个模板实现: 课程开设信息 CourseInfoTemplate 及 学生选课信息 CourseChosenTemplate
6、 格式化信函生成器 FormattedLetter : 根据数据库记录及模板生成格式化通知
因为这个东西并不复杂, 这里就不讲解太多了, 相信童鞋们看过代码之后很快就会明白的。下面给出代码实现:
### course.sql
##############################################
# course.sql: 建立一个简单的学生选课数据库
# NOTE: 必须保存为 utf-8 格式(文件 - 另存为),
# 以保证在命令行下执行该文件能够顺利插入中文
##############################################
##############################################
# how to exec sql file
# mysql -u root -p < /your_path/course.sql
# Enter password: [input your root password]
##############################################
# 或者使用 root 登录 mysql,
# 然后复制所有语句在 mysql 中执行。
#############################################
######################################
# mysql statement:
######################################
CREATE USER 'course6'@'localhost' IDENTIFIED BY 'course6';
CREATE DATABASE course;
grant all on course.* to 'course6'@'localhost';
#####################################
USE course;
CREATE TABLE students (
id INT NOT NULL AUTO_INCREMENT,
stuName varchar(12) NOT NULL,
PRIMARY KEY(id)
) ;
CREATE TABLE courses (
id INT NOT NULL AUTO_INCREMENT,
cName varchar(15) NOT NULL,
cTime int NOT NULL,
note varchar(30),
PRIMARY KEY(id)
) ;
CREATE table stus_cours (
stu_id int NOT NULL,
cour_id int NOT NULL,
sch_term varchar(5) NOT NULL,
PRIMARY KEY(stu_id, cour_id)
);
alter table stus_cours add constraint stuCons foreign key(stu_id) references students(id);
alter table stus_cours add constraint courCons foreign key(cour_id) references courses(id);
desc courses;
desc students;
desc stus_cours;
insert into students values(10001, '小华');
insert into students values(10002, '小明');
insert into courses values(1001, '高数', 54, '');
insert into courses values(1002, '大学英语', 81, '');
insert into stus_cours values(10001, 1001, '2nd');
insert into stus_cours values(10002, 1001, '2nd');
insert into stus_cours values(10001, 1002, '1st');
insert into stus_cours values(10002, 1002, '1st');
SELECT * from students;
SELECT * from courses;
SELECT * from stus_cours;
SELECT stuName , cName, cTime , sch_term
FROM students, courses, stus_cours
WHERE students.id = stus_cours.stu_id
AND courses.id = stus_cours.cour_id;
JDBCUtil.java
package javatech.JDBClearn;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.mysql.jdbc.ResultSetMetaData;
import com.mysql.jdbc.Statement;
public class JDBCUtil {
private static Connection conn;
private static final String mysqlDriver = "com.mysql.jdbc.Driver";
private static final String mysqlURL = "jdbc:mysql://localhost:3306/course";
private static final String username = "course6";
private static final String password = "course6";
private JDBCUtil() { }
public static Connection getConnection() throws SQLException, ClassNotFoundException {
Class.forName(mysqlDriver);
conn = DriverManager.getConnection(mysqlURL, username, password);
return conn;
}
public static void printResult(ResultSet rs) throws SQLException
{
ResultSetMetaData rsmd = (ResultSetMetaData) rs.getMetaData();
int colNum = rsmd.getColumnCount();
while(rs.next()) {
for (int i = 0; i < colNum; i++)
System.out.printf(rs.getString(i+1) + " ");
System.out.println();
}
}
public static List<DBRecord> toRecords(ResultSet rs) throws SQLException
{
List<DBRecord> records = new ArrayList<DBRecord>();
ResultSetMetaData rsmd = (ResultSetMetaData) rs.getMetaData();
int colNum = rsmd.getColumnCount();
while(rs.next()) {
String[] datas = new String[colNum];
for (int i = 0; i < colNum; i++)
datas[i] = rs.getString(i+1);
records.add(new DBRecord(datas));
}
rs.close();
conn.close();
return records;
}
public static ResultSet getResultSet(String sqlString) throws SQLException, ClassNotFoundException
{
Connection conn = getConnection();
Statement stm = (Statement) conn.createStatement();
ResultSet rs = stm.executeQuery(sqlString);
return rs;
}
public static void main(String[] args) throws SQLException, ClassNotFoundException
{
String sqlString = "SELECT stuName, cName, cTime, sch_term " +
"FROM students, courses, stus_cours " +
"WHERE students.id = stu_id AND " +
" courses.id = cour_id; ";
ResultSet rs = getResultSet(sqlString);
printResult(rs);
rs.close();
conn.close();
}
}
DBRecord.java
package javatech.JDBClearn;
/**
* DBRecord: 与数据库中一条行记录相对应的数据对象
*
*/
public class DBRecord {
// 将数据库中检索到的一条记录转化为字符串并存储
private String[] datas;
public DBRecord(String[] datas) {
this.datas = datas;
}
// 返回数据库中一条记录的第 index 个数据项
public String getDataTerm(int index) {
if (index < 0 || index > datas.length-1) {
throw new ArrayIndexOutOfBoundsException("数组下标越界!");
}
return datas[index];
}
// 返回数据库一条记录所含的数据项的数目
public int dataTermNum() {
return datas.length;
}
public String toString() {
StringBuilder sb = new StringBuilder("[DBRecord]: ");
for (String s: datas) {
sb.append(s);
sb.append(' ');
}
sb.append('\n');
return sb.toString();
}
}
FormattedLetter.java
package javatech.JDBClearn;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class FormattedLetter {
/**
* 用数据库中每条记录的数据项依序替换模板中的模板数据。
* @param record 数据库中的一条记录
* @param tpl 指定模板文件接口
* @return 替换后的格式化信函
*/
public String formattedLetter(DBRecord record, Template tpl)
{
String text = tpl.getText();
for (int i=0; i < record.dataTermNum(); i++)
text = text.replaceAll("\\$" + (i+1), record.getDataTerm(i));
return text;
}
/**
* 批量生成格式化信函
*/
public List<String> batFormattedLetter(List<DBRecord> records, Template tpl)
{
List<String> letters = new ArrayList<String>();
for (DBRecord r: records) {
letters.add(formattedLetter(r, tpl));
}
return letters;
}
/**
* printList: 打印列表内容
*/
public static <T> void printList(List<T> list)
{
System.out.println("List: [");
for (T t: list) {
System.out.println(t);
}
System.out.println("]");
}
public static void test(String sqlStmt, Template tpl) throws SQLException, ClassNotFoundException
{
ResultSet rs = JDBCUtil.getResultSet(sqlStmt);
List<DBRecord> records = JDBCUtil.toRecords(rs);
List<String> letters = new FormattedLetter().batFormattedLetter(records , tpl);
System.out.println("Formatted letters: ");
printList(letters);
}
public static void main(String[] args) throws SQLException, ClassNotFoundException
{
String courseInfoSql = "SELECT cName, cTime FROM courses";
test(courseInfoSql, new CourseInfoTemplate());
String courseChosenString = "SELECT stuName, cName, cTime, sch_term " +
"FROM students, courses, stus_cours " +
"WHERE students.id = stu_id AND " +
" courses.id = cour_id; ";
test(courseChosenString, new CourseChosenTemplate());
}
}
CourseInfoTemplate.java
package javatech.JDBClearn;
public class CourseInfoTemplate implements Template {
private String text = "同学们请注意: 本学期开设的课程有 $1, 共 $2 学时,请勿错过!";
public String getText()
{
return text;
}
}
CourseChosenTemplate.java
package javatech.JDBClearn;
public class CourseChosenTemplate implements Template {
private String text =
"$1 , 你好! 你所选修的课程是 $2, 共 $3 学时, 在 $4 上, 请勿错过。 ";
public String getText()
{
return text;
}
}
Template.java
package javatech.JDBClearn;
public interface Template {
String getText();
}
末记: 可以改进的地方
1、 使用 replaceAll 方法替换模板文件中的多个模板数据效率可能会有些低下, 有待改进;
2、 必须根据数据库中检索到的记录的数据项顺序及模板中定义的模板项依序对应地替换,而不能无序的任意替换;
3、 模板文件及格式化信函生成器还可以做得更灵活一点, 比如,添加输入输出流, 使之模板文件可以来自于各种输入流而不是简单的字符串, 格式化信息可以输出到各种目标流中,而不仅仅是控制台。