Java 使用JDBC连接到PostgreSQL数据库时出现问题

Java 使用JDBC连接到PostgreSQL数据库时出现问题,java,jdbc,Java,Jdbc,我有一个程序,我需要连接到我的postgreSQL数据库。我有4门课: public class FileReader { public Stream<String> readFileFromResource(String path) throws URISyntaxException, IOException { Path result = Paths.get(getClass().getClassLoader().getResource(path).toU

我有一个程序,我需要连接到我的postgreSQL数据库。我有4门课:

public class FileReader {
    public Stream<String> readFileFromResource(String path) throws URISyntaxException, IOException {
        Path result = Paths.get(getClass().getClassLoader().getResource(path).toURI());
        return Files.lines(result);
    }
}

不幸的是,我有一个错误:连接已经关闭。我不明白为什么我会有这个错误。可能是因为我应该将语句初始化和连接关闭放在一个方法中

错误:

Table is not created
org.postgresql.util.PSQLException: Connection has already been closed
    at org.postgresql.jdbc.PgConnection.checkClosed(PgConnection.java:885)
    at org.postgresql.jdbc.PgConnection.createStatement(PgConnection.java:1727)
    at org.postgresql.jdbc.PgConnection.createStatement(PgConnection.java:431)
    at school_app.SQLQueryExecutor.executeSQL(SQLQueryExecutor.java:11)
    at school_app.SQLFileExecutor.lambda$0(SQLFileExecutor.java:14)
    at java.base/java.nio.file.FileChannelLinesSpliterator.forEachRemaining(FileChannelLinesSpliterator.java:117)
    at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
    at school_app.SQLFileExecutor.executeSQLFile(SQLFileExecutor.java:12)
    at school_app.SchoolApp.main(SchoolApp.java:17)

您可以调用forEach,因此对每一行或任何
readFileFromResource
返回的内容执行
executeSQL

但是,
executeSQL
将关闭给定的连接,从而保证第二次和所有后续调用都将失败

您的代码设置存在两个样式问题:

资源应由创建者关闭 连接是在
main
中建立的,但在
executeSQL
中是关闭的。那不好。你需要养成这样的习惯,让造物主也离你更近

您可以在SQLQueryExecutor代码中正确地执行此操作:语句的创建者(也需要关闭)也是最接近的;try-with-resources结构使它非常容易。您需要对连接执行相同的操作:删除整个
finally
块,然后像创建语句一样创建连接

错误异常处理 你在给forEach打电话。这是一种糟糕的风格——除非有明显的优点,否则您不想使用lambda,因为它们有非lambda代码所没有的固有缺点:它们不是异常透明、可变局部变量透明或控制流透明

这在这里是一个特别大的问题,因为您对每个元素所做的调用可能会抛出一个选中的异常

关于异常处理的一般规则很简单:如果捕捉到它,那么处理它。记录它是而不是处理它。请注意,
psv main
被允许(并且通常应该!)
抛出异常
,任何明确执行SQL操作的方法(它就在名称中!)都可能应该声明为
抛出SQLException
。那么你所有的各种各样的拦网都可以消失。这意味着您的代码要少得多,如果发生异常,您将得到更好的处理(java本身处理它的能力要比您强得多,它正确地汇总了每个方法并记录了所有相关的细节,而不是只记录一些部分并愉快地继续进行,尽管现在由于发生了异常,各种假设都被打破了)

因此,只需使用
for(字符串行:fr.readFromResource(path)){}
。在集合上调用
forEach
很少是正确的。(如果您已经有一个
消费者
实例准备就绪,那么这是有意义的,而且仅此而已。)


注意:如果“只是在方法中添加一个
抛出
子句”不起作用,那么正确的\(ツ)/“
e.printStackTrace()
不知道如何处理此异常处理程序,这有各种缺点。正确的方法是:
抛出新的RuntimeException(“uncaught”,e)更新编辑器的模板。

您不应该关闭SQLQueryExecutor中的连接。

您正在关闭
SQLQueryExecutor中的连接,并再次尝试重新使用该连接。您不应该关闭
SQLQueryExecutor中的连接,而应该关闭在
SchoolApp.main中打开的连接非常感谢!这几乎是一个完美的答案。我要补充的一点是,
FileReader
返回的
引用的是一个文件,因此它也需要关闭,但它不是。这也应该在
SQLFileExecutor.executeSQLFile
中尝试使用资源。
public class SQLFileExecutor {
    public void executeSQLFile(Connection connection, String path) throws URISyntaxException, IOException {
        FileReader fr = new FileReader();
        SQLQueryExecutor sqlQueryExecutor = new SQLQueryExecutor();
        fr.readFileFromResource(path).forEach(i -> {
            try {
                sqlQueryExecutor.executeSQL(connection, i);
            } catch (SQLException e) {
                System.out.println("Table is not created");
                e.printStackTrace();
            }
        });
    }
}
public class SchoolApp {
    private static final String USER = "user1";
    private static final String PASSWORD = "01234";
    private static final String URL = "jdbc:postgresql://localhost:5432/school";
    
    public static void main(String[] args) throws SQLException, URISyntaxException, IOException {
        Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
        SQLFileExecutor sqlFileExecutor = new SQLFileExecutor();
        sqlFileExecutor.executeSQLFile(connection, "CreateTables.sql");
    }
}
Table is not created
org.postgresql.util.PSQLException: Connection has already been closed
    at org.postgresql.jdbc.PgConnection.checkClosed(PgConnection.java:885)
    at org.postgresql.jdbc.PgConnection.createStatement(PgConnection.java:1727)
    at org.postgresql.jdbc.PgConnection.createStatement(PgConnection.java:431)
    at school_app.SQLQueryExecutor.executeSQL(SQLQueryExecutor.java:11)
    at school_app.SQLFileExecutor.lambda$0(SQLFileExecutor.java:14)
    at java.base/java.nio.file.FileChannelLinesSpliterator.forEachRemaining(FileChannelLinesSpliterator.java:117)
    at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
    at school_app.SQLFileExecutor.executeSQLFile(SQLFileExecutor.java:12)
    at school_app.SchoolApp.main(SchoolApp.java:17)