简体   繁体   中英

Problem with connection to PostgreSQL DB using JDBC

I have a program in which i need to connect to my postgreSQL DB. I have 4 classes:

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);
    }
}

and

public class SQLQueryExecutor {


    public void executeSQL(Connection connection, String sqlContents) throws SQLException {
        try (Statement statement = connection.createStatement()){
            statement.execute(sqlContents);
        }finally {
            connection.close();
        }
    }
}

and

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();
            }
        });
    }
}

and

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");
    }
}

Unfortunately, I have an error: the connection has already been closed. I don't get it why I have this error. Maybe the reason is that I should have the statement initialization and connection closing in one method?

the error:

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)

You call forEach, so you do executeSQL for each line or whatever readFileFromResource returns.

However, executeSQL will close the connection it is given, thus guaranteeing that the second and all further invocations will fail.

You have two style issues with your code setup:

Resources should be closed by the creator

The connection is made in your main , but it is closed in your executeSQL . That's no good. You need to get in the habit of having the creator also be the closer.

You do it right in your SQLQueryExecutor code: The creator of the Statement (which also needs closing) is also the closer; the try-with-resources construct makes it very easy. You need to do the same thing with the connection: Remove that entire finally block, and then do to the creation of your connection the same you do to when you make statements.

Bad exception handling

You're calling forEach. This is bad style - you don't want to use lambdas unless there is a clear advantage, because they have intrinsic disadvantages non-lambda-based code doesn't have: They are not exception transparent, mutable local variable transparent, or control flow transparent.

That is an especially big problem here, given that the call you do for each element may throw a checked exception.

The general rule about exception handling is simply this: IF you catch it, then handle it. Logging it is not handling it. Note that psv main is allowed to (and generally should!) throws Exception , and any method that clearly does SQL things (it's right there in the name!) should probably be declared to throws SQLException . Then all the various catch blocks you have can all go away. This means you have far less code and if an exception does occur, you get better handling (java itself handles it far better than you can, by properly rolling up each method and logging all relevant details, instead of only logging some parts and blithely trucking on, even though all sorts of assumptions are now broken due to the exception having occured).

So, just use for (String line : fr.readFromResource(path)) { } . Calling forEach on a collection is rarely correct. (It makes sense if you already have a Consumer<T> instance ready to go, and not much more than that.

NB: If ever 'just add a throws clause to the method' isn't going to work, then the right ¯\\ (ツ) /¯ no clue how to handle this exception handler is not e.printStackTrace() , that has all sorts of disadvantages. The right way to do it is: throw new RuntimeException("uncaught", e); . Update your editor's templates.

您不应关闭 SQLQueryExecutor 中的连接。

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM