Vaadin:如何显示来自 MySQL 数据库的数据?

Vaadin: How do I display data from a MySQL database?

我正在开发一个 Vaadin Flow(版本 14.1)应用程序,我遇到了这个问题,我无法直接连接到 MySQL 数据库。

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

这是按钮点击侦听器:

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

这是数据源 class 方法:

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,这是我用来测试它是否失败的字符串,但如果没有评论,标签就会保持不变.

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

statement = conn.createStatement();

提前致谢!

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

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

此应用是使用 Vaadin 14.1.5 构建并 运行 使用 "Plain Java Servlet" 风格的入门项目 provided by the Vaadin.com site。 运行 在 macOS Mojave 上使用捆绑的 Jetty Web 容器。

我对他们的 Maven POM 文件所做的唯一更改是更改为 Java 版本 13,并为 H2 Database Engine 添加依赖项以使其成为使用内存数据库的独立示例.

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

我使用 Vaadin 启动的钩子来建立我的 DataSource object and initialize the database. Following the manual, nested in the resources folder, I created folders META-INF > services. Per the Java SPI 设施,在那里我创建了一个名为 com.vaadin.flow.server.VaadinServiceInitListener 的文件,其中包含一行来指定我的 class 的名称实现此文件名称中命名的接口:

work.basil.example.ApplicationServiceInitListener

也就是我的ApplicationServiceInitListenerclass实现了Vaadin接口VaadinServiceInitListener。当我的 Vaadin 网络应用程序启动时,我的 class 将自动实例化并通过 Java SPI 设施调用其方法。

我的ApplicationServiceInitListenerclass:

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

那个 class 调用我的 App class 充当一种 service locator. Designed as a singleton via 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();
    }
}

那 class 叫我的 Database class。在实际工作中,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 class。我禁用了 @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 );
    }
}

您应该检查 ResultSet rs 是否真的有结果。调用rs.next()的时候,看看是不是returns false。 How to check if ResultSet is empty

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;即使 table 只有一列,也要这样做,因为如果有人在另一列前面加了怎么办?它可能会破坏您的应用程序。