本博文仅供学习记录同时分享给有需要的人,如有大佬觉得文中所写有所缺陷请给予建议,本人会尽快修改。
博主作为本届的一名应届毕业生终于也要离开学校的怀抱步入社畜的步伐当中了,在编写完自己那并不光彩的简历后,在各种招聘网站上不停的投递,终于在7月11号收到了一家正常的公司的笔试以及面试邀请。在去笔试前也是了解了很多网络上很经典的面试笔试题,虽然基础不是很好,但也是自我感觉良好。直到我看到了笔试题目......
笔试题目
一、编程题(编程语言不限)
一个公司中有DM、PM、S三种职位,DM的下级是PM、PM的下级是S,superior中表示着他的上级是谁。name是员工的名字,type是员工的职位、salary是员工的工资,要求输入员工的名字后返回他和他的所有下级的工资总和。
如:
输入A,返回57700
输入B,返回16600
输入E,返回3300
name | type | superior | salary |
A | DM | 10000 | |
B | PM | A | 7500 |
C | PM | A | 6500 |
D | PM | A | 5500 |
E | S | B | 3300 |
F | S | B | 3000 |
G | S | B | 2800 |
H | S | C | 3800 |
I | S | C | 3500 |
J | S | C | 2500 |
K | S | D | 2500 |
L | S | D | 3500 |
M | S | D | 3300 |
直接看题目里的表可能不太直观咱们换一种方式来看。
id | name | type | superior | salary |
1 | A | DM | 0 | 10000 |
2 | B | PM | 1 | 7500 |
3 | C | PM | 1 | 6500 |
4 | D | PM | 1 | 5500 |
5 | E | S | 2 | 3300 |
6 | F | S | 2 | 3000 |
7 | G | S | 2 | 2800 |
8 | H | S | 3 | 3800 |
9 | I | S | 3 | 3500 |
10 | J | S | 3 | 2500 |
11 | K | S | 4 | 2500 |
12 | L | S | 4 | 3500 |
13 | M | S | 4 | 3300 |
这样看起来是不是方便一点,用id对应上级还可以防止出现重名的问题。
根据题中给出的例子发现输入A返回的是57700,也就是说总和不仅仅只有A+B+C+D
B、C、D的下级的工资也需要计算至A的总和里(老板真是黑,工资全都要算在他身上hhhhhhhh),如果E继续有下级的话就要继续加进去直到没有下级为止,这样一来是不是立刻就看出来需要用递归了呢?
接下来开始编程吧,数据还是放到数据库里,咱们先来建一张表
CREATE TABLE salary(
s_id INT NOT NULL AUTO_INCREMENT,
s_name VARCHAR(20) NOT NULL,
s_type VARCHAR(5) NOT NULL,
s_superior INT NOT NULL,
s_salary INT NOT NULL,
PRIMARY KEY(s_id)
)DEFAULT CHARSET=utf-8;
然后往里面输入数据,不用理id让它去自增长吧。INSERT INTO salary(s_name,s_type,s_superior,s_salary)VALUES("A","DM",0,10000);
你可以一个一个打.....也可以随手写个程序让他自己往里面写......好了,咱们先来习惯性的建一个实体类Salary,代码如下
/**
* Created by mengyan on 2017/7/11.
*/
public class Salary {
private Integer salaryId;
private String salaryName;
private String salaryType;
private Integer salarySuperior;
private Integer salarySalary;
//省略get set方法
}
既然要对数据库进行操作,JDBC当然是不能少的,现在开始写一个JDCB的工作类,代码如下/**
* Created by mengyan on 2017/7/11.
*/
public class JDBCUtil {
private static Connection connection = null;
private static PreparedStatement preparedStatement = null;
private static ResultSet resultSet = null;
/**
* 获取连接
* @return connection
*/
private static Connection getConnection(){
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/databaseName?useSSL=true&useUnicode=true&characterEncoding=UTF-8","databaseName","databasePwd");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e){
e.printStackTrace();
}
if(connection != null)
return connection;
return null;
}
/**
* 关闭释放资源
*/
protected static void AllClose(){
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
connection = null;
}
}
if(preparedStatement != null){
try {
preparedStatement.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
preparedStatement = null;
}
}
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
resultSet = null;
}
}
}
}
这样JDBC最基本的获取与释放资源就写好了。现在写一个能根据不同条件查询数据的方法/**
* 查询且返回一个List
* @param sql
* @param objects
* @return list
*/
public static List executeQueryList(String sql ,Object... objects){
List list = new ArrayList();
if(connection==null)
connection=getConnection();
try {
preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i<objects.length;i++){
preparedStatement.setObject(i+1,objects[i]);
}
resultSet=preparedStatement.executeQuery();
while (resultSet.next()){
Salary salary = new Salary();
salary.setSalaryId(resultSet.getInt("s_id"));
salary.setSalaryName(resultSet.getString("s_name"));
salary.setSalaryType(resultSet.getString("s_type"));
salary.setSalarySuperior(resultSet.getInt("s_superior"));
salary.setSalarySalary(resultSet.getInt("s_salary"));
list.add(salary);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
AllClose();
}
return list;
}
测试一下,test代码如下System.out.println(JDBCUtil.executeQueryList("select * from salary where s_salary>?",3000));
结果很明显,这个方法已经成功的查询出来了我们想要的数据,但是这个查询方法总觉得哪里不对劲,如果表和实体类不止salary一个呢,如果还有一个专门存放职位的表type你继续继续使用这个方法怕不是往西瓜盒子里装苹果,盒子空出一大块然后再被老板打一顿。
我们改良一下这个方法
/**
*预处理sql语句
* @param ps
* @param sql
* @param objects
* @return
*/
private static PreparedStatement getPreparedStatement(PreparedStatement ps , String sql,Object... objects){
if(connection==null)
connection=getConnection();
try {
ps = connection.prepareStatement(sql);
for (int i = 0;i<objects.length;i++){
ps.setObject(i+1,objects[i]);
}
} catch (SQLException e) {
e.printStackTrace();
}
return ps;
}
/**
* 查询且返回一个List
* @param sql
* @param objects
* @return list
*/
public static List executeQueryList(String sql,RowToObject make,Object... objects){
List list = new ArrayList();
preparedStatement = getPreparedStatement(preparedStatement,sql,objects);
try {
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
list.add(make.makeRowToObject(resultSet));
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
AllClose(connection,preparedStatement,resultSet);
}
return list;
}
RowToObject.java/**
* Created by mengyan on 2017/7/11.
*/
public interface RowToObject {
public Object makeRowToObject(ResultSet rs);
}
SalaryMapping.java/**
* Created by mengyan on 2017/7/11.
*/
public class SalaryMapping implements RowToObject {
@Override
public Object makeRowToObject(ResultSet rs) {
Salary salary = new Salary();
try {
salary.setSalaryId(rs.getInt("s_id"));
salary.setSalaryName(rs.getString("s_name"));
salary.setSalaryType(rs.getString("s_type"));
salary.setSalarySuperior(rs.getInt("s_superior"));
salary.setSalarySalary(rs.getInt("s_salary"));
} catch (SQLException e) {
e.printStackTrace();
}
return salary;
}
}
我们通过RowToObject接口映射它不同的实现类,包装不同的实体类。使用时传入你想调用的实体类的实现类就好。
JDBCUtil.executeQueryList("select * from salary where s_salary>?",new SalaryMapping(),3000));
如果你想查询职位表type你只需要创建一个包装Type的RowToObject接口的实现类就可以了。至于为什么将预处理sql语句单独作为一个方法拿出来,往下看就知道了。现在对数据的查询方法写好了,问题回到了如何查询和计算工资总数上,既然一开始想到了递归咱们就先按照递归的方法去实现,首先先根据传入的姓名查询到他,然后根据他的id判断他是否有下级如果没有直接返回,如果有则让他的下级继续进行这个操作直到没有下级为止。
代码如下
/**
* Created by mengyan on 2017/7/12.
*/
public class SalaryDaoIpml extends JDBCUtil implements SalaryDao {
/**
* 获得此人需下级工资总和的方法
* @param name
* @return
*/
@Override
public int getSumSalary(String name) {
int sum=0;
String sql = "select * from salary where s_name=?";//使用名字查询的sql语句
List list =this.executeQueryListNoClose(sql,new SalaryMapping(),name);//调用JDBCUtil中的方法查询
for (int i = 0;i<list.size();i++){
Salary salary = (Salary)list.get(i);
sum+=getSumSalary(salary);//调用写好的递归方法
}
return sum;
}
/**
* 不断向下查询是否还有下级并将工资加在一起
* @param salary
* @return
*/
private static int getSumSalary(Salary salary){
int sum = 0;
String sql = "select * from salary where s_superior=?";//使用id查询这个id是否存在下级的sql语句
List list = JDBCUtil.executeQueryList(sql,new SalaryMapping(),salary.getSalaryId());//调用JDBCUtil中的方法查询
if(list.size()!=0){//对查询出的list进行判断如果size不等于0则还存在下级,如果等于零就不存在下级直接返回数值
for (int i = 0; i<list.size();i++){//遍历全部下级
Salary subSalary = (Salary)list.get(i);
sum+=getSumSalary(subSalary);//进行递归
}
}
return sum+salary.getSalarySalary();//返回工资总和
}
}
来测试一下SalaryService salaryService = new SalaryService();
System.out.println(salaryService.getSumSalary("A"));
结果57700
SalaryService salaryService = new SalaryService();
System.out.println(salaryService.getSumSalary("B"));
结果16600
SalaryService salaryService = new SalaryService();
System.out.println(salaryService.getSumSalary("E"));
结果3300
这样整个程序就写完了,但是是不是忘了些什么,咱们在递归的时候每次调用数据库操作的时候都打开关闭资源进行了无数次,所以我决定为JDBCUtil中添加另一个方法
/**
* 查询且返回一个List
* @param sql
* @param make
* @param objects
* @return
*/
public static List executeQueryListNoClose(String sql,RowToObject make,Object... objects){
List list = new ArrayList();
preparedStatement = getPreparedStatement(preparedStatement,sql,objects);
try {
resultSet = preparedStatement.executeQuery();
while (resultSet.next()){
list.add(make.makeRowToObject(resultSet));
}
} catch (SQLException e) {
e.printStackTrace();
}
return list;
}
一个不进行资源关闭的查询方法大家有没有发现预处理SQL语句的方法被复用了?这就是刚从将他单独拿出来的理由,同时在SalaryDaoImpl中也进行一些修改/**
* Created by mengyan on 2017/7/12.
*/
public class SalaryDaoIpml extends JDBCUtil implements SalaryDao {
/**
* 获得此人需下级工资总和的方法
* @param name
* @return
*/
@Override
public int getSumSalary(String name) {
int sum=0;
String sql = "select * from salary where s_name=?";//使用名字查询的sql语句
List list =this.executeQueryListNoClose(sql,new SalaryMapping(),name);//调用JDBCUtil中的方法查询
for (int i = 0;i<list.size();i++){
Salary salary = (Salary)list.get(i);
sum+=getSumSalary(salary);//调用写好的递归方法
}
this.AllClose();
return sum;
}
/**
* 不断向下查询是否还有下级并将工资加在一起
* @param salary
* @return
*/
private static int getSumSalary(Salary salary){
int sum = 0;
String sql = "select * from salary where s_superior=?";//使用id查询这个id是否存在下级的sql语句
List list = JDBCUtil.executeQueryListNoClose(sql,new SalaryMapping(),salary.getSalaryId());//调用JDBCUtil中的方法查询
if(list.size()!=0){//对查询出的list进行判断如果size不等于0则还存在下级,如果等于零就不存在下级直接返回数值
for (int i = 0; i<list.size();i++){//遍历全部下级
Salary subSalary = (Salary)list.get(i);
sum+=getSumSalary(subSalary);//进行递归
}
}
return sum+salary.getSalarySalary();//返回工资总和
}
}
在使用这个方法时一定叫记住关闭资源,这个程序写到这里就完整的结束了,如果有更好的修改方法可以在评论里留言我会很开心。