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
也就是我的ApplicationServiceInitListener
class实现了Vaadin接口VaadinServiceInitListener
。当我的 Vaadin 网络应用程序启动时,我的 class 将自动实例化并通过 Java SPI 设施调用其方法。
我的ApplicationServiceInitListener
class:
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 只有一列,也要这样做,因为如果有人在另一列前面加了怎么办?它可能会破坏您的应用程序。
我正在开发一个 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
也就是我的ApplicationServiceInitListener
class实现了Vaadin接口VaadinServiceInitListener
。当我的 Vaadin 网络应用程序启动时,我的 class 将自动实例化并通过 Java SPI 设施调用其方法。
我的ApplicationServiceInitListener
class:
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 只有一列,也要这样做,因为如果有人在另一列前面加了怎么办?它可能会破坏您的应用程序。