Я не могу обнаружить никаких проблем с вашим кодом. Но я могу предоставить вам полный рабочий пример приложения для сравнения
Мой пример приложения идет в соответствии с принципами, изложенными в коде вашего Вопроса. Vaadin Button
выполняет запрос к базе данных, используя объект DataSource
из таблицы имен пользователей. Значение из первой найденной строки отображается в виджете Vaadin Label
на веб-странице.
Это приложение было создано и работает с Vaadin 14.1.5 с использованием аромата «Простой Java сервлет» стартового проекта , предоставленного сайтом Vaadin.com . Запуск на MacOS Mojave с помощью встроенного веб-контейнера Jetty.
Моими единственными изменениями в их POM-файле Maven было изменение на Java версию 13 и добавление зависимости для H2 Database Engine , чтобы сделать это самостоятельно Пример использования базы данных в памяти.
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
Я использовал перехват для запуска Vaadin, чтобы установить sh my DataSource
объект и инициализировать базу данных. Следуя инструкции , вложенной в папку resources
, я создал папки META-INF
> services
. Согласно средству Java SPI , я создал файл с именем com.vaadin.flow.server.VaadinServiceInitListener
, содержащий одну строку для указания имени моего класса, который реализует интерфейс, названный в имени этого файла:
work.basil.example.ApplicationServiceInitListener
То есть мой класс ApplicationServiceInitListener
реализует интерфейс Vaadin VaadinServiceInitListener
. Мой класс будет автоматически создан, и его метод будет вызван с помощью этого Java SPI, когда запустится мое веб-приложение Vaadin.
Мой 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
, который действует как своего рода указатель службы . Разработанный как singleton через 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
, так как мы не используем эту функцию для прогрессивных веб-приложений.
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 );
}
}