
колледже Доусон в Монреале, Канада. Он также является программным консультантом и по совместительству преподавателем в Школе расширенного обучения при Институте вычислительной техники Университета Конкордия . Он ведет блог на omniprogrammer.com и пишет в Твиттере @omniprof . Его регулярные колонки о NetBeans в образовании перечислены здесь .
В этой статье я покажу вам, как я учу своих студентов программировать JDBC для настольных приложений JavaFX. Мой подход работает следующим образом:
- Используйте JavaFX bean-компоненты
- Установите соединение, чтобы войти в базу данных
- Оформите запрос через подготовленное заявление
- Скопируйте результаты в подходящую структуру данных
- Закройте все объекты и затем выйдите из базы данных
Этот тип подхода необходим, когда настольное приложение использует сервер базы данных, совместно используемый другими приложениями или пользователями. Подход, использующий пул соединений, является предпочтительным способом работы с базами данных в приложении на стороне сервера и может также подойти в некоторых случаях на настольном компьютере.
Давайте начнем с компонента данных. При создании приложения JavaFX вы должны написать свои bean-компоненты, чтобы поля могли связываться с элементом управления. В то же время вы хотите, чтобы компонент продолжал использоваться в классах, которые не знают о JavaFX.
Начните с создания всех полей как свойств JavaFX. Показанные свойства являются интерфейсами, которые будут созданы соответствующим классом в конструкторе.
public class FishData {
private IntegerProperty id;
private StringProperty commonName;
private StringProperty latin;
private StringProperty ph;
private StringProperty kh;
private StringProperty temp;
private StringProperty fishSize;
private StringProperty speciesOrigin;
private StringProperty tankSize;
private StringProperty stocking;
private StringProperty diet;
Сигнатура конструктора не по умолчанию — это сигнатура обычного компонента данных. Аргументы — это все примитивы или фактические типы объектов, которые будет содержать компонент. Код конструктора создает экземпляр объекта свойства и передает ему соответствующий аргумент. Конструктор по умолчанию просто вызывает конструктор не по умолчанию с начальными значениями, которые вы определили как соответствующие.
public FishData(int id, String commonName, String latin, String ph,
String kh, String temp, String fishSize, String speciesOrigin,
String tankSize, String stocking, String diet) {
super();
this.id = new SimpleIntegerProperty(id);
this.commonName = new SimpleStringProperty(commonName);
this.latin = new SimpleStringProperty(latin);
this.ph = new SimpleStringProperty(ph);
this.kh = new SimpleStringProperty(kh);
this.temp = new SimpleStringProperty(temp);
this.fishSize = new SimpleStringProperty(fishSize);
this.speciesOrigin = new SimpleStringProperty(speciesOrigin);
this.tankSize = new SimpleStringProperty(tankSize);
this.stocking = new SimpleStringProperty(stocking);
this.diet = new SimpleStringProperty(diet);
}
public FishData() {
this(-1, "", "", "", "", "", "", "", "", "", "");
}
Если ваши поля содержат объект, который не имеет соответствующего свойства, такого как BigDecimal, вы можете использовать ObjectProperty.
private ObjectProperty unitCost;
В конструкторе это будет выглядеть так:
this.unitCost = new SimpleObjectProperty<>(unitCost);
Установщики и получатели в bean-компоненте JavaFX имеют тот же интерфейс, что и обычный bean-компонент. Это то, что обеспечивает совместимость. Внутри этих методов код либо извлекает значение из объекта свойства, либо устанавливает значение в объекте свойства. Вы никогда не используете set или get для прикосновения к реальному объекту свойства.
public int getId() {
return id.get(); // Use get on the propert
}
public void setId(int id) {
this.id.set(id); // Use set on the property
}
Для компонентов JavaFX требуется третий метод для каждого поля, которое вы привязываете к элементу управления. Этот метод возвращает объект свойства. Этот метод должен использоваться только в привязке. Я не уверен, существует ли соглашение об именах, но я говорю своим ученикам использовать имя поля плюс слово «Свойство».
public IntegerProperty idProperty() {
return id;
}
Вот метод, который будет извлекать все записи из таблицы, содержащей записи рыбы. Переменные url, user и password были объявлены, и я учу своих студентов, что эта информация должна быть загружена из файла свойств.
public ObservableList findAll() throws SQLException {
ObservableList fishData = FXCollections.observableArrayList();
String selectQuery = "SELECT ID, COMMONNAME, LATIN, PH, KH, TEMP, FISHSIZE, SPECIESORIGIN, TANKSIZE, STOCKING, DIET FROM FISH";
// Using try with resources, available since Java 1.7
// Classes that implement the Closable interface created in the
// parenthesis () will be closed when the block ends.
try (Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement pStatement = connection.prepareStatement(selectQuery);
ResultSet resultSet = pStatement.executeQuery()) {
while (resultSet.next()) {
fishData.add(createFishData(resultSet));
}
} catch (SQLException sqlex) {
log.error("SQL Error", sqlex);
throw sqlex;
}
log.info("# of records found : " + fishData.size());
return fishData;
}
public ObservableList findAll() throws SQLException {
Метод возвращает ObservableList объектов FishData. ObservableList реализует интерфейсы Collection и List, что делает его совместимым, например, с ArrayList. Существующий код, который ожидает ArrayList, продолжит работать с этим методом.
try (Connection connection = DriverManager.getConnection(url, user, password);
PreparedStatement pStatement = connection.prepareStatement(selectQuery);
ResultSet resultSet = pStatement.executeQuery()) {
Эта попытка использует попытку с ресурсами, представленную в Java 7. Каждый из объектов в скобках реализует интерфейс Closable. Это означает, что когда этот блок try завершается успешно или из-за исключения, все объекты будут закрыты в обратном порядке после их создания.
while (resultSet.next()) {
fishData.add(createFishData(resultSet));
}
Здесь код проходит через ResultSet, вызывает закрытый метод для создания объектов FishData, а затем добавляет объекты в ObservbleList. Вот частный метод.
private FishData createFishData(ResultSet resultSet) throws SQLException {
FishData fishData = new FishData();
fishData.setCommonName(resultSet.getString("COMMONNAME"));
fishData.setDiet(resultSet.getString("DIET"));
fishData.setKh(resultSet.getString("KH"));
fishData.setLatin(resultSet.getString("LATIN"));
fishData.setPh(resultSet.getString("PH"));
fishData.setFishSize(resultSet.getString("FISHSIZE"));
fishData.setSpeciesOrigin(resultSet.getString("SPECIESORIGIN"));
fishData.setStocking(resultSet.getString("STOCKING"));
fishData.setTankSize(resultSet.getString("TANKSIZE"));
fishData.setTemp(resultSet.getString("TEMP"));
fishData.setId(resultSet.getInt("ID"));
return fishData;
}
Назад в findAll это блок catch.
} catch (SQLException sqlex) {
log.error("SQL Error", sqlex);
throw sqlex;
}
Раньше я учил своих учеников просто использовать условие throws и не пытаться поймать исключение здесь. Недавнее чтение на эту тему, а также некоторые занятия в классе изменили мой подход. Исключение теперь перехватывается и регистрируется перед повторным вызовом. Я не верю, что менеджер баз данных должен принимать решение о том, что делать с исключением, поэтому я перебрасываю его.
Последний пример — это метод, использующий в SQL предложение «where». Вот когда PreparedStatement является обязательным. Для этого также требуется внутренняя попытка с ресурсами, поскольку в скобках попытки может быть размещено только создание экземпляров объектов.
public FishData findID(int id) throws SQLException {
// If there is no record with the desired id then this will be returned
// as a null pointer
FishData fishData = null;
String selectQuery = "SELECT ID, COMMONNAME, LATIN, PH, KH, TEMP, FISHSIZE, SPECIESORIGIN, TANKSIZE, STOCKING, DIET FROM FISH WHERE ID = ?";
// Using try with resources, available since Java 1.7
// Classes that implement the Closable interface created in the
// parenthesis () will be closed when the block ends.
try (Connection connection = DriverManager.getConnection(url, user, password);
// You must use PreparedStatements to guard against SQL Injection
PreparedStatement pStatement = connection.prepareStatement(selectQuery);) {
// Only object creation statements can be in the parenthesis so
// first try-with-resources block ends
pStatement.setInt(1, id);
// A new try-with-resources block for creating the ResultSet object begins
try (ResultSet resultSet = pStatement.executeQuery()) {
if (resultSet.next()) {
fishData = createFishData(resultSet);
}
}
} catch (SQLException sqlex) {
log.error("SQL Error", sqlex);
throw sqlex;
}
log.info("Found " + id + "?: " + (fishData != null));
return fishData;
}
Я надеюсь, что это сделает вашу кодировку JDBC более надежной и что вы перейдете на JavaFX для своих графических интерфейсов.