java打印表格 将ResultSet中的数据打印成表格

问题描述

MySQL的查询语句输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
mysql> select * from instructor;
+-------+------------+------------+-----------+
| ID | name | dept_name | salary |
+-------+------------+------------+-----------+
| 10101 | Srinivasan | Comp. Sci. | 65000.00 |
| 12121 | Wu | Finance | 90000.00 |
| 15151 | Mozart | Music | 40000.00 |
| 22222 | Einstein | Physics | 95000.00 |
| 25566 | Brown | Biology | 100000.00 |
| 30765 | Green | Music | NULL |
| 32343 | El Said | History | 60000.00 |
| 33456 | Gold | Physics | 87000.00 |
| 45565 | Katz | Comp. Sci. | 75000.00 |
| 58583 | Califieri | History | 62000.00 |
| 76543 | Singh | Finance | 80000.00 |
| 76766 | Crick | Biology | 72000.00 |
| 77987 | Kim | Physics | 98000.00 |
| 83821 | Brandt | Comp. Sci. | 92000.00 |
| 88877 | Perry | Finance | 125000.00 |
| 88878 | Perry | Finance | 125000.00 |
| 98345 | Kim | Elec. Eng. | 80000.00 |
+-------+------------+------------+-----------+
17 rows in set (0.04 sec)

使用JDBC执行上述的查询语句,将结果集中的查询结果以表格的形式打印出来。

思路

  • 通过结果集的元数据可以知道结果集中的列数,和列名.
  • 然后遍历结果集,分别统计每一列中的最大字符数。
  • 然后通过System.out.printf()方法进行格式化输出。

字符串格式化方式

左对齐

%-10s表示这个字符串的长度为10,不足10的地方以空格填充,带-的表示左对齐.

1
2
System.out.printf("属性:%-10s ", rsmd.getColumnName(i));
System.out.printf("类型:%-10s\n", rsmd.getColumnTypeName(i));
1
2
3
4
属性:ID         类型:VARCHAR   
属性:name 类型:VARCHAR
属性:dept_name 类型:VARCHAR
属性:salary 类型:DECIMAL

右对齐

%10s表示这个字符串的长度为10,不足10的地方以空格填充,默认右对齐

1
2
System.out.printf("属性:%10s ", rsmd.getColumnName(i));
System.out.printf("类型:%10s\n", rsmd.getColumnTypeName(i));
1
2
3
4
属性:        ID 类型:   VARCHAR
属性: name 类型: VARCHAR
属性: dept_name 类型: VARCHAR
属性: salary 类型: DECIMAL

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package tools;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;

/**
* 结果集打印机.将结果集中的数据打印成表格.
*/
public class ResultSetPrinter {
public static void printResultSet(ResultSet rs) throws SQLException {
ResultSetMetaData resultSetMetaData = rs.getMetaData();
// 获取列数
int ColumnCount = resultSetMetaData.getColumnCount();
// 保存当前列最大长度的数组
int[] columnMaxLengths = new int[ColumnCount];
// 缓存结果集,结果集可能有序,所以用ArrayList保存变得打乱顺序.
ArrayList<String[]> results = new ArrayList<>();
// 按行遍历
while (rs.next()) {
// 保存当前行所有列
String[] columnStr = new String[ColumnCount];
// 获取属性值.
for (int i = 0; i < ColumnCount; i++) {
// 获取一列
columnStr[i] = rs.getString(i + 1);
// 计算当前列的最大长度
columnMaxLengths[i] = Math.max(columnMaxLengths[i], (columnStr[i] == null) ? 0 : columnStr[i].length());
}
// 缓存这一行.
results.add(columnStr);
}
printSeparator(columnMaxLengths);
printColumnName(resultSetMetaData, columnMaxLengths);
printSeparator(columnMaxLengths);
// 遍历集合输出结果
Iterator<String[]> iterator = results.iterator();
String[] columnStr;
while (iterator.hasNext()) {
columnStr = iterator.next();
for (int i = 0; i < ColumnCount; i++) {
// System.out.printf("|%" + (columnMaxLengths[i] + 1) + "s", columnStr[i]);
System.out.printf("|%" + columnMaxLengths[i] + "s", columnStr[i]);
}
System.out.println("|");
}
printSeparator(columnMaxLengths);
}

/**
* 输出列名.
*
* @param resultSetMetaData 结果集的元数据对象.
* @param columnMaxLengths 每一列最大长度的字符串的长度.
* @throws SQLException
*/
private static void printColumnName(ResultSetMetaData resultSetMetaData, int[] columnMaxLengths) throws SQLException {
int columnCount = resultSetMetaData.getColumnCount();
for (int i = 0; i < columnCount; i++) {
// System.out.printf("|%" + (columnMaxLengths[i] + 1) + "s", resultSetMetaData.getColumnName(i + 1));
System.out.printf("|%" + columnMaxLengths[i] + "s", resultSetMetaData.getColumnName(i + 1));
}
System.out.println("|");
}

/**
* 输出分隔符.
*
* @param columnMaxLengths 保存结果集中每一列的最长的字符串的长度.
*/
private static void printSeparator(int[] columnMaxLengths) {
for (int i = 0; i < columnMaxLengths.length; i++) {
System.out.print("+");
// for (int j = 0; j < columnMaxLengths[i] + 1; j++) {
for (int j = 0; j < columnMaxLengths[i]; j++) {
System.out.print("-");
}
}
System.out.println("+");
}

}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package tools;
import java.sql.*;

public class Test {
private static String driver = "com.mysql.cj.jdbc.Driver";
private static String URL = "jdbc:mysql://127.0.0.1:3306/university?serverTimezone=UTC";
private static String user = "root";
private static String password = "root";

/**
* JDBC测试.
*/
private static void JDBCexample() {
// 1.加载数据库驱动
try {

Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
testStatement();
}

/**
* 测试Statement的用法.
*/
private static void testStatement() {
try (// 2.打开数据库连接
Connection conn = DriverManager.getConnection(URL, user, password);
// 3.创建语句
Statement stmt = conn.createStatement()) {
// 模拟SQL注入
testSqlInjecton(stmt);

} catch (SQLException sqle) {
System.out.println("SQLException : " + sqle);
}
}

/**
* 模拟SQL注入.
*
* @param stmt Statement对象.
* @throws SQLException
*/
private static void testSqlInjecton(Statement stmt) throws SQLException {
String name = "X' or 'Y' = 'Y";
String sql = "select * from instructor where name = '" + name + "'";
ResultSet rs = stmt.executeQuery(sql);
ResultSetPrinter.printResultSet(rs);
}

public static void main(String[] args) {
JDBCexample();
}
}

运行结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+-----+----------+----------+---------+
| ID| name| dept_name| salary|
+-----+----------+----------+---------+
|10101|Srinivasan|Comp. Sci.| 65000.00|
|12121| Wu| Finance| 90000.00|
|15151| Mozart| Music| 40000.00|
|22222| Einstein| Physics| 95000.00|
|25566| Brown| Biology|100000.00|
|30765| Green| Music| null|
|32343| El Said| History| 60000.00|
|33456| Gold| Physics| 87000.00|
|45565| Katz|Comp. Sci.| 75000.00|
|58583| Califieri| History| 62000.00|
|76543| Singh| Finance| 80000.00|
|76766| Crick| Biology| 72000.00|
|77987| Kim| Physics| 98000.00|
|83821| Brandt|Comp. Sci.| 92000.00|
|88877| Perry| Finance|125000.00|
|88878| Perry| Finance|125000.00|
|98345| Kim|Elec. Eng.| 80000.00|
+-----+----------+----------+---------+

最新版MySQL数据库驱动的问题

问题1 驱动名称不对

1
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

解决方案

将数据库驱动名称com.mysql.jdbc.Driver改成com.mysql.cj.jdbc.Driver

问题2 没有指定时区

1
SQLException : java.sql.SQLException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.

解决方案

1
jdbcUrl=jdbc:mysql://localhost:3306/spring

后面加上?serverTimezone=UTC,如下所示:

1
jdbcUrl=jdbc:mysql://localhost:3306/spring?serverTimezone=UTC

问题3 查询字符串分隔符没有使用转义字符

如果有多个查询字符串要用&隔开,并且&要转义成&amp;
但如果你的jdbcUrl类似下面:
jdbcUrl=jdbc:mysql://localhost:3306/spring?serverTimezone=UTC&characterEncoding=utf-8
就是有多个params的时候需要以&分开,但&要改为&amp; 如下:
jdbc:mysql://localhost:3306/spring?serverTimezone=UTC&amp;characterEncoding=utf-8

不需要转义的情况

经过为的实践,也不一定都要对&进行转义,在使用Mybatis时,如果把URL写在.properties文件中,然后在mybatis配置文件中通过${}来引用,这种情况下不需要对URL中&进行转义.

旧版本的navicat无法连接mysql8.0

问题描述

最新安装了最新的MySQL8.0,但是使用navicat9.1无法连接,报错如下:

1
Client does not support authentication protocol requested by server; consider upgrading MySQL client

原因

应该是MySQL8.0的加密方式改变了,但是比较老版本的navicate没有来得及支持,因此需要将mysql设置为支持传统的那种密码加密。

解决方案

登入MySQL的命令行界面,然后输入:

1
2
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'you_password';
FLUSH PRIVILEGES;

修改上面的you_password为你数据库root用户的密码即可.

参考链接

https://blog.csdn.net/m_amazing/article/details/84313789

问题描述

今天使用如下的子查询时:

1
2
3
4
5
6
select dept_name,avg_salary
from ( select dept_name, avg(salary) as avg_salary
from instructor
group by dept_name
)
where avg_salary>42000;

报了:Every derived table must have its own alias这个错误

1
2
3
4
5
6
7
mysql> select dept_name,avg_salary
from ( select dept_name, avg(salary) as avg_salary
from instructor
group by dept_name
)
where avg_salary>42000;
1248 - Every derived table must have its own alias

解决方案

Every derived table must have its own alias翻译成中文就是:每个派生表都必须有自己的别名
所以给子查询取一个别名即可:

1
2
3
4
5
6
select dept_name,avg_salary
from ( select dept_name, avg(salary) as avg_salary
from instructor
group by dept_name
) as I
where avg_salary>42000;

这样就能正常执行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mysql> select dept_name,avg_salary
from ( select dept_name, avg(salary) as avg_salary
from instructor
group by dept_name
) as I
where avg_salary>42000;
+------------+--------------+
| dept_name | avg_salary |
+------------+--------------+
| Biology | 72000 |
| Comp. Sci. | 77333.333333 |
| Elec. Eng. | 80000 |
| Finance | 85000 |
| History | 61000 |
| Physics | 91000 |
+------------+--------------+
6 rows in set

cmd切换到 新版控制台 后没有显示文字怎么办

问题描述

今天从旧版控制台切换到新版控制台后,弹出的cmd是个黑框框,上面没有文字,好奇怪.

在这里插入图片描述

解决方案

在cmd窗体顶部右键,选择属性
在这里插入图片描述然后切换到终端选项卡,在终端颜色那一块,取消勾选使用单独的前景即可。

在这里插入图片描述这样就有文字了。
在这里插入图片描述

如何切换VScode里面的java版本

最近用VScode打开新版本的java代码,发现无法通过编译,这肯定是因为我用的旧版本的java

1. 电脑使用新版本的java

排查后发现是java版本不对,然后我把电脑上java版本切换为新版,然后发现,通过其他编译可以正常编译运行,但是VSCode里面就是无法编译运行,个人猜测VScode里面使用的还是旧版本的java,网上找了一通没有找到该怎么切换VScode里面的java版本.

2. 重装VScode里面的java环境

保证现在电脑里用的是新版本的java,然后卸载VScode里面的java环境(Java Extension Pack),然后重启VScode,再重新安装Java插件(Java Extension Pack),这个使用插件就会使用电脑里的新版本的java.

总结

这样做就是重装插件比较耗时,不过还算能用,有人知道怎么切换VScode里面的java版本的话教教我.

安装了多个java 如何切换java版本

问题描述

平常用的是java8,最近在学习java的新特性。这就需要从java8往更高的java版本切换。由于还在使用java8,测试完新特性后我需要再切换回java8.

如何切换

安装java的时候我们会配置JAVA_HOME这个环境变量.所有要切换java版本,只需要修改JAVA_HOME即可,在JAVA_HOME里面写上java8的安装目录,那就使用的是java8,JAVA_HOME里面写上java9的安装目录,则使用的是java9

解决方案

手动切换

手动修改,JAVA_HOME中的地址即可,这里不介绍。

在cmd中临时切换java版本

例如现在在cmd中,临时需要用到java9的jshell,可以输入如下命令进行设置:

1
set path=E:\java\java9\jdk-9\bin;%path%

其中:

1
E:\java\java9\jdk-9\bin

是jdk9的安装路径.

批处理实现

因为手动修改JAVA_HOME,太过繁琐,所以通过一个批处理程序来实现.

新增环境变量

新增JAVA_HOME_8,JAVA_HOME_9,JAVA_HOME_11分别写入java8,java9,java11的安装目录,这和配置JAVA_HOME一样.

批处理脚本

创建一个名为快捷方式的目录。

TJava.bat

在这个快捷方式目录下新建TJava.bat,写入如下命令:

1
2
3
@echo off
java -version
pause

运行这个批处理就可以看到当前用的是哪个java版本了.这里使用批处理是为了快速运行这个java -version命令,不用手动操作。

CJavaBat.bat

然后在快捷方式下,创建批处理脚本CJavaBat.bat,开头的Cchange的意思.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@echo off
@echo ------------------------------------------------
@echo 当前Java版本为:
call TJava.bat
@echo ------------------------------------------------
@echo 输入要使用的java版本对应的选项:
@echo 选项 含义
@echo 8 切换环境为JDK8
@echo 9 切换环境为JDK9
@echo 11 切换环境为JDK11
@echo ------------------------------------------------
set /P choose=请输入选择:
IF "%choose%" EQU "8" (
REM 修改JAVA_HOME环境变量为%JAVA_HOME_8%,
setx "JAVA_HOME" "%%JAVA_HOME_8%%" /m
echo 已经修改 "JAVA_HOME" 为 %%JAVA_HOME_8%%
) ELSE IF "%choose%" EQU "9" (
setx "JAVA_HOME" "%%JAVA_HOME_9%%" /m
echo 已经修改 "JAVA_HOME" 为 %%JAVA_HOME_9%%
REM setx "Path" "%cd%;%path%" /m
) ELSE IF "%choose%" EQU "11" (
setx "JAVA_HOME" "%%JAVA_HOME_11%%" /m
echo 已经修改 "JAVA_HOME" 为 %%JAVA_HOME_11%%
)
pause

上面程序中的setx命令就是用来修改环境变量的.语法格式为:setx 环境变量名 环境变量值 /msetx设置环境变量需要管理员权限,以管理员身份,运行上面的CJavaBat.bat就可以修改JAVA_HOME环境变量的内容,从而切换Java版本。

CJavaBat.bat设置管理员权限

创建CJavaBat.bat的快捷方式,然后将这个快捷方式重命名为CJava,注意一定要修改名字。
然后在快捷方式上面右键。选择属性,在快捷方式选项卡上,点击高级,然后勾选上用管理员身份运行
这样以后直接点击CJava这个快捷方式就可以以管理身份运行CJavaBat.bat这个批处理脚本了。
在这里插入图片描述在这里插入图片描述

配置CJava到Path环境变量

将这个快捷方式目录配置到Path环境变量中,这样系统会在这个目录下查找程序,

通过Win+R运行CJava

然后就可以按下Win+R来启动运行,结束输入CJava,即可启动程序。

CJava程序说明

CJava中输入选项:8,则切换到Java8,输入9,则切换到Java9,输入11则切换到java11,依次类推,可以拓展上面的程序,以支持切换更多的java版本。

CJava运行效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
------------------------------------------------
当前Java版本为:
java version "1.8.0_221"
Java(TM) SE Runtime Environment (build 1.8.0_221-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)
请按任意键继续. . .
------------------------------------------------
输入要使用的java版本对应的选项:
选项 含义
8 切换环境为JDK8
9 切换环境为JDK9
11 切换环境为JDK11
------------------------------------------------
请输入选择:11
成功: 指定的值已得到保存。
已经修改 "JAVA_HOME" 为 %JAVA_HOME_11%
请按任意键继续. . .

可以看到切换之前是Java8,切换后为Java11

查看是否切换成功

运行上面创建TJava.bat,查看运行效果:

1
2
3
4
java version "11.0.4" 2019-07-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.4+10-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.4+10-LTS, mixed mode)
请按任意键继续. . .

可以看到现在已经成功切换为Java11

解决 String concatenation as argument to ‘StringBuffer.append()’ call 警告

问题描述

最近带idea中老是收到下面这个警告:

1
Warning:(20, 16) String concatenation as argument to 'StringBuffer.append()' call

出现警告的代码:

1
sb.append("<property name=\"" + group1 + "\" value=\"${" + group1+ "}\"/>\r\n");

分析原因

其实这个提示是表示方法使用不恰当,因为StringBuffer对象的append方法目的就是做对象拼接用的,里面再通过”+“来拼接就不得当,这是编译器对Java的代码规范。

解决方案

1. 全部使用append方法

替换+号为).append(

因为append方法的返回值还是StringBuffer,所以可以链式调用,我们可以通过链式调用来替换掉里面的加号。
也就是,将:

1
sb.append("<property name=\"" + group1 + "\" value=\"${" + group1+ "}\"/>\r\n");

替换成:

1
sb.append("<property name=\"").append(group1).append("\" value=\"${").append(group1).append("}\"/>\r\n");

使用IDE的查找替换功能进行替换

当然,手动替换可能有些繁琐,需要使用查找替换功能,

  1. 查找:+
  2. 替换为:).append(即可。

写代码实现替换

核心代码如下:

1
2
3
4
5
6
7
8
9
/**
* 把StringBuffer或StringBuilder中的字符串连接符<code>+</code>替换append()方法
*
* @return 替换后的字符串.
*/
public String StringConnectorToAppend(String stringBufferAppendCode) {
String returnValue = "// " + stringBufferAppendCode + "\n";
return returnValue + stringBufferAppendCode.replaceAll("\\s*\\+\\s*", ").append(");
}

2. 忽略

这个不替换也能运行,而且替换后代码长度变长了,不好阅读,可以选择忽略.

idea Shift+Alt+I快捷键是弹出TouchPad Driver Diagnostics的问题

原因

Shift+Alt+LSynaptics(新思)触摸板驱动中用于触摸板测试的快捷键

解决方案:禁用触控板

以管理员身份运行cmd,输入如下命令:

1
reg add HKEY_LoCAL_MACHINE\sYsTEM\CurrentControlset\services\synTP\Parameters\Debug /v DumpKernel /d 00000000 /t REG_DwoRD /f

重启电脑后生效

运行后,不会立即生效,需要重启电脑才会生效.