Методы работы с базами данных

В

этой главе описывается ряд базовых методов, которые необходимы для созда­ния полезных приложений, взаимодействующих с базами данных. Также при­водится описание приложений, в которых используются отношения типа "один ко многим" и вычисляемые поля.

Эта глава содержит смесь из описания инструментов генерации приложений и описания более сложных инструментов настройки параметров модулей данных.

Все эти инструменты исключительно просты в применении, так что материал этой главы не содержит ничего особо сложного. Однако приложениям, которые мы будем создавать, присущи некоторые довольно-таки мощные возможности, связан­ные с работой с базами данных, поэтому можно сказать, что в этой главе раскрыва­ется гораздо более важная тема.

В этой главе мы займемся разработкой двух приложений. Первое приложение является довольно простым. Вы сможете найти его в сопровождающих книгу мате­риалах под названием InsertDelete. Второе приложение несколько более сложное, но и более интересное. Оно опять-таки входит в состав сопровождающих материа­лов и называется OneToManyCalc. Вы также можете найти код для данной книги на Web-сайте ПО Адресу Www.Elvenware.Com.

Отношение "один ко многим"

В этом разделе книги вы узнаете о создании отношений "один ко многим" между двумя таблицами. Отношение (relationship) "один ко многим" ("one-to-many") иног­да называют отношением "главный-подчиненный" ("master-detail").

В приведенных примерах мы свяжем таблицу album (альбом) и таблицу artist (исполнитель). По завершении, при просмотре одной записи в таблице artist мы сможем просматривать связанные с ней записи в таблице album. Например, при просмотре в таблице artist информации, относящейся к Electric Light Orchestra, Мы сможем увидеть и все альбомы, выпущенные этой группой, которые хранятся в базе данных.

Вы увидите два различных способа установки отношения между двумя таблица­ми. В первом примере для создания отношения "один ко многим" будет использо­ваться Data Modeler. Во втором примере будут применяться инструменты, встроен­ные в интегрированную среду разработки JBuilder. Используйте первый метод для создания модуля данных с нуля, а второй метод — для редактирования существую­щего модуля данных.

Создание отношения "один ко многим" с помощью Data Modeler

Наступило время обсудить этапы, необходимые для создания отношения "один ко многим" между двумя таблицами.

Ниже приведены начальные шаги:

■ Выберите в меню JBuilder пункт File ∣ New Project.

■ Выберите в меню JBuilder пункт File ∣ New DataModuIe.

■ Убедитесь, что флажок Invoke Data Modeler отмечен, после чего щелкните на кнопке OK.

■ Откройте базу данных jbbook (информацию по этой базе данных можно най­ти ь главе 39).

■ Выберите таблицу artist и щелкните на кнопке Сору АН.

■ Выберите в меню Data Modeler пункт Queries j Add.

■ Выберите таблицу album и щелкните на кнопке Сору АН.

■ Выберите в меню пункт Queries ∣ Link.

■ В таблице artist выберите поле code. В таблице album выберите поле GroupCode. Отметьте флажки Allow cascading deletes (Позволить каскадное удаление) и Allow cascading updates (Позволить каскадное обновление). За справкой по данному этапу обратитесь к рисунку 42.1.

рис. 42.1. создание отношения типа "один ко многим " с каскадными удалениями и обновлениями между таблицами artistи albumЩелкните на кнопке OK в диалоговом окне Link Queries (Связать запросы). Теперь вы можете увидеть визуальное представление отношения между двумя таблицами (см. рис. 42.2).

K5∣ InfcQuprres

Masterquerv:__________ DotMquary

Jartist …………… …. J! album

Specitv retabonsr≠ ot master query to detail Query: Artist j album

¾⅞’t⅞’. ……. * ‘ J⅛⅜OURCOOE………………..

………………. . Jciitkfor list of columns . ……………………

o ontamodeter it vsrc fftγa∖a∣ itintiltertevnnlittedi ι r< vuhfrtteditwetatoud x,file queries view database h
я sb ¾
reieborc one tcnnany pdelayfetchofdataiiuntiineeded
p alldwcascadingupdates p №* cascading deletes!
[ ok j cancel

curreritguery =■—-- 
cokirnfls where ordersy oroupby sql j testj arailable columns: p dljbnet ... .⅛^
,j data ж ∏∣∣h detimbon
æ (balbuminto — .—
!bfliarbel ki"t—~
æ ibuitisltoe л!*:.!, ∙i
o= чв1 ħnnlr -llɪ i*∙.".i*,⅛"pfi
-j , . .. jʃ .u⅛. iτ*~ -ɪ*
,: ⅛ tables,agewgata...,« gemove,«remove all
jeelectedcdlumns code Λlβum types loudness medium rating,jl
рис. 42.2. использование data modeler для просмотра визуального представления отношения между двумя таблицами

Выберите в меню Data Modeler пункты File ∣ Save и File ∣ Exit. В результате будет сгенерирован код вашего модуля данных.

Выберите в меню JBuilder пункт File ∣ New ∣ Data Module Application. Щелкните на кнопке OK в мастере приложений Application Wizard.

На данном этапе вы благополучно выполнили генерацию своего приложения. Выберите в меню пункт Project ∣ Project Properties, перейдите на закладку Run и щел­кните на кнопке New. Отыщите главный файл своего приложения, который должен называться DataModuIeITwoTierApp или что-то вроде того.

Щелкните на кнопке OK в двух диалоговых окнах, чтобы вернуться в главное окно IDE-среды JBuilder. Щелкните на зеленой кнопке запуска. Теперь вы должны будете видеть свое приложение в состоянии выполнения, как показано на рис. 42.3.

Перебирая записи в таблице artist, вы сможете видеть и связанные с ними за­писи из таблицы album Если вы удалите запись из таблицы artist, все связанные записи в таблице ALBUM удалятся автоматически.

Создание отношения "один ко многим" вручную

В этом примере мы снова приступим к определению отношения типа "один ко многим" между таблицами artist и album. Выберите в меню пункт File ∣ New Project, чтобы создать новый проект, и пункт File ∣ New ∣ Application, чтобы добавить в него новое приложение. Щелкните на кнопке Finish на первой странице обоих диа­логовых окон, принимая все значения, предлагаемые по умолчанию.

Выберите в меню пункт File ∣ New Data Module. На этот раз необходимо убедить­ся, что флажок Invoke Data Modeler не был отмечен. Щелкните на кнопке OK, чтобы закрыть диалоговое окно.

Fte

И*

И

<4

M *

X H

А

А

<⅛*

5

LaA DaAs

Нв LWes

Bom

M

BLthfca MwijL

QfcMW

AtBttyfc

Recortflof 197

M

* ►

И ♦

√∙

X «

А

А

J______________ Длим

Г Types

ιoune≈~l Мвяим

MTWG Г СС06ГО

CD0βNW<

I

2

352 XSP

J……….. 1

Г

,J

В!

3

412SKcħMBr∙w(nκ 1>

О

Qg

OiOTObaxB

4

421 ‘Someday My Prtxe Wf Come

………………… ɪ………………………….

J)I

Ois4θ9tfm

■мни Devu

5

423’Worhn’

…… ɔ ®

…………….. . 0……………………………

О!

О 66O9ed06

.Mws Davls Qulrtec

; е

444 KinO Of Пив

О

Oi

О:

О £320*9506

SMtos Devls

; 7

466 jin A Stont Wey

О

θΓ

0⅛

Oil00ββf(tt

MtosDeve

Е

⅛< TraManWti TheHnm

…………………….. Ll………………………….. «

Oi

Ci4aOeS70β

MtosDavls

9

447iUve-Evi(tfbκ 1)

I 0

Oi

О’

О’ЭсОЬмОб

‘MtosDevIs

10

446 LteeXvi (Пк 2)

О

О

Oj’

DltfOctoOT

Davfr

11

449 Xtee Around Tbe Worid

J__ О

О,

05

Oi7410970b

Toes Devb

470 >Dairfc Meguf 2

О

LLiLLLZ

ɪ

0<220be∙04

.)avH. Mte<

: 13

471 DerkMagutl

О

OS

О]

θi21<fec5M

1DavbzMtof

Jf_____

1 >rj

ReurtflofTl

Рис. М3. Автоматически сгенерированное JBuilder Приложение, использующее отношение типа "один ко многим"

В панели содержимого JBuilder переместите фокус на новый модуль данных и переключитесь на панель конструирования. В палитре компонентов перейдите на закладку DataExpress. Выберите первую пиктограмму слева, которая содержит под­пись com. borland. dx. sql. dataset. Database. Поместите экземпляр этого компонента в узел доступа к данным в панели структуры, как показано на рис. 42.4.

Находясь в инспекторе, щелкните на кнопке эллипсиса для свойства connection (соединение), чтобы вызвать диалоговое окно Connection (Соединение), показанное на рис. 42.5. Щелкните на кнопке Choose Existing Connection (Выбрать существую­щее соединение) и найдите базу данных Jbbook. Введите имя пользователя и па­роль, после чего щелкните на кнопке Test Connection (Протестировать соединение).

Если вы хотите, чтобы пользователю нужно было вводить пароль, отметьте фла­жок Prompt user password (Запросить пароль у пользователя). В противном сЛучае пароль будет жестко закодирован в приложении. Последняя опция является более предпочтительной во время разработки приложения, так как вам не приходится тратить время на ввод пароля. Щелкните на кнопке OK для того, чтобы закрыть диа­логовое окно.

Теперь вы должны сформировать запросы к базе данных по таблицам artist и album. Выберите четвертый компонент на закладке DataExpress палитры компонен­тов. Этот компонент называется com. borland. dx. sql. dataset. QueryDataSet. Поместите два экземпляра этого компонента в узел Data Access в панели структуры. Используя инспектор, присвойте первому компоненту имя queryDataSetArtist, а второму — queryDataSetAlbum. В это же время вы можете изменить имя базы данных с database1 на database.

Выберите queryDataSetArtist и щелкните в инспекторе на кнопке эллипсиса для свойства query (запрос). Появится диалоговое окно Query (Запрос), показанное на рис. 42.6. В поле Database этого окна укажите имя созданной вами базы данных. Щелкните на кнопке SQL Builder и раскройте узел database, чтобы отобразить таб­лицы. Выберите таблицу ARTIST и щелкните на кнопке Сору АН, как показано на рис. 42.7. Щелкните на кнопке OK. Теперь диалоговое окно Query должно выглядеть так, как показано на рис. 42.6. Щелкните на кнопке Test Query (Протестировать зап­рос), дабы убедиться в достоверности введенных данных. Хоть это и не обязательно, однако я предпочитаю отмечать флажок Place SQL text in resource bundle (Поместить текст SQL-запроса в группу ресурсов). Если вы выберите этот пункт, то после щелчка на кнопке OK в нижней части диалогового окна Query будет предложено подтвер­дить имя группы ресурсов. Подтвердите имя группы ресурсов, не изменяя его.

рис. 42.4. выбор объекта database, располо-женного слева от страницы dataexpress палитры компонент, и помещение его в узел data access панели структуры гc* connection ∣<
general j extended properties' 
'connectionpropertlee 
 
driver: 
jorg.b∣tmrn -nysql, -1w∙ ɪ 
∙∣rl 
jdbcynysqiznocalhosvibbook ∙∙∙. 1 j 
ueemami. j 
: jchartie Γ^ use extended properties i 
password: 
. i*»* Γ^ eromptuserpassword 
test 
testconnectlon success 
 
 ok cancel * help j

,рис. 42.5. выбор и тестирование соединения с базой данных

Исследование группы ресурсов

На этом этапе вам может быть интересно, как выглядит группа ресурсов (resource bundle). Ее текст представлен в листинге 42.1.

Листинг 42.1. Простая группа ресурсов, содержащая запрос к таблице ARTIST

Package Untitledl8;

Import java. util.*;

Public class SqlRes extends java. util. ListResourceBundle

{

Static final Object[][] contents = new String[][]{

{ "artist", "SELECT artist. CODE, artist. LAST, artist. FIRST," +

"artist. BORN, artist. DIED, artist. BIRTHPLACE, artist. COMMENT," + "artist.Artisttype from artist" }};

Public Object[][] getContents()

{

Return contents;

1

solbulider browse tables.. 3
рис. 42.6. настройка и тестирование запроса в диалоговом окне query
ʌ <jtιεry ∣x∣
ouery j parameters j 
uataoe .

SQL statement

SEUCT artist. CODE, artist. LAST, artist. FIRST, artist. BORN, aɪ Ttist. DIED, artist. BIRTHPLACE, artist. COHHENT, artist. ARTIST Ttpe froh artist

c? sql θttilder
p execulequerylmmbdlatelywhenopened p place bqltext in resource bundle load options:
load all rows
,• testquery ∣
lss3sss>afisw<sffiaα6<
,success,j-
columns i wnere) orterby! oroup by∣ 80lj testj axaflable column»; Γ~ distinct
selected columns:
code
last
tirst
born
>died
!birthplace
^comment
¾ databases s 0∣ database
ё 6] tables jit siaibum s biattummtb ⅝ imlhl $ b arttsttyρe
si book ⅛∙ si cdrom ⅛t hbfoo ⅛-hb location æ- gb loudness ⅛ fijj medium ⅛' я songs ж iffltypes
definition j da⅞a
table.cat tablemschem ? tablemname ⅛⅛t
∣tablen,tvpe ~ !table
remarks
cow aft >
«remove ag [j
рис. 42.7. использование sql builder для создания запроса

Массив Contents В группе ресурсов содержит пару строковых переменных. Пер­вый элемент пары называется artist. Используйте эту строку, для того чтобы осу­ществить выборку по запросу:

ResourceBundle sqlRes = ResourceBundle. getBundle("untitledlβ.SqlRes");

String myquery = sqlRes. getString("artist")

Но давайте не будем долго задерживаться на группе ресурсов. У нас есть еще и другая работа.

Выберите компонент queryDataSetAlbum. В инспекторе переключитесь на свой­ство query и повторите все шаги для компонента queryDataSetAlbum. Единственное различие будет заключаться в том, что на этот раз вы выберите все поля из таблицы album, а не из таблицы artist. Если вы решите использовать группу ресурсов, в нее будет добавлен новый код, содержащий ваш запрос:

Static final Object[][] contents = new String[][]{

{ "artist", "SELECT artist. CODE, artist. LAST, artist. FIRST," +

"artist. BORN, artist. DIED, artist. BIRTHPLACE, artist. COMMENT," + "artist.Artisttype from artist" ),

{ "album",

" SELECT album. CODE, album. ALBUM, album. TYPES, album. LOUDNESS, " + "album. MEDIUM, album. RATING, album. GROUPCODE, album. CDDBID, " + "album.Cddbname from album" )},-

Теперь можно вызывать метод SqlRes. getstring("album"), чтобы осуществить выборку по запросу album.

До сих пор все было достаточно понятным. Следующий этап будет чуть-чуть сложнее, однако, в действительности, он сравнительно прост.

Чтобы создать истинное отношение типа ’’один ко многим’’ между двумя табли­цами, выберите в инспекторе свойство ITiasterLink компонента queryDataSetAlbum. Выберите в поле Master dataset (Глав­ный набор данных) компонент queryDataSetArtist. Установите поле Code для поля Master link columns (Главные столбцы связи) Укажите значение GroupCode для поля Detail link columns (Подчиненные столбцы связи). Выберите все опции в нижней части диалогового окна, как показано на рис. 42.8. Эти оп­ции позволяют отсрочить извлечение подчиненных строк, пока они не понадо­бятся, и использовать каскадные обнов­ления и удаления. Если вы выберете оп­ции задержки извлечения, кнопка тести­рования, вероятнее всего, работать не будет. Вам, скорее всего, следует сначала отключить опцию, чтобы запустить тест и убедиться в том, что построенный зап­
рос работает. Если все в порядке, включите опцию снова, поскольку она повышает производительность вашего приложения и снижает сетевой трафик.

В предыдущей главе я объяснял устройство сгенерированного кода, предназна­ченного для создания базы данных и организации запросов к набору данных. Одна­ко вам все еще нужно разобраться с кодом, который создает отношения "главный- подчиненный" между двумя таблицами:

CgueryDataSetAlbum. SetMasterLink (new

примечаниеMasterLinkDescriptor CqueryDataSetArtist, new String[] {"CODE"], new String[] {"GROUPCODE"), true, true, true));

B Коде, который сгенерировал JBuiIder, Вы увидите такой синтаксис: Com.Borland.Dx.Dataset.MasterLinkDescriptor. В данном случае, как и во многих других местах данного раздела текста, я удалил фрагмент кода Com.Borland.Dx.Dataset. Я считаю, что это лишнее, так как мы уже включили опера­тор Import, Который ссылается на этот пакет, в верхнюю часть файла исходного кода: Import Com.Borland.Dx.Sql.Dataset.*. Мне кажется, что весь этот дополнительный синтаксис лишь делает неясным назначение кода. В результате, я обычно его удаляю, на­Деясь получить код, который будет легче понять.

примечаниеПервые три параметра, передаваемые в метод SetMasterLink Компонента QueryDataSet, Почти не требуют объяснений Первое поле представляет собой на­бор данных, который вы хотите связать с таблицей album. Во втором поле указыва­ется поле или поля главной таблицы, через которые устанавливается связь. Третье поле представляет собой поле подчиненной таблицы, используемое в связи.

Выше я рассказывал о природе связи между двумя таблицами. В дан­ном случае. Code Является первичным ключом таблицы ARTIST, A GroupCode — внешним ключом таблицы ALBUM. Если в поле Code Для записи Electric Light Orchestra Таблицы ARTIST Указано значение 11, Тогда запись значения 11 Поле GroupCode Таб­Лицы ALBUM Укажет на то, что данный альбом создала группа Electric Light Orchestra.

Как только вы внесли эти изменения, должен будет создаться параметризован­ный запрос в вашей группе ресурсов:

{ "album", "SELECT album. CODE, album. ALBUM, album. TYPES, album. LOUDNESS, ,’ + "album. MEDIUM, album. RATING, album. GROUPCODE," +

"album. CDDBID, album. CDDBNAME FROM album where GROUPCODE=:CODE" }};

Обратите внимание на то, что этот код был модифицирован — в него было вклю­чена конструкция where. В частности, мы намерены выбрать из таблицы album все записи, для которых Groupcode равняется некоторому значению, которое еще толь­ко нужно будет определить. Оно будет равно переменной, обозначенной через син­таксис :CODE. Наличие двоеточия перед словом code указывает на то, что это либо параметр, либо переменная времени выполнения. Инструмент DataExpress присво­ит значение code во время выполнения программы Когда пользователь выбирает исполнителя, в SQL-выражение на место переменной. — ode будет вставлено реаль­ное значение поля code для данного исполнителя. Например, если была выбрана

Группа Electric Light Orchestra, Code Будет равняться 11, т. е. будут выбраны все запи­си из таблицы album, для которых GroupCode Имеет значение 11.

Это все, что вам нужно сделать, чтобы гарантировать правильность составления запроса. Проверьте все элементы кода, чтобы убедиться в том, что они верны. В час­тности, убедитесь, что была правильно составлена конструкция where. Если есть неточности, введите этот код вручную.

Отображение отношения "один ко многим" для пользователя

Чтобы показать отношение пользователю, потребуется создать пользовательс­кий интерфейс для этого приложения. Начнем с создания двух панелей, одной — для таблицы artist и другой — для таблицы ALBUM.

Выберите в меню пункт File ∣ New Class. В качестве базового класса для нового компонента укажите javax. swing. JPanel. В качестве имени пакета укажите имя глав­ного пакета, прибавив к нему имя встроенного пакета ui: untιtled18.ui. Дайте своему новому компоненту имя ArtistView, как показано на рис. 42.9.

Переведите свой новый компонент в режим конструирования. Для свойства layout своего компонента укажите значение BorderLayout. Перейдите на закладку dbSwing палитры компонентов и поместите TableScroIIPane на свой компонент, ус­тановив для его свойства constraints значение Center. Поместите компонент JdbTabIe поверх TableScroIIPane и присвойте ему имя tableArtist.

Откомпилируйте код, чтобы убедиться, что на данном этапе все работает нор­мально. Вам следует сделать это прежде, чем перейти на следующий шаг. Мастер модулей данных будет правильно работать только с откомпилированным кодом.

Пребывая в режиме конструирования диалогового окна ArtistView, выберите в меню пункт Wizards ∣ Use DataModuIe. В мастере использования модуля данных (Use DataModule Wizard) установите для имени поля значение dMod, а для класса DataModule Имя своего модуля данных. Укажите, что следует совместно использо­вать статический экземпляр модуля данных, как показано на рис. 42.10.

О Class WiZdfri

J CreateanewJawaclass

‘—ʃ FUI In the fields below to set the package, name, base class, and other options for the

Java class which will be created.

Class IhfotmatlOh ` —* .. — ■

Package: tuntitled1B. ui

3 — Ij

Рис. 42.9. Создание

Class name: 1⅛tSMev

Основы для визуального

Base class Yaxswi∏q. IPa ιe

3~∑Jj

Представления

Таблицы ARTJST

Γ Uμuuιre

R Public

P Generatsdefaultconstructor

Γ Generaternalnmethod

Pr Override abstract methods

R Generateheadercomments

I

I OK j Cancel J Help

Как объяснялось в предыдущей главе, этот мастер дает возможность использо­вать одноэлементный шаблон, чтобы гарантировать, что будет создан только один экземпляр модуля данных.

Код, который генерирует мастер Data Module Wizard, выглядит следующим обра­зом:

DataModulel dMod;

DMod = untitledl8.DataModulel. getDataModule();

Выберите компонент tableArtist и воспользуйтесь его свойством dataSet, чтобы выделить из модуля данных таблицу queryDataSetARTIST. После этого вы увидите таблицу в панели, как показано на рис. 42.11.

Теперь необходимо повторить весь этот процесс для таблицы album. Создайте новый компонент с именем AIbumView, который был бы потомком класса JPanel. Установите для его свойства Layout значение BorderLayout и поместите его в пакет ui. Поместите на него TableScroIIPane и компонент JdbTabIe из закладки dbSwing. Сна­чала откомпилируйте свою программу, а затем с помощью мастера Use DataModule Wizard подключите новый визуальный компонент к своему модулю данных. Устано­вите для свойства dataset компонента JdbTabIe значение queryDataSetAlbum. После выполнения этого последнего шага, вы, скорее всего, не получите какого-либо визу­ального подтверждения, что все сработало правильно. В действительности, может показаться, что компонент JdbTable Полностью исчез. Пусть это вас не тревожит; на самом деле все в порядке.

Перейдите на Framel и переключитесь в режим конструирования. Чтобы выб­рать два новых компонента, созданных вами, воспользуйтесь инструментом Bean Chooser, который находится в левом углу палитры компонентов. Подробное описа­ние этого процесса можно найти в части VIl.

c use oatamoduk wizard,unwιeoι⅛.um,⅞moβuie1,java field declaration,field name: idmod
Γ create new instance of datamoduie <♦ 8hare (static) instance ofdatamoduie c callersetsinstancewiihsetmoduieo
,satoct mamnma otjact ant options
salact an existing datamoduie and how to reference it
oatamoduie e'ass` >-- -
∣ juntttɪed 1 b datamoduie 1
,i’,рис. 42.11. просмотр таблицы artist в компоненте jdbtable, внедренной в прокручиваемую область палитры,рис. 42.10. использование мастера use datamodule wizard для получения доступа к вновь созданному модулю данных,z7 flui∣dv< g.∕i{4.j.tv√*'*j∏hlfetb∕uuut∣cui *t^(czwt<t>e∙d1wyimtt⅛tv.fqx>j',filt edit search view project run team wizards ioois window help
'd ⅛ β ∙ ав ∙ f"^<i ⅜ i'» r"" j^√⅞, ∙⅛ ∣ a
sfl ♦
,«а artswewj к 3, datamoduiai| м;& fritmf; x⅛⅛8q∣r∙∙;
к swtig1 swbgcortetnere ∣ dβtβfeφreβ∣ <fcswħg ∣m⅛acbswing 1 <bsw*sgmxtats i k⅛smββaena :i xmll ы& i ΛvΓ Γji*j
,2 i 3 schulman
3 j 4 marsalls
« 
,й:
jd
.l0j.

Поместите на Framel компонент ArtistView и установите для его свойства constraints значение WEST. Поместите на Framel компонент AIbumView и установите для его свойства constraints значение CENTER.

После помещения компонентов на Framel вы, скорее всего, обнаружите, что все выглядит не совсем правильно. В частности, трудно показать все поля таблицы artist, чтобы при этом таблица album хорошо выглядела. Просто не хватает места на экране, чтобы все красиво изобразить.

На этом этапе у вас есть три альтернативы:

■ Сделать выборку меньшего количества полей из таблицы artist, внеся изме­нения в свой SQL-oπepaτop

■ Скрыть те поля, которые вы не хотите показывать.

■ Найти золотую середину, выбрав поля, предназначенные для отображения, и скрыв поля, которые не следует показывать.

Я отдаю предпочтение второму варианту. Ниже показано, как вы можете изме­нить компонент ArtistView, чтобы остались видимыми ТОЛЬКО ПОЛЯ FIRST И LAST. При этом поле code остается незатронутым, и это важно, поскольку оно задейство­вано в отношении "один ко многим" в таблице album:

Private void jblnit() throws Exception

{

∕∕ Код опущен

TableArtist. SetHiddenColumns(new int[]{0,3,4,5,6,7});

}

Несложно заметить, что метод SetHiddenColumns компонента JdbTable позво­ляет указать поля, которые должны быть скрыты.

Если вы запустите свое приложение на этом этапе, вы увидите два поля из таблицы artist и все поля из указанной строки таблицы album, как показано на рис. 42.12.

Вы можете сказать, что все выглядит не совсем корректно. Однако чтобы это ис­править, сначала потребуется рассмотреть, что такое вычисляемые поля.

На данном этапе у вас есть базовые знания, необходимые для создания отноше­ний "один ко многим" между двумя таблицами. Среди сопровождающих книгу ма-

Рис. 42.12. Вручную созданное отношение "один ко многим"(версия 0.5)

Териалов вы найдете программу MasterDetail, которая в точности демонстрирует, как этот процесс реализуется на практике.

Вычисляемые поля

Под Вычисляемым полем (Calculated Field) понимается новый столбец, добавляе­мый к таблице, значение которого вычисляется во время выполнения. В нашем слу­чае мы показываем пользователю имя и фамилию из таблицы album. Пользователь обычно не нуждается в том, чтобы эти поля находились в двух отдельных столбцах. Скорее всего, он даже этого не хочет. Очевидно, что вместо этого пользователь пред­почел бы видеть оба эти значения в одном поле, где имя и фамилия были бы объе­динены: Pat Metheny, Andres Segovia, Steve Gordon, Leona Boyd, Joe Sample и т. д.

Откройте queryDataSetArtist в панели структуры и выберите поле <new column>, как показано на рис. 42.13.

Измените значение свойства CoIumnName на firstLastCalc и установите для свой­ства caption значение Name. Для свойства CaIcType установите значение Calculated. На данном этапе вы сможете прокрутить инспектор вверх и заполнить свойство name, которое должно принять значение firstLastCalc. По завершении все должно выглядеть так, как показано на рис. 42.14.

jbndder g-* srrjavazauijntitledsznnttticdlя/stε∕unttt⅛rΠfl∕f)atamerfalσ1 java,£не ξdtt search view project run team wljards iools window help
d ls s ’ъ ⅛'β<j" √⅝1 гйь- 
½l s∙ - s? - jΛ - e - *∙ *> *
⅛tfβa s3 ufltriedi 8 jpx e⅛] untmedi bjpx
!⅛tff tιnttβd18 ⅛⅛f unmledl8.ul ,& apphcationijava e⅛. datamodulei java φ framel java -⅛ sqiras java
,kialitbwimtni , i х ft artsmew
x & mamodutai x ⅛ frarnal j x asqlres
swing i swing cortalnara ∣ dmdenpraaa f aswaig ! mara aswinaldbsw jrr
(3s ⅛- на и- '** □ [ɪj qfe:, ►
“ * ,∙ a⅛⅛»а.
,рис. 42.13. просмотр столбцов таблицы в компоненте querydatasetartist и выбор нового столбца,untmβdl idatamoduiei
ljlm : . j menu ⅛ -cjoataaccaee
pj database
∞ pp querydatasetartlst ∙ ∙∙* code
* la8t
• first
* born
• died
• ∙ birthplace
♦ comment
• arbsttype
,column cotumnna. catatype 9ral⅛
jcode [code jnt. 
last’ ɪ ilafft. s⅛⅛q ij 
first i⅛sτ~.. [string 
i born bqrn ⅛mestamp' •i 

,quaιyθatasatmst

В панели структуры выберите queryDataSetArtist. В инспекторе перейдите на зак­ладку Events. Создайте запись для события CaIcFieIds:

Void QueryDataSetArtist__CalcFields(ReadRow ChangedRow,

DataRow calcRow, boolean isPosted)

{

If (isPosted)

<

String CalculatedValue = changedRow. getString("FIRST"); CalculatedValue += " ,, + changedRow. getString("LAST") ; calcRow. setstring("fIrstLastCalc", CalculatedValue);

}

}

После того как вы вставите этот метод, можете запускать приложение. Оно будет выглядеть в основном похожим на то, как оно выглядело ранее, но теперь в таблице artist появится новое поле. Кроме полей имени и фамилии, будет еще и новое поле, содержащее имя и фамилию вместе. Чтобы увидеть это поле, возможно, надо будет воспользоваться полосой прокрутки в нижней части приложения (см. рис. 42.15). Далее в этой главе мы продолжим модификацию этой компоновки, чтобы она приобрела внешний вид, который бы в большей степени соответствовал ожида­ниям пользователей.

Вызов метода CaicFields происходит по одному разу для каждой строки набора данных. Вам передается копия текущего состояния каждой строки и место для вво­да изменений в строку. В конечном итоге, вам передается булево значение, относя­щееся к текущему состоянию строки. Вот те три параметра, которые попадают в об­работчик события CalcFields:

■ ChangedRow. Здесь находятся значения полей текущей строки.

CalcRow. Воспользуйтесь этим параметром для передачи в DataExpress новые значения, которые вы намерены вставить. Вы можете изменить любое из по­лей строки, но обычно бывает нужно изменить значения только в вычисляе­мых полях.

IsPosted. Это значение будет равно True, Если была выполнена запись зна­чений полей. Исходя из своего опыта, скажу, что это значение почти всегда

рис 42.15. просмотр нового вычисляемого поля, добавленного в наше простое приложение 34t *pat pmhenygroup
517*as rak wknta so rβk wicnta гак
518; bright sbe ue
s20=i can see yocr house from here
521 ɪletter from home
522 ⅛nβ** chautauqua 523jθffτamp
525 ⅛βlfe (tdwng)
525 ɪwe live here
,527 quartet 52fi∣ imaginary day 529 questions and answer 5x tharomloτou,u.

Равно true, если только пользователь не находится в процессе редактирова­ния строки. В таких случаях, вам, вероятно, не стоит пытаться делать вычис­ления по строке. Нужно подождать, пока isPosted не станет равно true, а за­тем вычислять значение. Строка будет записана, как только пользователь вы­зовет эту функцию через соответствующие пункт меню или кнопку, либо когда он переведет фокус на другую строку.

Полезно знать, когда происходит вызов метода calcFields. Лучший способ для того, чтобы это сделать, заключается в том, чтобы вставить в текст метода некото­рый код, который явно укажет на его состояние:

Int value = 0;

Void queryDataSetArtist_calcFields(ReadRow ChangedRow,

DataRow calcRow, boolean isPosted)

{

Value++;

System, ou t. pr in tin ("Значение: " + value + " " + isPosted); if (isPosted)

{

String CalculatedValue = ChangedRow. getString("FIRST"); CalculatedValue += " ” + ChangedRow. getString("LAST”); calcRow. setstring("firstLastCalc", CalculatedValue);

)

}

,⅛ i rains Till®

__________ fΓ1Rf×Γ

⅛⅛⅛t⅛J*TΓV⅛ ʌ flf**"

* ⅛⅛∙ ∙

BobbyDyIan

• ∙ Г

N SMraCSory

Andraws Scbulman

237Amertcan Garage

Wynton Marsalls

341 IPatMetbenyGroup

Miles Davls

I*

SI 7 As Falls Wlcblta. So Falls wCb Я

PhIIlD Glass

S

SIS-BrtghtSizeUfe

Joshua Redman

520:1 Can See Your House From Hi

SteekDan

U

521 .Letter From Home

Nell Young

622 New Cbautauqua

Julian Bream

Ii

523; Offramp

PatMetheny

S10

524 StIIILifeaalklng)

HoiiyCoie

S

525 We Live Here

Beatles

12

527 Quartet

Donald Fagen

13

528: Imaginary Day

Erie Clapton

Jki

529.Questιons AndAnswer [

FrankZappa

— 16

SSOTheRoadToYou Vj

рис. 42.16. изменение представления путем сокрытия столбцов first и last. в конечном итоге можно наблюдать только новое вычисляемое поле без заголовка строкиПри первом запуске приложения приведенный здесь оператор printin будет очень сильно занят. Он будет вы­зываться по одному разу для каждой строки набора данных, что в данном случае составляет приблизительно 200 раз. Затем метод не будет упоминаться до тех пор, пока вы не начнете редакти­ровать какую-нибудь строку. Пока вы редактируете строку, isPosted будет иметь значение false. Когда вы запи­шете строку, используя кнопку или пункт меню, либо перейдя на следую­щую строку, isPosted будет равно true.

На данном этапе, возможно, имеет смысл сделать столбцы имени и фами­лии, first и last, невидимыми, чтобы пользователь видел только новое вычисляе­мое поле (см. рис. 42.16). Для этого потребуется внести изменения в вызов метода SetHiddenColumns класса ArtistView:

TableArtist. SetHiddenColumns(new int[]{0,l,2,3,4,5,6,7}); tableArtist. SetRowHeaderVisible(false);

При добавлении полей 1 и 2 в метод SetHiddenColumns выполняется сокрытие полей first и last. Новое поле, столбец firstLastCaic, имеет номер 8, поэтому

Он остается видимым. Я также добавил код, который делает невидимыми поля с но­мерами строк, находящиеся в левом углу каждой строки. Эти поля называются заго­ловками строк. Под конец я выделил модуль данных, перешел в режим конструиро­вания, затем выделил строку IlrstLastCaIc и установил для ее свойства width значение 25. В окне кода результат будет выглядеть следующим образом:

FIrstLastCalc. SetWidth (25) ;

Если вы посмотрите на рис. 42.16, то наверняка заметите, что расположение компонентов на Framel Изменилось. В частности, теперь новый столбец занимает небольшое пространство, и освободилось больше места для таблицы album. Чтобы это изменить, откройте Framel, Выделите компонент Artistviewl В панели компо­нентов, и установите его предпочтительные размеры в 200, 425. При этом убеди­тесь, что его минимальные размеры имеют меньшие значения, например, 28, 28.

Последнее изменение внешнего вида приложения является чисто эстетическим. Внешний вид по умолчанию, принятый в Windows, для приложений такого рода не подходит. Что касается меня, то я бы открыл Applicationl. java И отредактировал строку, отвечающую за внешний вид следующим образом:

UIManager. SetLookAndFeel (" javax. swing. plat. metal. MetalLookAndFeel,,) ;

примечание"Металлический" внешний вид делает разделение между компонентами вашего приложения более ярко выраженным.

Основная цель этой главы заключается в том, чтобы показать, как рабо­тают основные функции баз данных в JBuiIder. Я попытался создать простой и легкий в построении интерфейс, чтобы он сопровождал наш урок. В своих программах вы, веро­ятно, будете тратить некоторое время на создание интерфейса, более приятного глазу. Тем не менее, даже простой интерфейс, созданный нами здесь, дал возможность рас­смотреть некоторые существующие свойства, предназначенные для изменения внешне­го вида приложения. Если вы начнете изучать эти свойства, то быстро поймете, что они Весьма разнообразны и сложны.

Редактирование данных

В созданном нами приложении таблица artist является таблицей, которая пол­ностью предназначена только для чтения. Таблицу album можно отредактировать, но мы не предоставили пользователю средства для записи изменений в базу дан­ных. В частности:

■ Все созданные нами вычисляемые поля предназначены только для чтения. Пользователь никоим образом не может изменять эти данные.

■ Таблицу album можно редактировать. Когда вы вносите изменения в данные строки, а затем переходите на другую строку, ваши изменения будут записаны в локальный набор данных. Однако приложение не располагает средством записи изменений, сделанных в локальном наборе данных, на сервер баз данных.

Сначала мы устраним вторую проблему, поскольку это сделать не особенно слож­но. Одно из решений состоит в том, чтобы просто поместить в верхнюю часть Frnmei Навигационную панель, как показано на рис. 42.17.

рис. 42.17. обновленная версия приложения с меню и навигационной панелью, расположенной в верхней части окна

Навигационная панель находится на закладке dbSwing палитры компонентов. Она расположена в крайнем правом углу и называется JdbNavTooIBar. После того, как вы поместите этот компонент на панель содержимого, находящуюся в панели структуры, потребуется установить для его свойства constraints значение North.

Теперь необходимо связать навигационную панель с queryDataSetAlbum. Чтобы это сделать, сначала при помощи мастера Data Module Wizard организуйте совмест­ное использование статического экземпляра модуля данных, как описывалось выше в этой и предыдущей главе. Откомпилируйте код, чтобы убедиться в том, что JBuilder не имеет претензий к текущему состоянию вашего приложения. Теперь щелкните на навигационной панели, чтобы перевести на нее фокус, перейдите в инспектор и установите для поля dataSet значение dMod. queryDataSetAlbum.

Если теперь запустить приложение, можно заметить, что при помощи появив­шихся кнопок в правом углу навигационной панели легко передать свою работу в базу данных или извлечь текущее состояние набора данных. Помните, что запись данных путем простого перемещения на другую строку или нажатия на кнопку фик­сации в навигационной панели изменяет только лишь локальную копию набора данных. Для передачи данных на сервер необходимо пользоваться кнопками, рас­положенными в правом углу.

Редактирование вычисляемого поля

В большинстве случаев вычисляемое поле будет содержать численное вычисле­ние, и вам не будет нужно, чтобы пользователь его редактировал. Однако в данном случае пользователь может захотеть отредактировать содержимое поля. Чтобы сде­лать это, необходимо предпринять некоторые особые меры

Нужно добавить в приложение меню. Перейдите на закладку Swing Containers и поместите JMenuBar в узел Menu панели структуры. Щелкните правой кнопкой мыши на JMenuBar в панели структуры и выберите в контекстном меню пункт Activate Designer. Создайте пункты нового меню File ∣ Exit и Options ∣ Edit Artist. В об-

рис. млъ. диалоговое окно для редактирования полей имени и фамилии из набора данных artistРаботниках для File ∣ Exit введите System, exit (O). Я опишу код для об­работки пункта меню Options ∣ Edit Artist в следующих нескольких абзацах.

Дополнительную информацию по со­зданию меню можно найти в главе 13.

Обязательно добавьте в конец метода ɔbɪnit () Следующую строку:

This. SetJMenuBar(jMenuBarl);

Я добавил в приложение меню.

Если вы выберите в этом меню пункт

Options I Edit Artist, появится диалоговое окно, показанное на рис. 42.18. Исходный код этого диалога показан в листинге 42.2. Это диалоговое окно дает пользователю возможность редактировать имя и фамилию в текущей строке.

import
import
import
import
import
Листинг 42.2. Диалоговое окно FirstLastInput

new jpanel() new jpanel() new jpanel() new jpanel() new jpanel() new jpanel() new jpanel() new jpanel() new jpanel()jradiobutton(); jradiobutton(); jradiobutton o; new jradiobutton();
new jradiobutton() new jradiobutton() ;
new jtextfieldo ; new jtextfieldo ;
jradiobutton4 = new jradiobutton5 = new jradiobutton6 = new jradiobuttonboth = jradiobuttonfirst — jradiobuttonlast =

jtextfield jtextfieldfirstname = jtextfield jtextfieldlastname =

JPanel jPanell JPanel jPanel2 JPanel jPanel3 JPanel jPanel4 JPanel jPanel5 JPanel jPanel6 JPanel jPanel7 JPanel jPanel8 JPanel jPanel9 JRadioButton JRadioButton JRadioButton JRadioButton JRadioButton JRadioButton public FirstLastInput(JFrame parent, DataModulel dMod)

{

Super(parent);

This. fName = dMod. getFirStName(); this. IName = dMod. getLastName(); this. dMod = dMod;

EnableEvents(AWTEvent. WINDOW_EVENT_MASK); try {

Jbɪnit () ;

)

Catch(Exception e)

{

E. printStackTrace();

)

SetSize(400, 220);

JTextFieldFirstName. setText(this. fName); jTextFieldLastName. setText(this. IName);

}

Private void jbɪnit() throws Exception

{

Borderl = BorderFactory. CreateMatteBorder(4,4,4,4,Color. pink);

This. getContentPane().setLayout(borderLayout2);

/∕setResizable(false);

JLabelFirstName. setText("First Name"); jPanell. setLayout(gridLayout2); gridLayout2.setColumns(1); gridLayout2.setRows(2);

JPanel3.setLayout(gridLayout3); grIdLayout3.setColumns(1); gridLayout3.setRows(2);

JLabelLastName. setText("Last Name"); jPanel2.setLayout(gridLayout4); jPanel4.setLayout(gridLayoutl); gridLayoutl. setColumns(1); gridLayoutl. setRows(3); jPanel4.SetBorder(borderl); gridLayout4.setColumns(2);

JRadioButton4.setText("jRadioButton3"); jRadioButton6.setText("JRadioButtonl"); jPanel5.setLayout(borderLayoutl);

JPanel8.SetLayout(flowLayout2);

FlowLayout2.SetAlignment(FlowLayout. LEFT);

JRadioButtonFirst. setText("First Name Only");

JRadioButtonFirst. addɪtemListener(new

FirstLastlnput-JRadioButtonFirst-ItemAdapter(this));

JRadioButtonLast-SetText("Last Name Only");

JRadioButtonLast. SddItemListener(new

FirstLastInput_jRadioButtonLast_itemAdapter(this));

JRadioButtonBoth. SetSelected(true);

JRadioButtonBoth. setText("First and Last");

JRadioButtonBoth. addɪtemListener(new

FirstLastlnput-JRadioButtonBoth-ItemAdapter(this));

JPanel6.SetLayout(gridLayout5);

GrxdLayout5.SetColumns(1);

GridLayout5.setRows(3);

JButtonCancel. SetText("Cancel");

JButtonCancel. addActionListener(this);

JButtonOk. SetActionCommand("Ok");

JButtonOk. setText("Ok");

JButtonOk. SddActionListener(this);

JPanel7.SetLayout(fIowLayoutl);

FIowLayoutl. SetAlignment(FlowLayout. LEFT);

JPanell. add(JLabelFirstName, null);

JPanell. add(JTextFieldFxrstName, null);

This. getContentPane().add(jPanel4, BorderLayout. CENTER);

J Panel4.add(j Panell, null);

JPanel4.add(jPanel3, null);

JPanel4.add(jPanel2, null);

JPanel3.add(jLabelLastName, null);

null);J Panel3.add(jTextFieldLastName, j Panel2.add(j Panel5, null);

null); null); null); borderlayout-south);JPanel2.add(jPanel6, null);

null);JPanel6.add(jRadioButtonFirst, j Panel6.add(JRadioButtonLast, jPanel6.add(JRadioButtonBoth, j Panel5.add(j Panel7, j Panel7.add(jButtonOk,

JPanel7.add(JButtonCancel, null);

JPanel5.add(jPanel9, BorderLayout-CENTER);

ButtonGroupl. add(jRadioButtonBoth); buttonGroupl. add(JRadioButtonLast); buttonGroupl. add(jRadioButtonFirst);

//}

Перекрыт, Стало быть, Можно выйти при закрытии окна Protected void processWindowEvent(WindowEvent Е) <

If (e. getID() == WindowEvent-WINDOW-CLOSING)

(

Cancel();

}

Super. ProcessWindowEvent(е);

)

Void cancel()

{

This. dispose () ;

}

Public String getFirst()

(

Return jTextFieldFirstName. getText();

}

Public String getLast()

{

Return jTextFieldLastName. getText();

)

Public void actionPerformed(ActionEvent e)

{

If (e. getSource() == jButtonCancel)

{

Cancel ();

)

Else

{

If (dlgState == DialogState. FIRST_ONLY)

<

DMod. SetFirstNameIjTextFieldFirstName. getText());

)

Else if (dlgState ≈ DialogState. LAST_ONLY)

{

DMod. SetLastName(jTextFieldLastName. getText());

)

Else

{

DMod. SetFirstLastName CjTextFieldFirstName. getText() , jTextFieldLastName. getText());

>

Cancel () ;

)

}

Private void ConfigureState()

(

If (dlgState = DialogState. FIRST_ONLY)

(

JTextFieldLastName. SetEnabled(false); jTextFieldFirstName. SetEnabled(true); jLabelLastName. SetEnabled(false); jLabeIFirStName. SetEnabled(true);

}

Else if (dlgState ≈ DialogState. LAST_ONLY)

{

JTextFieldLastName. SetEnabled(true); jTextFieldFirstName. SetEnabled(false); jLabelLastName. SetEnabled(true); jLabelFirstName. SetEnabled(false);

)

Else

{

JTextFieldLastName. SetEnabled(true); jTextFieldFirstName. SetEnabled(true); jLabeILastName. SetEnabled(true); jLabelFirstName. SetEnabled(true);

}

)

Void jRadioButtonFirst_itemStateChanged(ItemEvent e)

{

If (e. getStateChange() =“ ItemEvent-SELECTED)

(

DlgState — DialogState. FIRST-ONLY;

ConfigureStateO ;

)

Void jRadioButtonLast—ItemStateChanged(ItemEvent Е)

{

If (e. getStateChange() == ItemEvent-SELECTED)

{

DlgState = Dialogstate. LAST_ONLY; ConfigureState();

}

)

Void jRadioButtonBoth_itemStateChanged(ItemEvent e)

(

If (e. getStateChange() == ItemEvent. SELECTED)

{

DlgState — DialogState. FIRST—LAST;

ConfigureStateO ;

}

)

∕**

* Класс перечисления. Создает перечислимый тип для использования в Java. */

Private final static class DialogState (

Private final String name; private DialogState(String name)

(

This, name = name;

}

Public String toString()

{

Return name;

}

Static final DialogState FIRST_ONLY = new DialogState(nFIRSTjONLYn);

Static final DialogState LAST_ONLY ≈ new DialogState(nLASTjONLYn);

Static final DialogState FIRST—LAST = new DialogState(nFIRSTjONLYn);

}

)

Class FirstLastInput_jRadioButtonFirst_itemAdapter implements

Java. awt. event. ItemListener

{

FirstLastInput adaptee;

FirstLastlnput-JRadioButtonFirst-ItemAdapter(FirstLastInput adaptee)

{

This. adaptee = adaptee;

}

Public void ItemStateChanged(ItemEvent e)

<

Adaptee. jRadioButtonFirst_ItemStateChanged(e);

)

)

Class FirstLastInput-jRadioButtonLast—ItemAdapter implements

Java. awt. event. ItemListener

{

FirstLastInput adaptee;

FirstLastlnput-JRadioButtonLast-ItemAdapter(FirstLastInput adaptee) {

This. adaptee = adaptee;

}

Public void itemStateChanged(ItemEvent e)

{

Adaptee. jRadioButtonLast—ItemStateChanged(e);

}

}

Class FirstLastInput_jRadioButtonBoth_itemAdapter Implements

Java. awt. event. ItemListener

{

FirstLastInput adaptЕе;

FirstLastInput_jRadioButtonBoth_itemAdapter(FirstLastInput adaptee) {

This. adaptee = adaptee;

)

Public void ItemStateChanged(ItemEvent e)

(

Adaptee. jRadioButtonBoth_itemStateChanged(e);

)

)

Код для запуска диалогового окна выглядит следующим образом:

FirstLastInput firstLastDlg = new FirstLastInputtthisr dMod); fIrstLastDlg. show();

Самой важной характеристикой этого кода является то, что он не затрагивает внутренние структуры модуля данных программы или диалогового окна FirstLastinput. Это очень важно, если вы хотите создать легкий в поддержке и многократно используемый код.

В диалоговом окне FirstLastinput взаимодействия с модулем данных сведены к минимуму:

Public FirstLastInputtJFreune parent, DataModulel dMod)

{

Super(parent);

This. fName = dMod. getFirstName(); this. IName = dMod. getLastName(); this. dMod ≈ dMod;

EnableEvents(AWTEvent. WINDOW_EVENT_MASK); try {

Jblnit();

}

Catch(Exception e)

1

E. printStackTrace();

}

βetSize(400, 220);

JTextFieldFirstName. setText(this. fName);

JTextFieldLastName. setText(this. IName);

}

Конструктор диалогового окна FirstLastinput использует для извлечения дан­ных из текущей строки такие методы модуля данных, как getFirstName и getLastName. Обратите внимание на то, что модуль данных самостоятельно зани­мается извлечением данных из набора данных Artist. Было бы неразумно застав­лять диалоговое окно FirstLastinput заниматься выполнением этой задачи.

Ниже приведен соответствующий код модуля данных:

Private final static String firstNameField = "FIRST"; private final static String IastNameField= "LAST";

Public String getFirstName ()

{

Return queryDataSetArtist. getStringCfirstNameField);

}

Public String getLastName()

{

Return CfueryDataSetArtist. getString (IastNameField) ;

}

Вряд ли можно вообразить себе что-то более простое, нежели эти методы.

Следующий метод диалогового окна FirstLastinput Устанавливает новые дан­ные, после того как их отредактировал пользователь:

Public void actionPerformed(ActionEvent Е)

{

If (e. getSource() == jButtonCancel)

{

Cancel () ;

) ∙

Else

{

If (dlgState == DialogState. FIRST_ONLY)

{

DMod. SetFirstName CjTextFieldFirstName. getText());

}

Else if (dlgState == DialogState. LAST_ONLY)

{

DMod. SetLastName(JTextFieldLastName. getText());

}

Else

{

DMod. SetFirstLastName CjTextFieldFirstName. getText(), jTextFieldLastName. getText());

}

Cancel();

)

)

Если пользователь выполнит щелчок на кнопке Cancel, вся операция будет отме­нена, как если бы ничего не произошло. Если пользователь все же хочет изменить данные, осуществляется вызов одного из методов, в зависимости от того, какие дан­ные были изменены: имя, фамилия или оба значения. То, какой выбор сделал пользователь, определяется маленьким Java-классом, который разработан таким об­разом, чтобы обладать той же функциональностью, что и перечисляемые типы в Pascal или C++:

Private final static class DialogState {

Private final String name; private DialogState(String name)

{

This, name = name;

)

880

Public String toString()

{

Return name;

)

Static final DialogState FIRST_ONLY = new DialogState(nFIRSTjONLYn); static final DialogState LAST_ONLY = new DialogState("LAST-ONLY"); static final DialogState FIRST_LAST = new DialogState("FIRST_ONLY");

}

Классы подобного рода подробно обсуждались в главе 16.

Ниже приведен код из модуля данных, который используется для записи данных в набор данных:

public void setfirstname(string firstname)
{
cpierydatasetartist.setstring(firstnamefieldz cpierydatasetartist. pos t ();
)
public void setlastname(string iastname)
{
querydatasetartist.setstring(iastnamefieldz cpierydatasetartist.post () ;
)
public void setfirstlastname(string firstnamez (
cpierydatasetartist.setstring(firstnamefieldz cpierydatasetartist-setstring (iastneunefieldz cpierydatasetartist.post() ;
)
firstname);
iastname);
string iastname)
firstname); iastname);

Первые два метода устанавливают значения для локальной копии набора дан­ных.

Ключевой функцией является метод из DataModulel. java для наших вы­числяемых полей, который был переписан так, чтобы осуществлять вызов

QueryDataSetArtist. SaveChanges() :

Void CpieryDataSetArtist-CalcFields(ReadRow ChangedRowz DataRow calcRowz boolean isPosted)

{

String CalculatedValue = ChangedRow. getString(fIrstNameField); CalculatedValue += " n + ChangedRow. getString(IastNameField); calcRow. setstring(" fIrstLastCalc n z CalculatedValue); if (IisPosted)

(

CpieryDataSetArtist. SaveChanges () ;

)

)

Этот метод более подробно обсуждался ранее в этой главе. Данная версия этого метода дополнена лишь тем, что здесь происходит вызов метода savechanges. Именно метод savechanges, а не CpieryDataSetArtist-PostO в действительности записывает данные на сервере баз данных.

Метод CaicFields будет автоматически вызван сразу же после того, как был выз­ван какой-то из первых двух методов. Любые изменения в текущей строке приводят

К вызову метода caicFieids. Как предлагалось выше в этой главе, вы можете доба­вить в эту процедуру метод System, out. pr in tin (или точку останова), чтобы ви­деть, когда происходит его вызов*

If (!isPosted)

(

System. out. printlnCqueryDataSetArtistsCalcFields Posted"); queryDataSetArtist. SaveChanges();

}

примечаниеОбратите внимание на то, что в данном коде порядок операций отличается от предыдущих примеров этого метода. В данном случае, нам нужно знать лишь то, произошла ли запись данных. Если нет, тогда мы вызываем метод savechanges Если данные не были записаны, можно предположить, что в наборе данных про­изошли новые изменения.

Существуют другие логические способы решить описанную здесь зада­чу. Я выбрал этот способ только потому, что он демонстрирует природу обработчика со­Бытий для вычисляемого поля.

Использование ClbDisposeMonitor и других служебных утилит

При создании формы, содержащей элементы управления данными, часто не по­мешает добавлять компонент dbDisposeMonitor, расположенных на закладке More dbSwing. Этот компонент будет обеспечивать то, что при закрытии окна для всех элементов управления данными, расположенных на JFrame Или JPanel, Находя­щихся в этом окне, свойство Dataset Будет иметь значение Null. Это позволит Java более эффективно очищать память для приложений, использующих элементы уп­равления данными. В частности, это даст возможность сборщику мусора выполнять свою работу более эффективно.

В целом, Я бы порекомендовал вам помещать DbDisposeMonitor Во все фреймы и панели, использующие элементы управления данными. Просто при помощи инс­пектора установите значение свойства dataAwareComponentContainer компонента dbDisposeMonitor равным this, и тогда все должно быть в порядке.

Можно произвести более точную настройку компонента DbDisposeMonitor. На­пример, существуют свойства булевого типа, которые определяют, хотите ли вы зак­рыть соединения с базой данных в своем компоненте. Также имеется невизуальное свойство DataSetAwareComponents, Которое содержит массив элементов управле­ния данными. Чтобы использовать это свойство, введите что-то подобное:

DBDisposeMonitorl. SetDatasetAwareComponents( new DataSetAware[] {albumViewl. jdbTablel});

Можно использовать это свойство вместо свойства DataAwareComponentContainer. Если вы присваиваете значение свойству DataAwareComponentContainer, Тогда СВОЙСТВО DataSetAwareComponents Игнорируется.

Свойство ExecuteθnWindowClosing Определяет, нужно ли, чтобы элемент управ­ления освобождал ресурсы уже при простом закрытии окна, а не при его удалении из памяти.

К другим служебным утилитам, которые часто имеет смысл использовать, отно­сится DBExceptionHandier, предназначенный для выдачи ошибки при возникнове­нии исключения DataSetException.

DBEventMonitor Помогает в отладке нового компонента путем создания отчета о событиях, сгенерированных вашими наборами данных. Метод DBEventMonitori. SetPrintStreamO МОЖНО ИСПОЛЬЗОВЭТЬ ДЛЯ ТОГО, Чтобы НЭПра — вить поток вывода в текстовый файл. Либо можно просто направить выдачу данных в стандартное устройство вывода, как это и происходит по умолчанию.

Необходимо просто поместить DBEventMonitor, Расположенный на закладке More dbSwιng палитры компонентов, на JFrame Или JPanel, Где находятся элементы управления данными. Установите значение свойства DataAwareComponent- Container Равным bold или имени элемента управления, за которым вы хотите на­блюдать. Теперь запустите приложение. По мере вашей работы с набором данных, в стандартное устройство вывода будут направляться данные о происходящих собы­тиях. Ниже приведено несколько строк выходных данных от монитора. Пожалуйста, обратите внимание на то, что я использовал символ \ только для того, чтобы раз­бить длинные фрагменты текста на несколько строк:

DBEventMonitorfSun Apr 14 17:28:02 PDT 2003] source: \

HOUSEADDRESS, NavigateListener. navigated()

DBEventMonitorfSun Apr 14 17:28:06 PDT 2003]

EditListener. modifying(dataSet: HOUSEADDRESS)

DBEventMonitorfSunApr 14 17:28:06 PDT 2003] source:

HOUSEADDRESS StatusListener. StatusMessage:

StatusEvent. EDIT_STARTED or StatusEvent-SORTING

DBEventMonitorfSun Apr 14 17:28:09 PDT 2003]source:

HOUSEADDRESS DataChangeListener. daJtaChanged: DataChangeEvent-ROWsCHANGED, rowAffected: 2

DBEventMonitorfSun Apr 14 17:28:09 PDT 2003] source:

HOUSEADDRESS DataChangeListener. postRow():

DataChangeEvent-POSTsROW, rowAffected: -1

B листинге 42.3 вы найдете методы, которые можно использовать для открытия или закрытия потока, в который можно производить запись с помощью DBEventMonitor. Обратите внимание на то, что я вызываю CioseStream Внутри Стандартного метода ProcessWindowEvent, Который JBuilder создает для всех стан­дартных объектов JFrame

Листинг 42.3. Методы getStream и CloseStream можно использовать для создания файлового потока, который затем передавать в dbEventMonitor

FileOutputStream fw,

PrintStream ps;

Private PrintStream getStream(String fileName)

{

Fw = null; ps = null;

Try

{

Fw = new FileOutputStream(fileNaroe) ; Ps = new PrintStream (fw) ;

)

Catch(IOException e)

{

System. out. println(e);

>

Return ps;

}

Private void CloseStream ()

{

Try

{

Ps. close () ; fw. close();

}

Catch(IOException e)

{

System. out. println(e);

}

}

∕∕ Стандартный конструктор, содержащий код инициализации DbEventMonitor public Framel()

(

EnableEvents(AWTEvent. WINDOW_EVENT_MASK); Try {

Jblnit(, ;

} catch(Exception e)

{

E-PrintStackTrace() ;

)

String fIleName = FileBox. InakeHoineDirectory (,,elvenware ") + "one2mn. txt"; PrintStream stream = getStream(fileName) ; dBEventMonitori. SetPrintStream(stream);

}

Protected void procesSWindowEvent(WindowEvent e)

{

Super. ProcessWindowEvent(e);

If (e. getID() == WindowEvent. WINDOW_CLOSING)

{

CloseStream();

System. exit(O);

)

)

Пример использования DBEventMonitor Включен в программу OneToManyCalc, Входящую в состав сопровождающих книгу материалов. Однако помните, что для того, чтобы монитор записывал в стандартное устройство вывода, вам лишь нужно установить ДЛЯ Свойства DataAwareComponentContainer Компонента DBEventMonitor Значение JFrarne, JPanel Или какой-нибудь другой ПОДХОДЯЩИЙ Элемент управления. Представленный здесь код нужен только тогда, когда вы хоти­те осуществлять запись в текстовый файл, а не в стандартное устройство вывода.

Следует добавить, что компонент DBEventMonitor Обладает большей гибкостью, нежели я продемонстрировал здесь. Например, вы можете связать его с определен­ным набором данных через СВОЙСТВО SetDataSets Компонента DBEventMonitor. Это полезно, если вы хотите использовать этот компонент внутри крупного невизуаль­ного компонента. Вы также можете наблюдать за отдельными компонентами по своему усмотрению. Ниже показано, как это делается:

ClBEventMonitorl. SetDatasetAwareComponents (new DataSetAware [ ]

{artistViewl. dbTablel});

В этой строке кода осуществляется доступ к таблице, которую мы поместили на панель Artistview. По умолчанию эта таблица будет иметь защищенный (protected) доступ. Вам следует явно установить доступ типа Public, Поскольку дан­ная таблица и Framel Находятся в разных пакетах. Вероятно, вам также понадобит­ся добавить оператор Import com. Borland. dx. dataset.* B верхнюю часть Framel. А вот когда все будет готово, вы сможете увидеть выходные данные только о тех со­бытиях, которые происходят с объектом JdbTable В компоненте Artistview. Далее показана некоторая часть кода, который генерируется при открытии таблицы и ре­дактировании первой записи:

DBEventMonitor[Mon Apr 15 12:57:38 PDT 2003] source: artist

LoadListener. dataLoaded()

DBEventMonitor[Mon Apr 15 12:57:38 PDT 2003] source: artist

AccessListener. accessChange()

AccessEvent-OPEN, reason: , AccessEvent-UNSPECIFIED

DBEventMonitortMon Apr 15 12:58:57 PDT 2003]

EditListener. modifying(dataSet: artist)

DBEventMonitortMon Apr 15 12:58:57 PDT 2003] source: artist

StatusListener. StatusMessage:

StatusEvent. EDIT_STARTED or StatusEvent-SORTING

Value: 198 false

DBEventMonitortMon Apr 15 12:58:57 PDT 2003]source: artist

DataChangeListener. dataChanged:

DataChangeEvent. ROW_CHANGED, rowAffected: O

DBEventMonitortMon Apr 15 12:58:57 PDT 2003]

EditListener. updating(dataSet: artist, newRow:

Com. borland. dx. sql. dataset. QueryDataSetO 6bade9:CODE=I:LAST=Dylan:FIRST=

Bobby:BORN=2001-07-15

OO:OO:OO. O:DIED=2-ll-30 00:00:00.O:BIRTHPLACE=Hibbing

MN. COMMENT=Com. borland. jb. io. InputStreamToByteArrayθ59a68:ARTISTTYPE=1: fIrstLastCalc=, oldRow:

Com. borland. dx. dataset. InternalRowO 5c62bc:CODE=I:LAST=Dylan:FIRST=

Bob:BORN=2001-07-15

00:00:00.0:DIED=2-ll-30 00:00:00.O:BIRTHPLACE=Hibbing

MN:COMMENT=com. borland. jb. io. InputStreamToByteArrayO 5 9a68:ARTISTTYPE=1: firstLastCalc=Bob Dylan)

Если вам нужно работать с таблицей album, а не с таблицей artist, можно доба­вить код, который работает с переменной albumviewl, а не с переменной ArtistViewl:

DBEventMonitorl. SetDatasetAwareComponents(new DataSetAware[]

{albumViewl. dbTablel});

Функция запроса пароля выдаст пользователю в нужное время диалоговое окно для ввода пароля. Простейший способ использования этого элемента управления заключается в отметку опции ввода пароля в диалоговом окне Connection, которое отображается после того, как вы выделите компонент Database в модуле данных и щелкнете на кнопке эллипсиса для свойства connection. В Результате изменится вы­зов метода Setconnection. В Следующем примере пароль запрашиваться не будет:

Database. SetConnection( New ConnectionDescriptor(

"jdbc:mysql://Iocalhost/jbbook",

"Charlie",

"foo" , // Это сам пароль

False, //Не запрашивать пароль

"org. gjt.пип.mysql. Driver")) ;

В следующем примере пароль запрашиваться будет:

Database. SetConnectioh( New ConnectionDescriptor(

"jdbc:mysql://Iocalhost/jbbook",

"charlie",

Null, ∕∕ Пароль установлен в Null

True, ∕∕ Запросить пароль у пользователя

"org. gjt. mm. mysql. Driver")) ;

Резюме

B Этой главе вы рассмотрели отношения типа "один ко многим" и вычисляемые поля. Представленный здесь материал должен дать вам хорошее понимание того, как разрабатывать приложения, взаимодействующие с базами данных, в среде JBuilder В частности, в этой главе мы меньше полагались на генераторы приложе­ний. Вместо этого приходилось самостоятельно манипулировать элементами управ­ления данными через их свойства, отображаемые в инспекторе.

В самом конце этой главы были затронуты вопросы применения компонента DBDisposeMonitor, с помощью которого можно очищать ресурсы, связанные с база­ми данных.

Чарли Калверт

Глава 43 _____

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *