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

Posted

技术标签:

【中文标题】Vaadin:如何显示 MySQL 数据库中的数据?【英文标题】:Vaadin: How do I display data from a MySQL database? 【发布时间】:2020-01-23 23:47:01 【问题描述】:

我正在开发一个 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();

提前致谢!

【问题讨论】:

哪个版本的 Vaadin? 调试时rs.getString(1)返回什么? 最后一个,Vaadin flow @BasilBourque @ReneAlvarenga 将此类详细信息发布为对您的问题的编辑而不是评论。我刚刚为你这样做了。 【参考方案1】:

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

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

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

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

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

我使用启动 Vaadin 的钩子来建立我的 DataSource 对象并初始化数据库。 Following the manual,嵌套在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 类,它充当一种service locator。通过enum设计为singleton。

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

【讨论】:

jesus christ... 这就是问题所在... String userName = App.INSTANCE.provideDatabase().getFirstUserName(); this.label.setText("用户:" + 用户名);您添加了一个字符串来获取名称,然后在标签 .setText 方法中使用该字符串,我已经尝试过并且效果很好......如果没有使用 String 对象检索 Datasource 方法,它不会更新,谢谢老兄!!...你知道为什么会这样吗?【参考方案2】:

您应该检查ResultSet rs 是否确实有结果。调用rs.next() 时,查看是否返回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;即使表格只有一列也要这样做,因为如果有人在另一列前面添加了怎么办?它可能会破坏您的应用程序。

【讨论】:

感谢您的回答@kscherrer!是的,结果集确实返回了一个字符串,它是用户名,但只有当类作为 java 应用程序运行时,如果它在 vaadin 应用程序中运行,它不会做任何事情,哦,这不是我的最终实现应用程序,目前只是测试,我将使用 stringbuilder 构建查询以避免最终实现的注入,目前它只是用于小测试以检查一切是否正常,然后再继续前进

以上是关于Vaadin:如何显示 MySQL 数据库中的数据?的主要内容,如果未能解决你的问题,请参考以下文章

使用 Vaadin 和 Spring (MySQL DB) 运行应用程序时出现问题

在 Vaadin Flow 中的布局中居中小部件

如何在 Vaadin 13 中制作侧边菜单?

服务器数据库使用情况

使用 Vaadin Hibernates ThreadLocal<Session>.get() 在第一次调用后返回 null

如何在 Vaadin 7 中制作一对单选按钮来表示 True/False 值但本地化文本?