2

我正在开发一个 Vaadin Flow(14.1 版)应用程序,但我遇到了这个问题,我没有让它直接与 MySQL 数据库连接。

我已经建立了与 maven 的 JDBC 连接,我还创建了一个名为 Datasource 的单例类,用于存储我的值和方法。但是现在它只有一个,因为我正在测试它,这就是我想要做的:

  • 单击应用程序上的按钮并更新标签

这是按钮单击侦听器:

button.addClickListener(click -> {
        label.setText(Datasource.getInstance().getUsername());
    });

这是 Datasource 类方法:

public String getUsername() {
    String username = "QUERY-FAILED";

    try {
        start();
        statement = conn.createStatement();
        ResultSet rs = statement.executeQuery("select * from names");

        rs.next();
        username = rs.getString(1);
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close();
    }

    return username;
}

但是标签不会更新,如果我评论 try 块它会更新为 QUERY-FAILED 这是我用来测试它是否失败的字符串,但如果它没有被评论,标签就会保持不变。

我还尝试向 Datasource 类添加一个 main 方法并将其作为 Java 应用程序运行,并且该方法工作正常,它确实返回了一个带有用户名的字符串。所以我猜我被困在与 vaadin 应用程序的连接之间的某个地方。此外,如果我在启动应用程序时尝试在我的 vaadin 应用程序中获取用户名字符串(而不是使用点击侦听器),我会收到一大堆错误,其中数据源在此处指示 nullpointerexception:

statement = conn.createStatement();

提前致谢!

4

2 回答 2

1

我无法发现您的代码有任何问题。但我可以提供一个完整的工作示例应用程序供您比较

我的示例应用程序遵循您的问题代码中列出的行。Vaadin使用用户名表中Button的对象执行数据库查询。DataSource找到的第一行的值显示在Label网页上的 Vaadin 小部件中。

在此处输入图像描述

这个应用程序是使用 Vaadin 14.1.5 构建和运行的,使用Vaadin.com 网站提供的“Plain Java Servlet”风格的入门项目。使用捆绑的 Jetty Web 容器在 macOS Mojave 上运行。

我对他们的 Maven POM 文件的唯一更改是更改为 Java 版本 13,并添加了对H2 数据库引擎的依赖项,以使其成为使用内存数据库的自包含示例。

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>1.4.200</version>
    </dependency>

我使用启动 Vaadin 的钩子来建立我的DataSource对象并初始化数据库。按照手册,嵌套在resources文件夹中,我创建了文件夹META-INF> services。根据Java SPI工具,我在那里创建了一个名为的文件,com.vaadin.flow.server.VaadinServiceInitListener其中包含一行来指定我的类的名称,该类实现了以该文件的名称命名的接口:

work.basil.example.ApplicationServiceInitListener

也就是说,我的ApplicationServiceInitListener类实现了 Vaadin 接口VaadinServiceInitListener。当我的 Vaadin Web 应用程序启动时,我的类将自动实例化,并通过该 Java SPI 工具调用其方法。

我的ApplicationServiceInitListener班级:

package work.basil.example;

import com.vaadin.flow.server.ServiceInitEvent;
import com.vaadin.flow.server.VaadinServiceInitListener;
import org.h2.jdbcx.JdbcDataSource;

public class ApplicationServiceInitListener implements VaadinServiceInitListener
{
    @Override
    public void serviceInit ( ServiceInitEvent serviceInitEvent )
    {
        System.out.println( "DEBUG Running `serviceInit` of " + this.getClass().getCanonicalName() );

        // Database work.
        prepareDataSource();
        App.INSTANCE.provideDatabase().initializeDatabase();
    }

    private void prepareDataSource ( )
    {
        JdbcDataSource ds = new JdbcDataSource();
        ds.setURL( "jdbc:h2:mem:demo;DB_CLOSE_DELAY=-1" );
        ds.setUser( "scott" );
        ds.setPassword( "tiger" );
        App.INSTANCE.rememberDataSource( ds );
    }
}

该类调用我的App类,它充当一种服务定位器。通过enum设计为单例

package work.basil.example;

import javax.sql.DataSource;
import java.util.Objects;

public enum App
{
    INSTANCE;

    // -------|  DataSource  |---------------------------------
    private DataSource dataSource;

    public DataSource provideDataSource ( )
    {
        return this.dataSource;
    }

    public void rememberDataSource ( DataSource dataSource )
    {
        this.dataSource = Objects.requireNonNull( dataSource );
    }


    // -------|  Database  |---------------------------------
    private Database database;

    public Database provideDatabase ( )
    {
        return new Database();
    }
}

那个班叫我Database班。在实际工作中,Database将是一个具有各种具体实现的接口,用于测试与部署。出于演示目的,我在这里忽略了这一点。

package work.basil.example;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Database
{
    public String getFirstUserName ( )
    {
        String userName = "QUERY-FAILED";

        String newline = "\n";
        StringBuilder sql = new StringBuilder();
        sql.append( "SELECT name_ from  user_ ; " ).append( newline );
        System.out.println( "sql = " + sql );
        try (
                Connection conn = App.INSTANCE.provideDataSource().getConnection() ;
                Statement statement = conn.createStatement() ;
                ResultSet resultSet = statement.executeQuery( sql.toString() ) ;
        )
        {
            while ( resultSet.next() )
            {
                userName = resultSet.getString( "name_" );
                break; // Go no further. We need only the first row found.
            }
        }
        catch ( SQLException e )
        {
            e.printStackTrace();
        }

        return userName;
    }

    public void initializeDatabase ( )
    {
        System.out.println( "DEBUG Running `initializeDatabase` of " + this.getClass().getCanonicalName() );
        String newline = "\n";

        // Create table.
        StringBuilder sql = new StringBuilder();
        sql.append( "CREATE TABLE user_ ( " ).append( newline );
        sql.append( "pkey_ IDENTITY NOT NULL PRIMARY KEY , " ).append( newline );  // `identity` = auto-incrementing long integer.
        sql.append( "name_ VARCHAR NOT NULL " ).append( newline );
        sql.append( ") " ).append( newline );
        sql.append( ";" ).append( newline );
        System.out.println( "sql = " + sql );
        try (
                Connection conn = App.INSTANCE.provideDataSource().getConnection() ;
                Statement statement = conn.createStatement() ;
        )
        {
            statement.executeUpdate( sql.toString() );
        }
        catch ( SQLException e )
        {
            e.printStackTrace();
        }
        System.out.println("DEBUG Finished `CREATE TABLE` statement.");

        // Populate table.
        sql = new StringBuilder();
        sql.append( "INSERT INTO user_ ( name_ ) " ).append( newline );
        sql.append( "VALUES " ).append( newline );
        sql.append( "( 'Alice' ) , " ).append( newline );
        sql.append( "( 'Bob' ) , " ).append( newline );
        sql.append( "( 'Carol' ) " ).append( newline );
        sql.append( ";" ).append( newline );
        System.out.println( "sql = " + sql );
        try (
                Connection conn = App.INSTANCE.provideDataSource().getConnection() ;
                Statement statement = conn.createStatement() ;
        )
        {
            int rowsAffected = statement.executeUpdate( sql.toString() );
            System.out.println( "DEBUG Inserted rows into name_ table: " + rowsAffected );
        }
        catch ( SQLException e )
        {
            e.printStackTrace();
        }
        System.out.println("DEBUG Finished `INSERT` statement.");
    }
}

最后是MainView课堂。我禁用了@PWA注释,因为我们没有将该功能用于渐进式 Web 应用程序。

package work.basil.example;

import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Label;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;

/**
 * The main view contains a button and a click listener.
 */
@Route ( "" )
//@PWA ( name = "Project Base for Vaadin", shortName = "Project Base" )
public class MainView extends VerticalLayout
{
    private Label label;
    private Button button;

    public MainView ( )
    {
        // Widgets
        this.label = new Label( "User: ?" );
        this.button = new Button(
                "Get user" ,
                event -> {
                    Notification.show( "Getting user." );
                    String userName = App.INSTANCE.provideDatabase().getFirstUserName();
                    this.label.setText( "User: " + userName );
                }
        );
        add( button );

        // Arrange
        this.add( label , button );
    }
}
于 2020-01-24T06:44:08.090 回答
1

你应该检查是否ResultSet rs真的有结果。调用时rs.next(),查看是否返回false。如何检查结果集是否为空

public String getUsername() {
    String username = "QUERY-FAILED";

    try {
        start();
        statement = conn.createStatement();
        ResultSet rs = statement.executeQuery("select userName from names");

        if(rs.next() != false){
            username = rs.getString(1);
        }
    } catch (SQLException e) {
        e.printStackTrace();
    } finally {
        close();
    }

    return username;
}

我注意到的另一件事是,从查询select * from names中,无法确定 userName 属性是您正在阅读的第一列rs.getString(1)。确保编写更精确的查询以避免难以发现的错误select userName from names:即使表只有一列也要这样做,因为如果有人在另一列前面添加了怎么办?它可能会破坏您的应用程序。

于 2020-01-24T08:04:36.657 回答