Sei sulla pagina 1di 76

Introduction

While the articles at vbmysql.com have been well received, there is always something new to cover. Every week I get requests for new sample code and articles explaining some new aspect of using Visual asic and !y"#$. %ne of the recurring requests has been for a full tutorial covering the creation of an application, from design to deployment, using Visual asic and !y"#$. In response to these requests I have written this article as the first of a series of articles that will provide a full tutorial for the creation of an application using Visual asic.&E' and !y"#$. I will cover application design, database design, V .&E' database programming, and deployment of the finished application. 'his tutorial will be very hands(on, documenting the creation of a real application that will then be available for download in both source and binary form for future reference. Intermediate files will be available after each tutorial section that can be used to familiari)e yourself with the pro*ect as it progresses. In this first installment of the series I will be covering the design of our application+s database. , well(designed database can be critical to the success of your database pro*ect, allowing for increased expandability and scalability, not to mention it makes for easier development. In this tutorial I will describe the steps needed to begin your application design, then move on to the design of your database, finishing up with the writing of your table creation statements.

Choosing an Application
'he very first thing required when creating an application is a need. -omputer programs are created to fulfill needs that the application users have. In my office we have many staff members, each of whom may or may not be in the office at any given time. We currently make use of a simple application that tracks which employees are in the office and displays a short message for those who are out of the office indicating when they will return. 'his type of in.out tracking software is fairly common, but this type of application often comes with two limitations/ many of these applications are limited to only a certain number of users, usually in the area of 01(211. In addition, these applications often use flat files to store information/ requiring that each user have access to a shared network directory in order to use the system. oth of these limitations can be overcome by using an "#$ 3pronounced 4ess(que(ell5, not 4sequel56 database. If the application we use at the office had its source code available, modifying it would have been an option 3freedom to modify an application to suit your needs is one of the great benefits of using %pen "ource software and tools6. "ince the application in question is a relatively simple one, I have chosen to rewrite it using Visual asic and !y"#$. 'he idea for your application may come from a desire to 4build a better mousetrap5 7 developing a better version of an existing application. %n the other hand you may have a

need for an application but cannot find a suitable existing pro*ect. 8inally, the idea or need may be that of someone else/ either a client or your employer. In any case, the first step when developing an application is deciding which application to develop.

Listing Requirements and Features


%nce you have determined what application you will be developing, it is important to then decide what specifically the application will do. 9ou will want to define its basic functionality, along with certain features that the software will implement. If you are developing this software for yourself, this stage in the development process can be fairly informal, but it is nevertheless very important. If you are developing software for a client or an employer then creating a list of features and requirements is vital. 'he list will determine what your responsibilities are as a software developer and will give a clear definition of what is required for your pro*ect to be considered complete. It should also be remembered that clients and employers have a tendency to request additional features as the pro*ect progresses while not wanting to pay any additional funds to implement them. If you have a list of features in hand you can inform your customer that your *ob is to implement the listed features and that any additional features will carry an additional cost. In addition to listing features and requirements, you will also want to develop a timeline and a budget. 9ou would want to schedule significant milestones such as pro*ect completion and ma*or phases of work being done. 8or example, you may wish to schedule a milestone to signify the delivery of application and database designs to the customer. 9ou may also want a milestone for delivery of a working demonstration of the pro*ect. %ften these milestones coincide with payments from the client when you work as a consultant, with the payments being defined in your budget. The Development Triangle %n the sub*ect of money and features and timelines, it is important to understand the balance that must exist between the three. 9our client will undoubtedly want you to deliver as many features as possible within a small budget and a short time period. It will be important for you to reali)e that you may sometimes need to re*ect a requested feature or timeline in order to keep everything in balance. Imagine that time, features, and money are three corners of a triangle:

While you can stretch the corners of this triangle, you cannot change the area it occupies, as the area of the triangle represents your total resources. 'he impact of this is that every section can only increase at the expense of the other two: If you want a pro*ect to have more features you will have to either take more time or spend more money for additional tools or developers. If you want a pro*ect to cost less money, you either need to decrease the number of features or allow the pro*ect to take more time 3this is because either you

cut back on the number of developers or allow them to only work on the pro*ect in their spare time6. 8inally, if you want the pro*ect done faster you either have to decrease the number of features or spend more money for more tools and.or developers 3or at least pay overtime for your existing developers6. When negotiating a feature list and timeline with your client or employer be sure to keep the triangle in mind, and even show it to your customer so they can understand the relationship between features, money, and time. If you are developing the pro*ect for your own use you benefit from having the perfect client, but still keep in mind that there is only so much you can do with your limited time. let+s take a look at the feature and requirements list for our in.out tracking software:

$ogin should require a username and password ;ser should be prompted for a status message when logged in ;ser can choose between no status message, a pre(set status message, or specify their own message when logging in or out $ast two user(defined status messages should be available in a list along with pre( defined ones ;ser should be able to login from any <- with an Internet connection, with an encrypted connection available ;ser should be able to flag a fellow user and be notified when they check in ;sers should be divided into separate groups 3"ales, ,ccounting, <rogramming, etc.6 !ain screen should show a list of all staff 3or selected groups6 with name, in.out status, status message, and custom fields ,pplication should minimi)e to system tray ;ser should be able to customi)e appearance, window si)e, font si)e, date format, name format, colors "tandard users can change their status, display format, and send other users messages =eceptionist users can change group users+ status 3receptionists can be assigned to multiple groups6 !anagers can add.delete users within their groups, change their status 3for their groups6, and assign receptionists ,dministrators can create users, assign managers and administrators, and change group memberships. 'hey have no restrictions ;sers should be able to choose between having the application start minimi)ed or maximi)ed ;sers should be able to choose refresh interval for open window

9our features and requirements list should be as detailed as needed for you and your client, and more complex applications should have more detailed documentation. %nce your list of features, your budget, and your timeline is approved you can then move on to designing your application.

Entity Design
%ne good approach to application design is to look at the different entities that will be involved in the application. ,n entity is simply an ob*ect we want to store information about. >esigning an application based on its component entities is a basic principle of ob*ect oriented programming. In our application the main entity is our users. 'here are four different kinds of user: =egular ;ser, =eceptionist, !anager, and ,dministrator. 'here are two approaches we can take we can take to represent our different types of user. We could describe four different entities, one for each type of user, or we could *ust have one user entity with an attribute that defines its type. When different entities have a common base but still have ma*or differences it can be beneficial to think of a base entity that is inherited by the other entities. let+s take vehicles as an example. While all vehicles have a common foundation in that they all have wheels and move forwards and backwards, they can still be very unique:

,s you can see, each of the vehicles have properties common to all vehicles, such as tires, steering wheels, and radios. ,t the same time each vehicle is also quite unique. -ars are smaller and usually carry more passengers than trucks. 'rucks carry fewer passengers but have more cargo space. 'axis are similar to cars but carry fare(paying passengers and have - radios and fare calculating equipment. We could even have shown a taxi as being a sub(entity of a car, inheriting all the traits of a car and then adding special traits of its own. 'he only unique properties for our user entity is whether they are a manager, administrator, or receptionist. With such a small difference between the different users it is better to create only one entity with a property that designates the user+s administrative responsibilities. %ther entities in our application include groups and events. let+s look at each entity in detail. Users , user is any user in our system. ;sers can login to our application, set their status, and view the status of others. , user has the following properties:

&ame <hone &umber ;sername <assword ,dministrator 39es.&o6 -reation >ate >eleted 39es.&o6 -ustom "tatus !essages

?roups user manages ?roups user is a receptionist for

'he deleted flag is used to determine whether the user should appear in lists. While we could simply delete a user from our database when they leave the company, it is better to simply mark them as deleted. While this will occupy more space in the database, it is a good record(keeping practice. 9ou may notice that there is no In.%ut status associated with the user/ this is because their status will be recorded in the Events entity. Groups , group is a set of users who share something in common. In most businesses staff members would be grouped by department/ such as ,ccounting, "ales, and "upport. It may be useful to allow users to be members of multiple groups so that a user can be in his department group and perhaps also be listed in a special group set up for an inter( departmental pro*ect. 'he following properties will apply to groups:

&ame -reation >ate >escription <rivate.<ublic >eleted 39es.&o6 !anagers =eceptionists

, private group is one that cannot be viewed by those outside the group. %ne example would be a group of executives who do not wish their status to be known by non( executives. Events ,n event is simply a change in a user+s status. When a user signs out, an event will be generated that will record which user the event refers to, whether the user signed in or out, their status message, and who caused the event. While normally a user will cause his.her own event, receptionists are allowed to sign other users in and out. We will want to be able to track who signed a given user in or out. 'he properties of events are as follows:

'imestamp ;ser In.%ut "tatus !essage -reator

%nce we have our entities defined, we can add them to a diagram:

In reality there are a few more entities but these three main entities are sufficient for the purposes of this tutorial.

Entity Relationships
&ow that we have our entities defined we need to look at the relationships between them. !y"#$ is a relational database and as such the data within it is defined by its relations. %ne way to define entity relationships is in terms of the quantities on each side of the relationship between two given entities. 8or example, a user can belong to one group 3accounting6, or many groups 3accounting, year end team, -hristmas party committee6. 'his is called a one(to(many relationship. Each group will contain many users. 'he relationship between the user and group entities is a many(to(many relationship 3many users belonging to many groups6. 'he relationship between users and events is different. ,n event will refer to one user, no more and no less. , user, on the other hand, will have many events that refer to him or her. 'his means that a one(to(many relationship will exist between the events and users. 'hese two types of relationship 3one(to(many and many(to(many6 will be taken into consideration when designing our database tables. 8or now let+s *ust draw some arrows to identify a relationship, with the arrowheads identifying the @many+ side of the relationship:

=elationships can be optional when a relationship can be defined as )ero(to(many, as is actually the case between the user and event entities. , new user may have never signed in or out. In this case there would be no events related to the user. %ur entities will influence our software in two ways. 'he entities that we have designed will be used later on to lay the foundation for our database tables. In addition, these entities will also help form classes, a building block of our final application 3the use of classes in application programming is referred to as ob*ect oriented programming and will be covered in an upcoming tutorial section6. 'here are a wide variety of ways to define data entities and their relationships. 'he description above is simply a general overview to get you started thinking in terms of entities, and does not represent any particular established method for describing and diagramming entities. >o you need to learn one of the established methodsA 'hat depends. When working on your own at developing an application it is often enough to quickly sketch out the entities and their properties and relationships using a system that you

understand and are comfortable with. If you find yourself managing a large pro*ect and working with other developers it becomes important to adopt a more formali)ed approach. %ne good system that is becoming increasingly popular is ;!$, or the ;nified !odeling $anguage. 'here are a myriad of books on the sub*ect of ;!$, some of which are even specifically directed at using ;!$ to design Visual asic B applications. ,n example of the latter would be >eveloping ,pplications with Visual asic and ;!$ by <aul =eed. , good portal to ;!$ resources is available from I ! at http:..www( C1B.ibm.com.software.rational.uml.. <ay particular attention to the document 4Introduction to the ;nified !odeling $anguage4.

Designing The Data ase


%nce we have our entities and their relationships defined, we can move on to designing the database schema itself. When designing a database, we will want to draw our tables, with one icon per table, and include the table name and columns that make up the table. I will be using the Visio design tool by !icrosoft to produce the database diagrams, but you can use any drawing tool you have available 3including the decidedly low(tech pencil6. let+s start with the following:

Choosing A !rimary "ey We now have all our entities listed with the fields that will make up their tables. 'he first thing we need to add to our tables is a !RI#AR$ "E$. , primary key is a column in your table that will uniquely identify every row in the table. In the ;ser table you may be tempted to use &ame or perhaps <hone &umber as a primary key, but your company may have more than one Dohn "mith, or your employees may have to share phones. While it is possible to have more than one column together as a composite primary key 3such as making the primary key the combination of &ame and <hone &umber6, you have to be certain that every entry will be unique 3what happens when the two Dohn "miths share the same phoneA6. "o what do we use as a primary key for the user tableA In this case it is probably best to introduce a new column, called ;serEI>. 'his column will be an integer and will be incremented automatically by !y"#$ through the use of the ,;'%EI&-=E!E&' keyword 3more on ,;'%EI&-=E!E&' later6. 8or the ?roup table we can use the &ame column as a primary key, as each group will be required to have a different name so as to avoid confusion when choosing which group to place a user in. 8or the Event table we could use the 'imestamp column as a primary key, but there is one caveat with this approach: the 'I!E"',!< data type is only accurate down to the second, which means that if two users record an event within the same second 3not unthinkable on a busy system with hundreds of users6, the primary key+s

requirement for uniqueness will be violated. In this case we have one of two choices: we could use a combination of the 'imestamp and ;ser columns as a primary key, safely assuming that a single user is unlikely to cause two events within the same second. y using the primary key of another table 3the ;serEI> from the ;ser table6 as part of a table+s primary key, we would create what is known as an Identi%ying Relationship& ,n identifying relationship is one where the child table or entity cannot exist without its parent. ;ser and ?roup have a 'on(Identi%ying Relationship because a ;ser can exist without belonging to a ?roup, and a ?roup can exist without any members. ,n Event, on the other hand, is meaningless without having a user to refer to. ,nother option is to create a new column 3EventEI>6, which would once again be an ,;'%EI&-=E!E&' integer value. 'ypically either approach would be valid, but there is one important fact we should keep in mind: !y"#$ and Visual asic use different date formats. 'he !y"#$ format of 9999(!!(>> FF:!!:"" is often misinterpreted by Visual asic, which uses a date format of !.>.9999 FF:!!:"". let+s look at the differences, first with a !y"#$ query and then with a simple V example:
mysql> select now(); +---------------------+ | now() | +---------------------+ | 2004-01-05 13:29:03 | +---------------------+ 1 row in set (0.02 sec)

,nd now in the V Immediate window:


?now 1 5 2004 1:25:3! "#

,s you can see there are some big differences in date handling. ,side from the order of the values, !y"#$ uses leading )eros when representing numbers smaller than ten, and uses a GH(hour clock. 'hese differences in how dates are handled can produce serious problems for Visual asic as V will be expecting one format while !y"#$ will be expecting another. 8or this reason it is recommended that you &EVE= use any date type columns as part of a table+s primary key. 'herefore we will use an EventEI> column as the primary key of our event table. While this does not produce an identifying relationship, we shall still treat it as one within our application. let+s take a look at our diagram now that it has primary keys:

9ou may have noticed that I changed the &ame column of the ?roup table to ?roupE&ame. 'he reason for this is that primary keys are often referred to in database queries that involve multiple tables, and it is easier to say SELECT Group_Name FROM Group, User; rather than SELECT Group.Name FROM Group, User; trust me, its a lot

easier this way. ,s you can see our primary key columns get moved into the center box of our table icons and have been highlighted in bold. With a Visio database diagram, all columns that form part of the primary key are displayed in the center box and have the <I identifier to their left. 'he boldface highlighting indicates that this is a required field and cannot be left blank 3a field which does not have a value assigned is actually assigned the value of &;$$, meaning undefined or no value6. Choosing Field 'ames and Required Fields &ext we will want to clean up our field names and designate the rest of our required fields. , note on field names: while you can use spaces in a field name with !y"#$, it will make your life harder as you will need to enclose the name in back ticks 346 every time you want to use it. 8or example, to access the -reation >ate column you would need to query it like this: SELECT `Creation Date` FROM Group; I personally do not like having to use back ticks and always name my columns using underscore characters to represent spaces when referring to primary keys 3;serEI>6, and use capitali)ation of distinguish names for regular columns 3-reation>ate6. When setting field names you will want to keep readability in mind/ you want column names to be short enough to type quickly when using them repeatedly, but you also want to make sure that future developers 3including yourself6 will be able to understand their meaning:

)ne(To(#any Relationships &ow that we have our columns named, let+s define the relationships between the tables. 8irst let+s look at the one(to(many relationship between an Event and a ;ser. When dealing with a one(to(many relationship, we place the primary key of the 4many5 table as a field in the 4one5 table 3I should note that column, field, and property are synonyms6. In this case we already have a ;ser column in the Event table, but let+s make it clearer by renaming it to match the definition in the ;ser table by calling it ;serEI>. In addition, we can use the relationship arrow to draw our relationship 3once again the arrowhead points to the 4many5 table6:

,s you can see, an 8I2 identifier has been added to the left column/ 8I stands for Foreign "ey& , foreign key is simply a column that refers to the primary key of another table. 'he Inno> table handler built into !y"#$ handles what is known as relational integrity. =elational integrity is the requirement that foreign keys represent real values. 8or example, if you tried to insert a row into the Event table that had a ;serEI> of 21, Inno> would check if a ;serEI> of 21 existed in the ;ser table and return an error if such a parent row did not exist. -onversely, you would not be allowed to delete a row from the ;ser table if there were rows in the Event table that referred to the row in question. 'he use of Inno> is beyond the scope of a tutorial on basic !y"#$ usage and

will receive no further coverage in this tutorial but will be the sub*ect of a future article. 8urther information of Inno> can be found at http:..www.mysql.com.doc.en.Inno> .html. #any(To(#any Relationships &ow that we have established the relationship between Event and ;ser, we can move on to defining the relationship between ;ser and ?roup. ,s you will recall, the relationship between ;ser and ?roup is a many(to(many relationship. 'his kind of relationship is not as simple to implement as a one(to(many relationship. In a one(to(many relationship we can place the primary key of the 4many5 table in a column of the 4one5 table, but this does not translate effectively into our many(to(many scenario: which table will hold the primary key of the otherA 'he answer is that neither table will hold the primary key of the other. Instead, we will create a third table to *oin the two:

In this case the ;serE?roup table has two foreign keys: the ?roupE&ame and the ;serEI>. 'hese two foreign keys form a Composite !rimary "ey, ensuring that there is only one entry in the table for each unique ;ser.?roup combination. In addition, two fields have been added that will flag whether the user is a manager or receptionist for the group, allowing us to remove the fields from both tables referring to managers and receptionists. 'his design allows us to have multiple managers and.or receptionists for each group and also allows a single user to manage multiple groups.

'ormali*ing The Data ase


>atabase normali)ation is covered in detail in another article I wrote at http:..www.vbmysql.com.articles.database(design.normali)e.html and I would recommend reading it for more information on database normali)ation. In this tutorial I will cover the basics as they apply to our example design. &ormali)ation is the process of removing redundant data from your tables in order to improve storage efficiency, data integrity and scalability. 'his improvement is balanced against an increase in complexity and potential performance losses from the *oining together of the normali)ed tables at query(time. First 'ormal Form 'he normali)ation process involves getting our data to conform to three progressive normal forms, and a higher level of normali)ation cannot be achieved until the previous levels have also been achieved 3there are actually five normal forms, but the last two are mainly academic and will not be discussed6. 'he 8irst &ormal 8orm 3or 2&86 involves removal of redundant data from hori)ontal rows. We want to ensure that there is no duplication of data in a given row, and that every column stores the least amount of information possible 3making the field atomic6.

We have two violations of 8irst &ormal 8orm 32&86 in our database model. 'he first is that our &ame column of the ;ser table holds both the first and last names of our user. 'his does not represent the smallest amount of data possible. In addition, it makes it difficult to sort users by last name. 8inally, our users will not be able to customi)e their lists to show names in order of first name.last name or last name.first name easily. 'o solve this we simply split our &ame column into two columns:

'he second violation of 2&8 is the hori)ontal redundancy of the "tatus columns 3note that while the columns appear vertically in this diagram the columns are actually hori)ontally arrayed when in table form. Imagine the table as a page on a ledger/ with column headings across the top of the page and individual rows below6. &ot only will it be hard to query hori)ontally redundant columns 39ou would need a query like SELECT * FROM User !ERE Status" # $M% Status& OR Status' # $M% Status&;6, but you have *ust locked your users into only having two custom status messages. If a customer ever demands the ability to store three status messages you will need to add another column and rewrite the sample query I *ust gave. Fori)ontally redundant columns can be dealt with by adding another table to store the status messages. 'his presents us with an opportunity to also add the predefined status messages to the system:

%ur new status table will hold every status message ever entered, whether the message is custom or predefined. %ur event table then links to the "tatus table to indicate which "tatus message the event uses. If a message is pre(defined then the ;serEI> field will not be populated. If the status message is a custom message then the ;serEI> field will be populated, associating the status message with its author for later retrieval 3remember that one of our features on the list is the retrieval of previous custom status messages for reuse6. efore we declare our table to be in first normal form, there is one other ad*ustment we can make. =ather than store a flag for !anager and a separate one for =eceptionist we can combine the two columns into one ,dministrative $evel column. "ince the two positions are mutually exclusive there is not much point in storing both fields separately, as no user will ever be a !anager ,&> a =eceptionist for the same group:

"ince a user could have no administrative privileges over the group I have made the field one that is not required. 'his way the field can be populated with &;$$ to indicate that the user has no administrative privileges. +econd 'ormal Form

Where the 8irst &ormal 8orm deals with redundancy of data across a hori)ontal row, "econd &ormal 8orm 3or G&86 deals with redundancy of data in vertical columns 3where the same data appears in multiple rows6. 'he normal forms are progressive, so to achieve "econd &ormal 8orm, your tables must already be in 8irst &ormal 8orm. In this case, the titles given for the administrative levels 3receptionist, manager6 could potentially appear in many rows, as a large company could have a lot of receptionists. We deal with vertical redundancy the same way we deal with hori)ontal redundancy, by adding tables to our schema:

%ur new table will allow us to change the title of an administrative level 3say from !anager to "upervisor6 without having to make the change for every user that is a manager, as would be the case if you had the word manager in every row that it applied to. 'his improves ease of maintenance. In addition we may find that we eventually need to add more data regarding the rights of the administrative level. With this approach we could easily add flags to the table representing different privileges the user possesses, then allow administrators to create new administrative levels as they see fit/ granting various permissions as they go. Third 'ormal Form In 'hird &ormal 8orm we are looking for data in our tables that is not fully dependent on the primary key, but dependent on another value in the table. 'ake an address for example: your -ity and "tate are not really dependent on you, but on your <ostal or Jip code. ,s such we could create a separate table with Jip -ode, -ity, and "tate and only store the )ip code in the ;ser table. While such an approach may ease maintenance it can also introduce complexity when trying to assemble an address. ,s such, data should be moved into third normal form when deemed necessary for maintenance of information. In our case there is not really any information in the schema that would be a candidate for further normali)ation into third normal form.

Choosing Column Types and ,riting CREATE TA-LE +tatements


, CREATE TA-LE statement is a special query we pass to !y"#$ to instruct it to create a new table to store our data. 'he syntax for -=E,'E ', $E can be found in the !y"#$ reference manual at http:..www.mysql.com.doc.en.-=E,'EE', $E.html. While the syntax may look complicated, it is actually quite simple. 'o create tables in !y"#$, we mainly need a table name, column names, and the type of data that each column will hold. let+s look at the ;ser table first. 'he first component of our -=E,'E ', $E query is the -=E,'E ', $E statement itself, which indicates the name of our table:
$%&'(& (')*& +ser (

'he opening round bracket indicates that the lines that follow define columns in our table. When defining a field we need a few pieces of information: first we need the name of the field, taken directly from the diagram above. "econd we need the data type of the field. 'his is where we determine whether the field will store numeric information, alphanumeric information, date and time related information, or other special data types. 'he full list of data types is available in the !y"#$ reference manual at http:..www.mysql.com.doc.en.-olumnEtypes.html, and I recommend referring to the V .!y"#$ data type conversion table at http:..www.vbmysql.com.articles.visual( basicEmysql.datatypes.html.When choosing data types it is important to keep a balance between row si)e and usability. Every field will occupy a certain amount of space in your row, and will have a certain capacity for holding data. We want to choose the smallest data type that can reasonably hold our information. let+s take ;serEI> as an example/ our ;serEI> field will be holding an integer value, and in theory we need one for each employee in our organi)ation. %ur choices for data type include 'I&9I&', "!,$$I&', !E>I;!I&', I&', and I?I&'. 'he largest numbers that can be stored in these various data types are 2GK, CGKBK, LCLLB1K, G2HKHLCBHK, and MGGCCKG1CBL0HKK0L1K respectively. In this case we can look at 'I&9I&' with its capacity of 2GK and consider it to be too small for our purposes. "!,$$I&', with its capacity of CGKBK should be more than adequate for even the largest organi)ations. 'he reason for choosing the smallest data type possible for our data is that larger data types result in longer rows, and longer rows take more time to search through when performing queries. 'herefore it is always in our best interests to keep our rows as small as possible. y choosing the 2B bit "!,$$I&' data type over, say, the CG bit I&' data type, we have saved almost G gigabytes of storage over a million rows. 'hus another benefit of choosing smaller data types is better storage efficiency. 9ou can always redefine a column to larger data type later on, even once the server is in production use. 'he remainder of our row description is devoted to extra information about our field. 8irst of all, when dealing with numeric data types we have the option of specifying whether the field should be allowed to handle negative numbers. 'he actual range of numbers that can be stored in a "!,$$I&' field is (CGKBL to CGKBK. In our particular application it does not make sense to have a negative ;serEI> value. ,s such it would be beneficial to declare the field ;&"I?&E>. &ot only does this prevent the insertion of negative numbers into our table, it also increases the maximum value we can hold in the field to B00C0 3this is because in binary form the leftmost bit of a number is reserved to represent the sign of the number. !y"#$ will use the leftmost bit to increase the maximum si)e of the number when the column is defined ;&"I?&E>6. ')T 'ULL. DEFAULT. AUT)/I'CRE#E'T. and !RI#AR$ "E$ ,nother piece of information we can designate for this field is whether the field can hold &;$$ values or not. ,s you recall, a field that is defined as required in our design is one that must have a value when the row is created or later updated, and &;$$ is a keyword that indicates the field has no value. ,s such, any field we earlier defined as required

must now have the &%' &;$$ keyword attached to it. &%' &;$$ will prevent the insertion of &;$$s and make the field mandatory. In combination with &%' &;$$ comes the >E8,;$' keyword. Dust because a field is defined &%' &;$$ does not mean that we have to specify a value when inserting records. ,ny &%' &;$$ field that is not specified when the row is inserted will receive the value pre(defined by the >E8,;$' keyword. &umeric values will automatically get a default value of )ero, while strings will default to an empty string 356. 9ou can use the >E8,;$' keyword to override this behavior. 'here are two final keywords that are commonly used when creating tables, but which can only be defined once. 'hese are the ,;'%EI&-=E!E&' and <=I!,=9 IE9 keywords. ,;'%EI&-=E!E&' indicates that if the field is not specified when inserting rows, the database will automatically generate a unique incrementing value and place it in the field defined ,;'%EI&-=E!E&'. 'his is very useful when using integers to identify rows, as the programmer does not have to worry about creating a unique number for each row he creates. 'he <=I!,=9 IE9 keyword is used to define the field as the primary key of the table. let+s look at the definition of our first column:
$%&'(& (')*& +ser ( +ser,-. -/( +/0-1/&. '+(2,-/$%&#&/( "%-#'%3 4&35

C0AR vs& 1ARC0AR &ow that our primary key is defined, let+s look at the name columns of our ;ser table. %ur name columns will be handled as strings, which are allowed to hold alphanumeric values. 'he two basic fields for handling strings are -F,= and V,=-F,=, each of which is defined in terms of the longest string it can hold 3defined in characters, between 2 and G006. 'he difference between -F,= and V,=-F,= is how they deal with unused space. let+s say you consider the longest possible last name to be H1 characters long, and a user has a name of Winstead 3L characters6. In a -F,= field, the field would still be H1 bytes long 32 character N 2 byte6, and the last CG characters would be blank spaces used to pad the string to the full H1 characters 3the trailing spaces will be removed when you retrieve the value6. 'his means that no matter how long the actual string is, it will still take a full H1 characters worth of space in the table. V,=-F,= on the other hand will store only the L characters and will only occupy L bytes of space in the table. 9our first instinct may be to choose V,=-F,= and benefit from the decreased storage space required in the table, in an effort to increase the speed of searches as I described above, but V,=-F,= is an exception to this rule. When you use V,=-F,= in a table 3as well as certain columns intended for large text and binary values6, the width of each row becomes inconsistent. %ne row may be H1 bytes long, another only L. !y"#$ can handle this by recording the length of the row in its file system, but the server must then read the length of each row before searching it and moving on to the next row, where it must check the length again, ad nauseam. y defining text fields as -F,= we will occupy more space on the hard(drive, but there is a speed increase involved as the server

will know that each and every row is exactly a given si)e 3such as 2G1 bytes6, allowing it to search through rows without checking their length first. ,s such I recommend defining all your text fields as -F,= to improve query performance, unless storage space is a strong consideration 3and with the prices of today+s hard(drives this is not really that great of a concern, especially with our application6. let+s look at the name columns:
$%&'(& (')*& +ser ( +ser,-. -/( +/0-1/&. '+(2,-/$%&#&/( "%-#'%3 4&35 *6st/6me $7'%(40) /2( /+**5 8irst/6me $7'%(40) /2( /+**5

,s you can see, each field will be a -F,= field that will hold up to H1 characters. 'he fields are defined &%' &;$$ to make them mandatory, with the standard default value of a blank string being used since we have not defined it otherwise. +toring !hone 'um ers 'here is no one sure way to store a phone number, as the data type used can be dependant on what you plan to do with your phone numbers. If you wish to do searches based on area code you may want to store the different parts of a phone number separately, with a field for area code, a field for the first three digits, a field for the last four digits, and perhaps even an optional field for the extension. 'his will make it easier to search and sort phone numbers. In our case we are only storing the number for the reference of a user+s fellow staff members, so we will be storing it in a simple -F,= column:
$%&'(& (')*& +ser ( +ser,-. -/( +/0-1/&. '+(2,-/$%&#&/( "%-#'%3 4&35 *6st/6me $7'%(40) /2( /+**5 8irst/6me $7'%(40) /2( /+**5 "9one $7'%(10) /2( /+**5

%ur ;sername and <assword fields will also be declared as -F,= columns:
$%&'(& (')*& +ser ( +ser,-. -/( +/0-1/&. '+(2,-/$%&#&/( "%-#'%3 4&35 *6st/6me $7'%(40) /2( /+**5 8irst/6me $7'%(40) /2( /+**5 "9one $7'%(10) /2( /+**5 +sern6me $7'%(1:) /2( /+**5 "6sswor; $7'%(40) )-/'%3 /2( /+**5

Fandling user logins will be discussed in a future article. 'he I&,=9 keyword used with the <assword field indicates that we want all comparisons with the <assword field to be done in a case(sensitive manner 3"o that <a""wo=> and password are treated as

different strings when validating users6. "trings in !y"#$ are treated in a case( insensitive manner unless the I&,=9 keyword is used. E'U# Columns 8or our ,dministrator field we will use an E&;! column. ,n E&;! column can be assigned only one value, chosen from a list of pre(defined values that we declare when creating the field. E&;! works well for representing value sets that will not increase in si)e as the database matures 3for that we use a separate table as with our ,dmin$evels table6. In this case we wish to represent a scenario that can only have two values: true or false 3either you are an administrator or you are not6. ,ssigning an E&;! involves created a comma(separated set of strings listing all possible values 3you can define up to B00C0 different potential values to an E&;! column, but if you need more than five or six you are probably better off creating a table to store the values6:
$%&'(& (')*& +ser ( +ser,-. -/( +/0-1/&. '+(2,-/$%&#&/( "%-#'%3 4&35 *6st/6me $7'%(40) /2( /+**5 8irst/6me $7'%(40) /2( /+**5 "9one $7'%(10) /2( /+**5 +sern6me $7'%(1:) /2( /+**5 "6sswor; $7'%(20) )-/'%3 /2( /+**5 ';ministr6tor &/+#(<(%+&<5 <8'*0&<) /2( /+**5

Date Columns 'he last two columns of our table are the -reated and >eleted fields. 'he >eleted field is once again a true.false column and will be treated the same as the ,dministrator field. 'he -reated field represents the date on which the record was created. !y"#$ provides four columns for handling date information, each of which is fairly self(explanatory: >,'E, 'I!E, >,'E'I!E, and 9E,= 3'here is a fifth 'I!E"',!< column that we will discuss later6. When choosing a data type to use consider what kind of information is needed. y far the most commonly used formats are >,'E and >,'E'I!E, as there is not as much use for *ust the time of day or year when recording information 3consider that most people do not search for time information regardless of day6. 8or our purposes the >,'E column is adequate as it is not essential to know the time of day that the row was created:
$%&'(& (')*& +ser ( +ser,-. -/( +/0-1/&. '+(2,-/$%&#&/( "%-#'%3 4&35 *6st/6me $7'%(40) /2( /+**5 8irst/6me $7'%(40) /2( /+**5 "9one $7'%(10) /2( /+**5 +sern6me $7'%(1:) /2( /+**5 "6sswor; $7'%(20) )-/'%3 /2( /+**5 ';ministr6tor &/+#(<(%+&<5 <8'*0&<) /2( /+**5 .elete; &/+#(<(%+&<5 <8'*0&<) /2( /+**5 $re6te; .'(&(-#& /2( /+**

)(3"& = #y-0'#;

'he closing round bracket indicates the end of the column definitions. 'he '9<E keyword is optional because the default table type is !yI",!, but we can use the keyword later to indicate that we wish to use one of the other !y"#$ table handlers, such as Inno> , FE,<, or > to name a few. !ore information on table handlers can be found at http:..www.mysql.com.doc.en.'ableEtypes.html, but for the purposes of this article we will stick to the default !yI",! table handler. 'he semicolon character 3/6 tells the !y"#$ server that we have finished our query and that it can go ahead and execute it. Execution of queries will be covered in a future tutorial, for now we will simply be preparing the queries for future use. I recommend you create your queries in a text editor such as notepad and save them using a filename such as ta()e_*reation_statements.s+). 'he .sql extension will indicate that the file holds "#$ queries. TI#E+TA#! Fields %ne special data type available with many databases is TI#E+TA#!. , timestamp column is very similar to the >,'E'I!E field but has certain special properties. If your table has a timestamp column then !y"#$ will automatically populate the field with the current time when a row is inserted without specifying a value for the timestamp column 3or if the timestamp column is specified as being &;$$6. 'he timestamp column will also be updated to the current time whenever a row is updated with new information 3once again as long as the column is either unspecified or set to &;$$6. %ne useful property of timestamp columns is their behavior when you have more than one timestamp column in a table. When you have multiple timestamp columns in a table, all timestamp fields will be assigned the current time when a new row is inserted, but only the leftmost timestamp column will be updated during subsequent updates of the table. 'his means that you can place two timestamp fields on a table, with the right one recording the creation date of the row and the left one tracking the last update on the row. We will use the 'I!E"',!< data type to record the creation time of events in our Event table, using the name 'imestamp. 'ypically you cannot use a data type as a column name as it is a reserved word, but the use of 'imestamp as a column name is an exception in !y"#$:
$%&'(& (')*& &>ent ( &>ent,-. #&.-+#-/( +/0-1/&. /2( /+** '+(2,-/$%&#&/( "%-#'%3 4&35 +ser,-. 0#'**-/( +/0-1/&. /2( /+**5 #ess6?e,-. #&.-+#-/( +/0-1/&. /2( /+**5 (imest6m@ (-#&0('#"5 0t6tAs &/+#(<-n<5<2At<) /2( /+**5 $re6tor 0#'**-/( +/0-1/&. )(3"& = #y-0'#;

, couple of things to note is that the In.%ut column was renamed "tatus to better reflect its meaning 3as In.%ut tells you what the column can hold but not what it means6. In

addition, the -reator column was changed from a required field to a non(required one. 'he logic behind this is that a &;$$ creator can signify that the user the event describes is the creator of the event, which could help later on when determining how often events are created by users other than the one the event describes. It is important to note that design can be an iterative process, and that changes like this are regularly made. 'he important thing is to remember to update your documentation when making changes:

Composite !rimary "eys In the ;serE?roup table we have a composite primary key. We can+t define both columns with the <=I!,=9 IE9 keyword and must instead use a different syntax:
$%&'(& (')*& +ser,1roA@ ( 1roA@,/6me $7'%(20) /2( /+**5 +ser,-. 0#'**-/( +/0-1/&. /2( /+**5 *e>el,-. (-/3-/( +/0-1/&.5 "%-#'%3 4&3 (+ser,-.5 1roA@,/6me) )(3"& = #y-0'#;

When creating a composite primary key we define the primary key after we finish defining our columns. De%ining )ur Remaining Ta les 'he remaining tables in our application will follow the same principles that have been described so far:
$%&'(& (')*& 0t6tAs ( #ess6?e,-. #&.-+#-/( +/0-1/&. /2( /+** '+(2,-/$%&#&/( "%-#'%3 4&35 +ser,-. 0#'**-/( +/0-1/&.5 #ess6?e $7'%(255) /2( /+**5 .elete; &/+#(<(rAe<5<86lse<) )(3"& = #y-0'#; $%&'(& (')*& 1roA@s ( 1roA@,/6me $7'%(20) /2( /+** "%-#'%3 4&35 $re6te; .'(&(-#& /2( /+**5 0co@e &/+#(<"ABlic<5<"ri>6te<)5 .elete; &/+#(<(rAe<5<86lse<) )(3"& = #y-0'#; $%&'(& (')*& ';min*e>els ( *e>el,-. (-/3-/( +/0-1/&. /2( /+** '+(2,-/$%&#&/( "%-#'%3 4&35 (itle $7'%(20) /2( /+**

)(3"& = #y-0'#;

'he full set of table definitions is available at http:..www.vbmysql.com.wp( content.uploads.G11B.21.vb(mysql(tutorial.)ip. It should be noted that once again the issue of reserved words has come up, as the ?roup table used a reserved word. It is possible to use a reserved word as a column or table name by wrapping it in back ticks 3O?roupO6 every time you use it but I prefer to avoid the hassle. Instead we+ll rename the table to ?roups 3generally you should avoid using plural forms in your table names6:

It should also be noted that *ust as you should use the same name for a foreign key as you use for the primary key 3use ;serEI> in all table referring to it6, you should also make sure to use the same datatype as well. "o make certain that all ;serEI> fields are "!,$$I&' ;&"I?&E>, and that all ?roupE&ame fields are -F,=3G16. Ensure that all -F,= and V,=-F,= columns are the same width 3in this case G1 characters6 as you do not want one table to be able to store a longer group name than another.

Conclusion
We have created a list of features and requirements for our application that will not only help us establish when our pro*ect is complete, but which also helps us define our application+s entities. %ur entity design will be used to define our application ob*ects, and is also used to design our database tables. &ow that our tables have been defined and designed, we can use the -=E,'E ', $E statements to create the tables in our !y"#$ server. In my next article I will cover installation of the !y"#$ server under Windows and then cover the basics of creating a database and its component tables.

Introduction
In my last article, I wrote about how to design a !y"#$ database for use as a data store for an application to be written in Visual asic.&E' 3V .&E'6. 'he completed table definitions can be found at http:..www.vbmysql.com.wp(content.uploads.G11B.21.vb( mysql(tutorial.)ip.In this tutorial, I am going to show you how to install #y+2L on ,indo3s, install the #y+2L 2uery -ro3ser, and how to load the table definitions into !y"#$ by using the +cript Editor feature of the !y"#$ #uery rowser. I will also show you how to enter some sample data into the database using the !y"#$ #uery rowser. 'his tutorial will assume that you are using !icrosoft WindowsP as the host operating system for both the !y"#$ server and the !y"#$ #uery rowser, and that

your copy of Windows is a recent Windows &' based version like Windows G111 or Windows Q<. 'his tutorial will also assume that you have no previous version of !y"#$ installed. ;pon completion of this tutorial, you should have a working !y"#$ installation that contains your database and some sample data, allowing you to begin developing with V .&E' in the next tutorial. ,t the end of this tutorial will be a link to a "#$ script containing all the "#$ statements used will be available. 'he script can be used to generate an exact clone of the database we will be building in this article.

Choosing a #y+2L 1ersion


'here are currently two versions of !y"#$ available for download from dev.mysql.com: !y"#$ 0.1 and !y"#$ 0.2. ,t the time of writing, !y"#$ 0.1 is the production versopm. 'his means that this version has been through extensive beta processes and has been cleared of any known bugs, with no new bugs reported within an arbitrary period. 9ou can find out the features included in the various versions of !y"#$ at http:..dev.mysql.com.doc.mysql.en.=oadmap.html. It is usually advisable to use the latest version of !y"#$ that has been declared production ready. While !y"#$ 0.2 will offer a greater feature set over !y"#$ 0.1, the fact that it is not yet released as ?, means that we would have difficulty determining whether a given error is caused by our code, or a bug in the alpha version of !y"#$.

Do3nloading and Installing #y+2L


!y"#$ 0.1 can be downloaded from http:..dev.mysql.com.downloads.mysql.0.1.html. 'here are three versions of the Windows download available: in,o-s Essentia)s, in,o-s, and it.out insta))er. In our case the in,o-s Essentia)s package will do *ust fine. >ownload the installer to your local hard(drive and when the download is complete, double(click on the install file to begin the installation 3the install file will have a name like mysql(essential(4&5&67(3in86&msi6. The #y+2L Installation ,i*ard %nce you double(click on the install file, the Installation Wi)ard will be displayed. 'here will be three different install types made available: T%pi*a), Comp)ete, and Custom. In our case, the Typical installation will suit our purposes. -hoose the 'ypical install option and click the 'e9t button. %n the confirmation screen, click the Install button to begin the installation. !y"#$ will be installed to the C/01ro2ram Fi)es0M%S3L0M%S3L Ser4er 5.6 folder, and you will be prompted to register with the !y"#$ web site. =egistration is optional but is useful if you want to use the forums at forums.mysql.com or report bugs at bugs.mysql.com.

'he final screen of the !y"#$ Installation Wi)ard prompts you to start the !y"#$ -onfiguration Wi)ard. 8or more information for using the !y"#$ Installation Wi)ard, see http:..dev.mysql.com.doc.mysql.en.WindowsEinstallEwi)ard.html. The #y+2L Con%iguration ,i*ard 'he !y"#$ -onfiguration Wi)ard will create a my.ini configuration file for you and will install !y"#$ as a service on your system. 'he full documentation for the !y"#$ -onfiguration Wi)ard is available at http:..dev.mysql.com.doc.mysql.en.WindowsEconfigEwi)ard.html, and is a good reference for advanced configurations. In this article I will cover the basics of using the !y"#$ -onfiguration Wi)ard, as applies to desktop developer use. 'he first dialog of the !y"#$ -onfiguration Wi)ard will prompt you to choose between a Stan,ar, Con7i2uration and a Detai)e, Con7i2uration. 'he "tandard -onfiguration is fine for a desktop developer who will be the only user connecting to !y"#$, where !y"#$ has to share resources with the rest of your desktop applications. -hoose the +tandard Con%iguration and click the 'e9t button. 'he next dialog displayed allows you to configure a Windows service for !y"#$. -onfiguring !y"#$ as a Windows service will allow !y"#$ to be started when your computer is booted, and is recommended to avoid the hassle of having to manually start !y"#$ every time you need to use it. I generally change the service name to the !y"#$H2 option so that it will play nice with other copies of !y"#$ on my desktop, but if you intend to stick to a single !y"#$ installation 3which is most likely the case6, you can use the default !y"#$ service name. -lick 'e9t to advance to the next dialog. ,fter configuring the !y"#$ service you will need to set the root password for your server. Whenever you have a production !y"#$ server you must set the root pass3ord to ensure security. If you do not set the root password, anyone who can access your !y"#$ server can cause all sorts of damage to your databases. If you are the only user, and the port to the server is blocked by a firewall, you may consider not setting a root password to simplify logging into !y"#$, but I do not recommend it. I recommend setting a root password, and also checking the Root ma% on)% *onne*t 7rom )o*a).ost option if you are installing !y"#$ on your desktop. >o not check the Create 8n 8non%mous 8**ount option. -lick the 'e9t button to advance to the confirmation dialog. %n the confirmation dialog, click the E9ecute button to begin the configuration process. 'he !y"#$ -onfiguration Wi)ard will create a configuration file, start the !y"#$ server, and set the root password you specified. %nce the configuration process is complete, click the Finish button to close the wi)ard.

Do3nloading and Installing the #y+2L 2uery -ro3ser


'he !y"#$ #uery rowser is a second(generation ?;I tool from !y"#$ , . 'he !y"#$ #uery rowser is a great tool that allows you to create and edit tables, and then easily browse the contents of the table, making changes as you go. 'he !y"#$ #uery rowser is currently in beta and is already a very useful tool. 'he documentation for the !y"#$ #uery rowser can be found at http:..dev.mysql.com.doc.query( browser.en.index.html. Installing the #y+2L 2uery -ro3ser 'he !y"#$ #uery rowser can be downloaded from http:..dev.mysql.com.downloads.query(browser.index.html. ,void the it.out 9nsta))er version. "ave the install file to your local hard(drive. 'he install file should have a name similar to mysql(query( ro3ser(:&:&:(gamma(3in&msi. %nce you have downloaded the install file, double(click it to install the !y"#$ #uery rowser. 'he installer is a standard install wi)ard and should require no explanation. 'he !y"#$ #uery rowser will be installed to C/01ro2ram Fi)es0M%S3L0M%S3L 3uer% :ro-ser ".6 unless you specify a different path.

Creating The Data ase


&ow that we have installed both the !y"#$ database server and the !y"#$ #uery rowser, we can begin the process of creating our database. "imply put, a database is a collection of data stored in tables made of columns and rows. , database serves as a container for the tables we created in the first tutorial. +tarting the #y+2L 2uery -ro3ser %nce the !y"#$ #uery rowser is installed, you can start the #uery rowser by clicking the +tart R !rograms R #y+2L R #y+2L 2uery -ro3ser. 9ou will then be presented with the connection dialog:

8ill in the fields with the information that is appropriate for your !y"#$ installation. If you installed the !y"#$ server and the !y"#$ #uery rowser on the same machine, use 2GK.1.1.2 as the hostname. 'he +chema field is the default database to use for queries. "ince we have not created the database for our application yet, this is set to the test database that is installed by default on all !y"#$ databases. -lick the )" button to start the !y"#$ #uery rowser. Creating A Data ase %nce the !y"#$ #uery rowser starts successfully, you should see a window like the following:

%n the right side you can see the database 3"chemata6 browser. 'his provides a list of all the databases currently residing on your !y"#$ server. 'he m%s+) database manages all the login information for your server and manages the permissions that users have when accessing !y"#$. 'he test database is provided as a place to test queries and table creation statements and is accessible to all users. 'he test database is highlighted in old text to indicate that the test database is currently the default database/ any queries you enter will be executed against the test database by default. 'o create the database, right(click within the database list and choose the Create 'e3 +chema option. 9ou will be prompted for a name for your database. ,t this point our application does not have a name, so we can *ust choose something descriptive. "ince our application will essentially track who is in and out of the office, I am going to name the database in/out. 'he name has no capital letters to avoid problems with case(sensitivity differences between Windows and $inux versions of !y"#$ 3you will notice that I

updated the "#$ script from the last tutorial to match this naming convention6. 9ou can separate words with a hyphen, underscore, or nothing at all. ,void using spaces in your database name, and remember that we want to avoid using reserved words. In my case I will be separating words with an underscore. %nce you have created the database, right(click on the database and choose the #a;e De%ault +chema option. 'he in;out database is now the default, and our upcoming table creation statements will be applied to this database. Creating the Ta les &ow that we have created the database and set it as the default, we can load the "#$ script with our table creation statements. >ownload the script from http:..www.vbmysql.com.wp(content.uploads.G11B.21.vb(mysql(tutorial.)ip to your local hard(drive. -hoose the )pen +cript < option from the File menu of the !y"#$ #uery rowser.

%nce the script is loaded, click the E9ecute button to create the tables. %nce the execution completes 3it should be instant6, you can click on the black arrow to the left of the database name to show the tables. -lick on the black arrows next to the table names to see the list of columns for the table:

,ssuming you encountered no errors, you database should now be created.

Creating a User
&ow that our tables are in place, we will want to create a user. 'o create a row in a table with the !y"#$ #uery rowser, first double(click the table 3in our case the user table6. 'his will create a "E$E-' S 8=%! user query in the top query area. -lick the Execute button to run the query, and a new result tab should be created with an empty set of rows:

-lick the Edit button at the bottom of the window to enable editing, then double(click in the )astname field to begin editing. 9ou can use the tab key to move to the next field in the row as you enter your data. In my case I am entering the following: lastname: Fillyer firstname: !ike phone: H1CCL1B0C0 NO !<1!ENS username: mike password: 2GCH0 administrator: '=;E deleted: 8,$"E created: G11H(22(GK 22:H2:11

'he most important thing is to remember what you table expects in terms of data formatting. 9our first and last name cannot be more than H1 characters. 9our phone number cannot be more than 21 characters. 9our username cannot me more than 2B characters and your password cannot be more than G1 characters. 'he values for the a,ministrator and ,e)ete, fields must be either TRUE or FAL+E, and the created field must be in the format of $$$$(##(DD 00=##=++. %nce you have entered your data, click the Apply Changes button to create the new row. 9ou can also click the Edit button to turn off row editing.

Conclusion
,ssuming you encountered no errors, you should now have a working copy of !y"#$ installed, along with the !y"#$ #uery rowser. 9ou should have been able to create the inEout database and populate it with the tables we designed in the previous tutorial. %nce the tables were created, you should have been able to create a new row in the user table. 9ou can also cheat and download the script at http:..www.vbmysql.com.wp( content.uploads.vb(mysql(tutorial(G.)ip and execute it to create a database that is an exact match of the one I created. In my next tutorial I will show you how to download and install Visual asic.&E' Express Edition and the !y"#$ -onnector.&E' database driver. I+ll show you how to combine these to create a login system that will allow users to authenticate against your !y"#$ database.

Introduction
'his article is part three of a series of articles made to document the creation of WindowsP applications using Visual asic.&E' and !y"#$. In the previous articles, I have described how to design an application and database, install !y"#$ and the !y"#$ #uery rowser, and how to populate our database using the "cript Editor feature of the !y"#$ #uery rowser. In this article, I will be describing the how to install !icrosoft Visual asic G110 Express Edition, the !y"#$ ,dministrator, and !y"#$ -onnector.&E'. We will use these tools to create a login form for our tracking application.

Designing a Login +ystem


'here are multiple options available when working on a login system, but typically it comes down to one of two options: 2. -uild your o3n login system& "tore usernames and passwords in a !y"#$ table, and use a single user account for all instances of the application that connect to

!y"#$. Each instance of the application will connect to one server account, then validate the user against the tables you created. G. Use #y+2L>s uilt(in security ta les& -reate !y"#$ users for each account, and let !y"#$ handle authentication and privilege management. 'hese approaches both have their respective benefits and drawbacks. When you build your own login system, you potentially have greater control over users, groups, and privileges. 9ou have simpler permission management because only one native !y"#$ user account is needed. %n the other hand, there is more potential for security problems because something you create yourself does not have the review process seen in the !y"#$ server. In addition, you have a security weakness in the fact that every client installation has a copy of the central user account for connecting to !y"#$, and that account has to have the maximum permissions needed for an administrative user. 'hese concerns are not so great when you are developing a web application, because it is easier to manage the one web instance than a collection of desktop applications. When you use the security built(into !y"#$, you inherit a robust privilege management system that has been thoroughly reviewed and tested. 9ou can limit privileges on a fine( grained level, and even assign different permissions based on which host the user connects from. 'he tradeoff is one of complexity: making the most of the built(in !y"#$ authentication is more difficult than building one of your own, and you must add a user to the !y"#$ server every time you want to add a new user to your application. ecause this is not a web(based application, we will be building our application using the built(in !y"#$ privilege system. In order to create a native !y"#$ user we will first install !y"#$ ,dministrator/ a ?;I application for server management.

Using #y+2L Administrator


'he !y"#$ ,dministrator is a ?;I tool provided by !y"#$ , for managing your !y"#$ server. 'he !y"#$ ,dministrator can be used to manage users, change server configuration, manage server databases, and monitor server status. Do3nloading and Installing #y+2L Administrator 'he !y"#$ ,dministrator can be downloaded from http:..dev.mysql.com.downloads.administrator.2.1.html. In our case we want the Windows version that includes an installer, avoid the @Without Installer+ version. "ave the installer file to your hard(drive and double click the installer icon to begin the installation. 'he installer is a standard Windows installer and should not require any special steps. Connecting to the #y+2L +erver ,fter the install is complete, there should be a !y"#$ ,dministrator icon on your desktop. >ouble(click the !y"#$ ,dministrator icon to display the login form.9ou can

also start the !y"#$ ,dministrator by clicking +tart R !rograms R #y+2L R #y+2L Administrator.

'he login form for the !y"#$ ,dministrator is almost identical to the login form for the !y"#$ #uery rowser, with the exception being that !y"#$ ,dministrator does not require you to specify a default schema. Enter your root login information and click the )" button to start the !y"#$ ,dministrator. Adding A User -lick the User Administration option from the left menu panel to bring up the user administration screen.

-lick the 'e3 User button to create your new user account. 8or the M%S3L User setting use the username value you set in your user row in the last tutorial. "et a password and set any ,dditional Information you want to specify 3all information in the 8,,itiona) 9n7ormation section is optional6.

%nce you have set a username and password, click the +chema !rivileges tab. -lick the in_out database and then the ?? button to assign all available privileges for the database to your user 3we will refine the privilege list in the future6. -lick the Apply Changes button to create the new user. 9ou can now exit the !y"#$ ,dministrator.

Connector@'ET
In early G11H !y"#$ , hired =eggie urnett of yte8Q and acquired his yte8Q .&E' data provider for !y"#$. 'he provider was renamed -onnector.&E' and not only is it provided free under the terms of the ?&; <ublic $icense, but it is one of the most feature(rich and best performing .&E' providers for !y"#$ that is currently available. -onnector.&E' is written in -T and is completely managed code, allowing it to be ported to any platform that supports .&E', including !ono. %ne advantage -onnector.&E' provides over other solutions is its use of the native !y"#$ protocol: many other solutions wrap the !y"#$ - client library and suffer a performance loss as a result. Do3nloading and Installing Connector@'ET !y"#$ -onnector.&E' is available for download at http:..dev.mysql.com.downloads.connector.net.. >ownload the version that includes an installer to your local hard(drive and extract the Jip file. >ouble(click the installer file to begin the installation process. <erform a complete install to the default directory.

1isual -asic&'ET
Visual asic.&E' is the new version of Visual asic. While it shares the Visual asic name, there are significant differences between Visual asic B and Visual asic.&E'. V .&E' is now entering into its third version. 'he first version was Visual asic.&E', the second was Visual asic.&E' G11C, and the new version is Visual asic.&E' G110. Visual asic.&E' G110 introduces a new Express version that we will use in this tutorial. 'he Express version of Visual asic.&E' is essentially a stripped(down version that still retains all the functionality needed to produce basic applications 3no pun intended6. Do3nloading and Installing 1isual -asic&'ET 6554 E9press Edition V .&E' G110 Express Edition is available for download at http:..msdn.microsoft.com.vstudio.express.vb.. $ook for a link to a Do3nload 'o3 link. %nce you have downloaded the installer, double(click the installer to being the installation process. 'he default installation options should be sufficient. +tarting 1isual -asic&'ET

%nce you have installed V .&E' G110, look for a link in the !rograms section of your +tart menu named 1isual -asic 6554 E9press Edition -eta. "tart V .&E' and click Ctrl U ' to start a new pro*ect.

Enter a name for the pro*ect and choose the button to create the pro*ect. The #ain 1-&'ET ,indo3

in,o-s 8pp)i*ation template. -lick the )"

%nce your pro*ect is created you should see a window similar to this one:

%n the left is the toolbox. 9ou can drag items from the toolbox to add them to your application. In the center is the workspace, showing your first form in design mode. %n the right is the "olution Explorer, which shows all the files involved in our pro*ect. 'aming The De%ault Form 8irst right(click on the Form".4( ob*ect in the "olution Explorer and choose the !roperties option. 'his will display a properties dialog where we can rename the file.

'he properties tool is used to set various properties for the ob*ects we will deal with as we work on our pro*ect. In this case we want to change the File 'ame property to more accurately reflect the purpose of the form. I will be prefixing the names of my forms with a %rm pre%i9. >o not change the extension of the form file. %nce you change the file name, click the form in the workspace. 'he properties tool will change to reflect the properties of the form itself. "croll down until you find the Te9t property and set it to something appropriate. In my case I set this to 9n;Out = Lo2in. 'he Te9t property determined what appears as the title of the form, and you can see this reflected in the workspace. Adding a Re%erence efore we can use -onnector.&E' with Visual asic, we need to add a reference to our pro*ect. y adding a reference, we are telling V .&E' where to find !y"#$ -onnector.&E' in order to access the ob*ects and methods of -onnector.&E'. 'o add a reference, choose the Add Re%erence< option of the !roAect menu. -hoose the -ro3se tab and browse to the -onnector.&E' installation, typically located at C/01ro2ram Fi)es0M%S3L0M%S3L Conne*tor Net n.n.n0(in0.NET N.N 3'he path may vary depending on the version number of -onnector.&E' and the .&E' 8ramework6. -hoose the #y+ql&Data&dll file and -onnector.&E' will be added to your pro*ect. 9ou will also need to add a reference to S%stem.Data.,)). +aving the !roAect &ow that we have configured a few settings in our pro*ect, we should save the pro*ect before continuing. -hoose the "ave ,ll option from the 8ile menu. ecause this is the first time we are saving the pro*ect, we are presented with the following dialog:

'hese settings should generally be acceptable, with the pro*ect being saved to a new folder within the #y Documents@1isual +tudio@!roAects folder 3In my case I use a custom path to !y >ocuments6. -lick the +ave button to save your pro*ect.

Designing The Login Form


'o design the login form, drag ob*ects from the toolbox onto the form. In our first version we will need to prompt users for a server address, username, and password. -lick on the Te9t-o9 ob*ect from the toolbox and drag it into the form. %nce placed on the form you can widen it, move it, and generally fine(tune its si)e and positioning. %nce you are happy with the placement of the textbox, go to the properties tool and find the 'ame property. -hange the name to better reflect the use of the textbox 3in my case I will be naming it t9t+erver6. <lace two additinal textboxes and name them t9tUsername and t9t!ass3ord. While it may be obvious to you and I what the purpose of these textboxes is, the end user will probably need some help. We will use La el ob*ects as cues to the user to indicate what each 'ext ox is used for. >rag the label ob*ect onto the form and place it in(line with the first textbox. %nce the label is placed, find the Te9t property in the properties tool and set the text to something like Ser4er/. =epeat this process two additional times for the remaining 'extbox ob*ects. ecause we will not be using the labels in actual application code there is not a real need to change the default names of the labels. 'he final element to add to our form will be a pair of buttons/ one to start the login process and one to cancel. >rag the buttons onto the form, and set the 'ame property to be *m,Lo2in and *m,Can*e). "et the Te9t property to be Lo2in and Can*e), respectively. %nce the process is complete your form should look something like this:

Creating an Event
I+m going to start by creating an event for the Cancel button. ,n event is something that triggers execution of code within V .&E'. In this case, the event in question will be the clicking of the -ancel button. 'he simplest way to create a button click event is to double click the button on the form. When you double(click the cancel button, the code view of the form will be displayed and you will see the following code appear:
"ABlic $l6ss Crm*o?in "ri>6te 0AB cm;$6ncel,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;$6ncel.$licD &n; 0AB &n; $l6ss

'he !u lic Class line indicates that this class describes our form, and the !rivate +u line shows that this is a subfunction that handles the C)i*> event of the *m,Can*e) ob*ect. 'he two End lines show where the code for each of these sections ends. We will add a single line to the subfunction to close the application when this button is clicked:
'@@lic6tion.&Git()

'his line instructions the application to close, and will be executed when we click the -ancel button. 9our code should now look like this:
"ABlic $l6ss Crm*o?in "ri>6te 0AB cm;$6ncel,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;$6ncel.$licD '@@lic6tion.&Git() &n; 0AB &n; $l6ss

+tarting the Application

%nce you have entered the code, save the pro*ect and press the F4 key to test your application. 9ou can also choose the +tart option from the >ebug menu or click the green icon on the toolbar. 9our form should be displayed and if you click on the -ancel button, the form should disappear as the application is closed. If you click on the $ogin button nothing would happen as we have not created any code for the event of clicking on the $ogin button.

Importing the Connector@'ET 'amespace


%b*ects in V .&E' are organi)ed into namespaces. &amespaces are logical grouping of ob*ects used to help organi)e the various ob*ects available in V .&E'. 'o use a -onnector.&E' Connection ob*ect, you need to define it as #y+ql&Data&#y+qlClient&#y+qlConnection 3more on this ob*ect later6. 'his of course is a lot to type on a regular basis, and we can use the Imports statement to shorten this. y adding Imports #y+ql&Data&#y+qlClient to the start of the source file, we can *ust refer to the -onnector.&E' -onnection ob*ect as #y+qlConnection.

Adding a #y+qlConnection ) Aect


!y"#$ -onnector.&E' is essentially a collection of ob*ects used to access a !y"#$ database. 'he first ob*ect we will use is the !y"ql-onnection ob*ect. 'he connection ob*ect serves as a broker between the other ob*ects contained within -onnector.&E' and the !y"#$ server. 'he connection ob*ect handles the login process and is the ob*ect we will use to verify that a user+s login information is correct. 'here are two steps to adding an ob*ect. 8irst we declare the ob*ect, then we instanciate it. When declaring an ob*ect we assign a name that we will use to refer to it, and also indicate the scope of the ob*ect, or in other words, what functions and procedures can access the ob*ect. In our case we will want to ensure that any function or procedure within the form will have access to the connection ob*ect, so we will declare the connection ob*ect first thing within the class:
-m@orts #y0ql..6t6.#y0ql$lient "ABlic $l6ss Crm*o?in .im conn 's #y0ql$onnection "ri>6te 0AB cm;$6ncel,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;$6ncel.$licD '@@lic6tion.&Git() &n; 0AB &n; $l6ss

'he Dim keyword is used when declaring ob*ects and variables. I+m using *onn as the name of my connection ob*ect. 'he As keyword is used to indicate what we are declaring 3an ob*ect, a variable, etc6. 8inally, #y+qlConnection is the ob*ect we are declaring.

Instanciating the #y+qlConnection ) Aect


&ow that we have declared the connection ob*ect, we will instanciate it. ;ntil we instanciate an ob*ect it is not actually available for use. We will instanciate the ob*ect within the subfunction that handles the click event for the Login button. >ouble(click the $ogin button in the design view to create the subfunction. 'o instanciate an ob*ect, we use the 'e3 keyword:
"ri>6te 0AB cm;*o?in,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;*o?in.$licD conn = /ew #y0ql$onnection() &n; 0AB

-uilding the Connection +tring


'he !y"ql-onnection ob*ect uses a connection string to know which server to connect to, which database to access, and what username and password to use to authenticate. 'he various properties are separated by semicolons. Fere is a sample connection string:
ser>er=loc6l9ost; Aser i;=miDe; @6sswor;=12345; ;6t6B6se=in,oAt

%f course, we need the connection string to reflect the information our user enters into the form. 'o do this we shall use the V character to combine multiple strings together, and the .'ext value of the 'ext ox ob*ects. ,dditionally I will use the E character to split our code into multiple lines:
"ri>6te 0AB cm;*o?in,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;*o?in.$licD conn = /ew #y0ql$onnection() conn.$onnection0trin? = Hser>er=H I tGt0er>er.(eGt I H;H , I HAser i;=H I tGt+sern6me.(eGt I H;H , I H@6sswor;=H I tGt"6sswor;.(eGt I H;H , I H;6t6B6se=in,oAtH &n; 0AB

)pening the Connection


'he last thing we need to do is instruct the connection ob*ect to open the connection to the !y"#$ server with the &)penBC method of the connection ob*ect:
"ri>6te 0AB cm;*o?in,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;*o?in.$licD

conn = /ew #y0ql$onnection() conn.$onnection0trin? = Hser>er=H I tGt0er>er.(eGt I H;H , I HAser i;=H I tGt+sern6me.(eGt I H;H , I H@6sswor;=H I tGt"6sswor;.(eGt I H;H , I H;6t6B6se=in,oAtH conn.2@en() &n; 0AB

Using a #essage-o9
%nce we have successfully opened the connection, we will want to let the user know that their username and password were correct. We can do this with the #essage-o9 ob*ect. In its simplest form, a !essage ox will display a message to the user, with an %I button. Fere+s a simple line of code to display a !essage ox:
#ess6?e)oG.09ow(H$onnection 2@ene; 0AccessCAllyJH)

Closing the Connection


When we are finished with our connection ob*ect we need to close it. y closing the connection we release the resources needed to keep the connection active. It is a good practice to close connections as soon as you are finished with them. 'he connection is closed with the &CloseBC method.
conn.$lose()

Disposing o% the #y+qlConnection ) Aect


%nce we are completely finished with an ob*ect, it is a good practice to dispose of it, thus minimi)ing resource usage in our application. When we dispose of an ob*ect, the resources it occupied are freed and the ob*ect no longer exists. We dispose of an ob*ect by calling its &DisposeBC method:
conn..is@ose()

Catching Errors
%ur code is currently only appropriate for an ideal situation. If we cannot connect to the server or if we provide the wrong username or password the connection ob*ect will return an error, also known as an exception. 'o handle errors, V .&E' has a special TR$ < CATC0 < FI'ALL$ syntax. We place the code with the potential error after the '=9 keyword and before the -,'-F keyword. 'he -,'-F keyword is used to indicate what kind of error we anticipate we might encounter 3in this case the error returned will be a -onnector.&E' #y+qlE9ception ob*ect6. ,ny code present after the 8I&,$$9 keyword will be executed whether there is an error or not. If an exception is encountered, the remaining code in the '=9 section will not be executed.

Fere+s the final code for the procedure that handles the connection:
"ri>6te 0AB cm;*o?in,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;*o?in.$licD conn = /ew #y0ql$onnection() conn.$onnection0trin? = Hser>er=H I tGt0er>er.(eGt I H;H , I HAser i;=H I tGt+sern6me.(eGt I H;H , I H@6sswor;=H I tGt"6sswor;.(eGt I H;H , I H;6t6B6se=in,oAtH (ry conn.2@en() #ess6?e)oG.09ow(H$onnection 2@ene; 0AccessCAllyH) conn.$lose() $6tc9 myerror 's #y0ql&Gce@tion #ess6?e)oG.09ow(H&rror $onnectin? to .6t6B6se: H I myerror.#ess6?e) 8in6lly conn..is@ose() &n; (ry &n; 0AB

'his brings all of our connection code together and allows it to handle errors without crashing. We instanciate the connection ob*ect and assign it a connection string. Within an error handling '=9 W -,'-F block we attempt to open the connection to the server and, if successful, we will show a !essage ox to the user indicating our success and then close the connection. If an error occurs while connecting, the code in the -,'-F block will be executed. In this case we will show the user a !essage ox with the &#essage property of the !y"qlException ob*ect, which contains the human(readable error message associated with the error. In the 8I&,$$9 block we will dispose of the connection ob*ect. We do this in the 8I&,$$9 block because whether or not the connection succeeds we will want to dispose of the ob*ect.

Testing the !roAect


%nce your code is in place, save the pro*ect and press the 80 key to begin the application in debug mode. 'ry using the proper server address, username, and password, then try using the wrong server address, then try the wrong password. 9ou will notice different error messages for using the wrong address and for using the wrong password. 'he error message for using the wrong username is the same as the error message for using the wrong password, as a seperate error message would provide a security threat in that a potential attacker would know whether they had a correct username or not.

Improvements

'here are a few improvements that will need to be made to our login form. 8irst of all, our user should not be expected to enter the server address every time they want to use our application, and in the future we will cover how to store the server address in a configuration file. 'he second improvement will be with regards to error handling. "howing the !y"#$ error message is fine during development, but the production version of our application should not show the database error messages directly. We can use the &'um er property of the !y"qlException ob*ect to determine what kind of error we are dealing with, and then create a custom error message. 8inally, we will of course need to eventually have a successful connection lead to a new form in the application. If our application consisted of a login form and nothing else, it would not be very popular.

Conclusion
In this tutorial we have covered installing !y"#$ ,dministrator and created a new !y"#$ user account. We then installed V .&E' and !y"#$ -onnector.&E'. 8inally we used V .&E' to create a login form for our application that will connect us to a !y"#$ server 3assuming we provide the correct server address and username.password6. 'he form is configured to handle errors during the connection by using the '=9 W -,'-F W 8I&,$$9 syntax. 'he pro*ect file created in this tutorial is available at http:..www.vbmysql.com.wp( content.uploads.vb(mysql(tutorial(C.)ip. 'he pro*ect files are for Visual asic G110 Express Edition. In the next tutorial we will create our first event and will create a form to display and update the current status of our users.

Introduction
'his article is part four of a series of articles on how to create a simple Windows application using Visual asic.&E' and !y"#$. In previous articles we have designed our database, installed the software we need to create our application, and designed a simple login form. 'he previous three articles are mandatory reading before starting this article. In this article I will demonstrate how to design queries, build forms, bind data to controls, and perform queries using the !y"ql-ommand, !y"ql>ata,dapter, and >ata'able classes. $ater I will demonstrate how to perform parameteri)ed queries and update data in the !y"#$ tables.

Adding Users. +tatus #essages. and Events to the Data ase

efore we query the database to get the current status of our users, we should add extra users to the system so we have some context for our query. In addition, we will want to add some pre(defined status messages and a few events to the system. 'o add the data, I will be using the !y"#$ #uery rowser. %nce the #uery rowser is open, double(click on the user table, then click the E9ecute button. -lick the Edit button at the bottom of the result set area, and add two new users to the database 3see ,rticle 'wo for more information6. 'he user details are not vital, *ust set the a,ministrator and ,e)ete, fields to False. %nce you have added two users, double(click on the status table and click E9ecute. We need to add some pre(defined status messages that our users can choose from. -lick the Edit button and add three messages. $eave the user_i, 'ULL, and set the ,e)ete, field to False. In my case I will be using the following three status messages:

?one to $unch ?one for the >ay In !eeting

%nce we have created users and status messages, we can add some events to the database. >ouble(click on the e4ent table and click the E9ecute button. 'he e4ent table is the core of the tracking application and brings together the user and status tables to allow us to track the status of all our users. When a user signs in or out, the event is tracked with the appropriate user id, status message, and time. In addition, we track who created the event, in case a secretary creates an event on behalf of a user. $et+s create a few events that can later be used when querying the database: event/id user/id message/id timestamp status creator 2 2 2 G110(12(2K 22:01:11 %ut 2 G G 2 G110(12(2K 22:0K:11 %ut G C C 2 G110(12(2K 22:0M:11 %ut C H 2 C G110(12(2K 2C:C1:11 In 2 0 G C G110(12(2K 2C:C1:11 In G B C C G110(12(2K 2C:C1:11 In C K G G G110(12(2K 2B:11:11 %ut G L C G G110(12(2K 2B:21:11 %ut C 'hese rows establish a chain of events of all three users going to lunch, returning to start a meeting, and then users G and C going home for the day.

Editing a Ta le De%inition

While we are looking at our data, you may wonder if a user should be able to sign in or out with no message at all. 'his is actually a desirable feature, but our current table definition does not allow it because messageEid is defined as &%' &;$$. 'o edit a table definition, right click on the table in the database browser and choose the Edit Ta le option.

,s you can see, there is a check mark in the ')T 'ULL column for messa2e_i,. -lick the check mark to remove the &%' &;$$ constraint and click the Apply Changes button to modify the table definition. With the table definition modified, add a final row to the e4ent table for user : with a &;$$ messageEid, a timestamp of 6554(5:(:D :7=55=55, and a status of In. 'his shows that the meeting ended, but that user 2 has not signed out and has no status to report.

-uilding a 2uery
&ow that our data is loaded into the database, we need to develop queries that can be used to retrieve the data in a format that is meaningful to our users. $ets begin with a simple +ELECT E query of the e4ent table, generated by double(clicking on the table in the !y"#$ #uery rowser:

'his does not present very useful information to an end user, so lets start by removing columns that our users do not benefit from:
0&*&$( e.Aser,i;5 e.mess6?e,i;5 e.timest6m@5 e.st6tAs 8%2# e>ent e

user/id message/id timestamp status event/id 2 2 G110(12(2K 22:01:11 %ut 2 G 2 G110(12(2K 22:0K:11 %ut G

C 2 G C G C 2

2 G110(12(2K 22:0M:11 C G110(12(2K 2C:C1:11 C G110(12(2K 2C:C1:11 C G110(12(2K 2C:C1:11 G G110(12(2K 2B:11:11 G G110(12(2K 2B:21:11 &;$$ G110(12(2K 2B:11:11

%ut In In In %ut %ut In

C H 0 B K L M

'his removed the e4ent_i, and *reator columns, which are not of interest to an end user. &ote that the event table is represented by an alias of e. 'his alias is then used as a shorthand when listing the columns to be queried. 8or a video demonstration on how to select individual columns, see http:..www.mysql.com.products.query( browser.tutorials.buildEqueries.html 3!acromedia 8lash required6. Foining Ta les ,ith an Inner Foin While the previous query is a bit more readable because it eliminates unnecessary rows, most end users will not like to identify their co(workers by number. 'he names of our users are stored in the user table, and can be used in our query by *oining the e4ent and user tables together. 'o *oin two tables together, we have to indicate to !y"#$ which columns can be used to relate the tables together. In this case, the user/id column of the user table is related to the user/id column of the e4ent table. In addition to establishing a relationship we need to select columns from the user table:
0&*&$( A.l6stn6me5 A.Cirstn6me5 e.mess6?e,i;5 e.timest6m@5 e.st6tAs 8%2# e>ent e5 Aser A K7&%& e.Aser,i; = A.Aser,i;

lastname %irstname message/id timestamp status Fillyer !ike 2 G110(12(2K 22:01:11 %ut Dones 'om 2 G110(12(2K 22:0K:11 %ut Dohnson Dulie 2 G110(12(2K 22:0M:11 %ut Fillyer !ike C G110(12(2K 2C:C1:11 In Dones 'om C G110(12(2K 2C:C1:11 In Dohnson Dulie C G110(12(2K 2C:C1:11 In Dones 'om G G110(12(2K 2B:11:11 %ut Dohnson Dulie G G110(12(2K 2B:21:11 %ut Fillyer !ike &;$$ G110(12(2K 2B:11:11 In In the +ELECT clause we added references to the )astname and 7irstname columns of the user table 3and used the alias u to refer to the user table6. In the FR)# clause we added a reference to the user table. 8inally, we added a ,0ERE clause to indicate that the user/id columns of both tables are related. With this relationship established, !y"#$ returned the first and last name of each user.

Concatenating Data !any users are accustomed to seeing names displayed in a format of Lastname. Firstname. 'his formatting can be achieved by concatenating the fields within the query using the -%&-,'36 function:
0&*&$( $2/$'((A.l6stn6me5 <5 <5 A.Cirstn6me) '0 n6me5 e.mess6?e,i;5 e.timest6m@5 e.st6tAs 8%2# e>ent e5 Aser A K7&%& e.Aser,i; = A.Aser,i;

name message/id timestamp status Fillyer, !ike 2 G110(12(2K 22:01:11 %ut Dones, 'om 2 G110(12(2K 22:0K:11 %ut Dohnson, Dulie 2 G110(12(2K 22:0M:11 %ut Fillyer, !ike C G110(12(2K 2C:C1:11 In Dones, 'om C G110(12(2K 2C:C1:11 In Dohnson, Dulie C G110(12(2K 2C:C1:11 In Dones, 'om G G110(12(2K 2B:11:11 %ut Dohnson, Dulie G G110(12(2K 2B:21:11 %ut Fillyer, !ike &;$$ G110(12(2K 2B:11:11 In Foining Ta les 3ith a Le%t Foin We can also *oin the status and event tables together to provide actual status messages instead of *ust messa2e_i, numbers. Fowever, if we use the syntax we used previously we will get incorrect results:
0&*&$( $2/$'((A.l6stn6me5 <5 <5 A.Cirstn6me) '0 n6me5 s.mess6?e5 e.timest6m@5 e.st6tAs 8%2# e>ent e5 Aser A5 st6tAs s K7&%& e.Aser,i; = A.Aser,i; '/. e.mess6?e,i; = s.mess6?e,i;

name message timestamp status Fillyer, !ike ?one to $unch G110(12(2K 22:01:11 %ut Dones, 'om ?one to $unch G110(12(2K 22:0K:11 %ut Dohnson, Dulie ?one to $unch G110(12(2K 22:0M:11 %ut Fillyer, !ike In !eeting G110(12(2K 2C:C1:11 In Dones, 'om In !eeting G110(12(2K 2C:C1:11 In Dohnson, Dulie In !eeting G110(12(2K 2C:C1:11 In Dones, 'om ?one 8or 'he >ay G110(12(2K 2B:11:11 %ut Dohnson, Dulie ?one 8or 'he >ay G110(12(2K 2B:21:11 %ut While our data is more readable with the actual status messages displayed, we are now missing our final row, which has a messa2e_i, of 'ULL. 'he reason for this is that the relationship between the e4ent and status tables is based on equality, and since the status

table has no rows with 'ULL for a messa2e_i,, the 'ULL row from the e4ent table is thrown out. 'his can be corrected by using a LEFT F)I' instead of an I''ER F)I'. In a LEFT F)I', a single row is returned for every row contained in the left(hand table of the LEFT F)I'. When there is a matching row in the right(hand table it is returned, otherwise a row with all values set to &;$$ is returned instead. In our case, we will place the e4ent table on the left and the status table on the right of the LEFT F)I' clause:
0&*&$( $2/$'((A.l6stn6me5 <5 <5 A.Cirstn6me) '0 n6me5 s.mess6?e5 e.timest6m@5 e.st6tAs 8%2# e>ent e *&8( L2-/ st6tAs s 2/ e.mess6?e,i; = s.mess6?e,i;5 Aser A K7&%& e.Aser,i; = A.Aser,i;

name message timestamp status Fillyer, !ike ?one to $unch G110(12(2K 22:01:11 %ut Fillyer, !ike In !eeting G110(12(2K 2C:C1:11 In Fillyer, !ike &;$$ G110(12(2K 20:2M:HM In Dones, 'om ?one to $unch G110(12(2K 22:0K:11 %ut Dones, 'om In !eeting G110(12(2K 2C:C1:11 In Dones, 'om ?one 8or 'he >ay G110(12(2K 2B:11:11 %ut Dohnson, Dulie ?one to $unch G110(12(2K 22:0M:11 %ut Dohnson, Dulie In !eeting G110(12(2K 2C:C1:11 In Dohnson, Dulie ?one 8or 'he >ay G110(12(2K 2B:21:11 %ut &ote that our 'ULL row has returned. When we use a LEFT F)I', the relationship information is moved from the ,0ERE clause to the FR)# clause, using the )' keyword. Formatting Dates While the query is getting quite readable, the date could use improvement. We can format date columns using the >,'EE8%=!,'36 function. 'he function is used by passing a date value and a format string:
0&*&$( $2/$'((A.l6stn6me5 <5 <5 A.Cirstn6me) '0 n6me5 s.mess6?e5 .'(&,82%#'((e.timest6m@5<MB M; M3 - Mr<)5 e.st6tAs 8%2# e>ent e *&8( L2-/ st6tAs s 2/ e.mess6?e,i; = s.mess6?e,i;5 Aser A K7&%& e.Aser,i; = A.Aser,i;

name Fillyer, !ike Fillyer, !ike Fillyer, !ike

message

DATE/F)R#ATBe&timestamp.>G Gd G$ H status Gr>C %ut In In

?one to $unch Dan 2K G110 7 22:01:11 ,! In !eeting Dan 2K G110 7 12:C1:11 <! Dan 2K G110 7 1C:2M:HM <!

Dones, 'om ?one to $unch Dones, 'om In !eeting ?one 8or 'he Dones, 'om >ay Dohnson, ?one to $unch Dulie Dohnson, In !eeting Dulie Dohnson, ?one 8or 'he Dulie >ay

Dan 2K G110 7 22:0K:11 ,! Dan 2K G110 7 12:C1:11 <! Dan 2K G110 7 1H:11:11 <! Dan 2K G110 7 22:0M:11 ,! Dan 2K G110 7 12:C1:11 <! Dan 2K G110 7 1H:21:11 <!

%ut In %ut %ut In %ut

%f course, most users cannot easily interpret DATE/F)R#ATBe&timestamp.>G Gd G$ H Gr>C, so we can assign an alias to make it easier. While we are at it, we can assign aliases to the remaining columns to make the data easier to interpret:
0&*&$( $2/$'((A.l6stn6me5 <5 <5 A.Cirstn6me) '0 /6me5 s.mess6?e '0 #ess6?e5 .'(&,82%#'((e.timest6m@5<MB M; M3 - Mr<) '0 .6te(ime5 e.st6tAs '0 0t6tAs 8%2# e>ent e *&8( L2-/ st6tAs s 2/ e.mess6?e,i; = s.mess6?e,i;5 Aser A K7&%& e.Aser,i; = A.Aser,i;

Creating a +u query %ur end users will not be interested in every single event that has occurred, but will instead want to know the current status of each user. 'o gather this information we must determine the latest e4ent_i, value from the event table for each user. We can determine the highest e4ent_i, value by using the #AIBC function:
0&*&$( #'N(e>ent,i;) 8%2# e>ent e

#AIBevent/idC M 'his does not do us much good because we need the maximum value on a per(user basis. ,ggregate functions like !,Q36, !I&36, ,V?36, and ";!36 can be used with a ?=%;< 9 clause to change the data that is aggregated:
0&*&$( e.Aser,i;5 #'N(e.e>ent,i;) 8%2# e>ent e 1%2+" )3 e.Aser,i;

#AIBe&event/idC M K L &ow we have the maximum eventEid value for each userEid in the event table.

user/id #AIBe&event/idC 2 M G K C L We can now use this as part of a subquery to determine the current status of each user. , subquery is a query within a query, contained within parenthesis. "ubqueries can be used in place of tables in a 8=%! clause, or to generate a list of values for use in areas such as a WFE=E clause. 'here is an excellent discussion of subqueries at the !y"#$ web site at http:..dev.mysql.com.tech(resources.articles.H.2.subqueries.html. 8or the purposes of our subquery we will remove the userEid reference, leaving us with a list of maximum eventEid values:
0&*&$( $2/$'((A.l6stn6me5 <5 <5 A.Cirstn6me) '0 /6me5 s.mess6?e '0 #ess6?e5 .'(&,82%#'((e.timest6m@5<MB M; M3 - Mr<) '0 .6te(ime5 e.st6tAs '0 0t6tAs 8%2# e>ent e *&8( L2-/ st6tAs s 2/ e.mess6?e,i; = s.mess6?e,i;5 Aser A K7&%& e.Aser,i; = A.Aser,i; '/. e>ent,i; -/ ( 0&*&$( #'N(e.e>ent,i;) 8%2# e>ent e 1%2+" )3 e.Aser,i; )

'ame #essage DateTime +tatus Fillyer, !ike &;$$ G110(12(2K 20:2M:HM In Dones, 'om ?one 8or 'he >ay G110(12(2K 2B:11:11 %ut Dohnson, Dulie ?one 8or 'he >ay G110(12(2K 2B:21:11 %ut )rdering 2uery Results %ne final tweak we can perform on this query has to do with ordering the data. %ur users are accustomed to reading information in alphabetical order, and we should aim to provide our data in the order they are expecting. We can sort the data returned from our query by using the )RDER -$ clause. 'he %=>E= 9 clause can take multiple column names for sorting, along with the optional DE+C keyword to indicate that data should be sorted in descending order. In our case we will sort by the name column:
0&*&$( $2/$'((A.l6stn6me5 <5 <5 A.Cirstn6me) '0 /6me5 s.mess6?e '0 #ess6?e5 .'(&,82%#'((e.timest6m@5<MB M; M3 - Mr<) '0 .6te(ime5 e.st6tAs '0 0t6tAs 8%2# e>ent e *&8( L2-/ st6tAs s 2/ e.mess6?e,i; = s.mess6?e,i;5 Aser A K7&%& e.Aser,i; = A.Aser,i; '/. e>ent,i; -/ ( 0&*&$( #'N(e.e>ent,i;) 8%2# e>ent e

) 2%.&% )3 n6me

1%2+" )3 e.Aser,i;

'ame #essage DateTime +tatus Fillyer, !ike &;$$ G110(12(2K 20:2M:HM In Dohnson, Dulie ?one 8or 'he >ay G110(12(2K 2B:21:11 %ut Dones, 'om ?one 8or 'he >ay G110(12(2K 2B:11:11 %ut 'his query will now form the basis of our status form.

Creating a +tatus Form


&ow that we have created some sample data and a query that can be used to show the current status of our users, we can start building a new form to display the query. "tart V .&E' and load the In(%ut pro*ect. =ight(click on the In(%ut pro*ect name in the "olution Explorer and choose the ,dd R Windows 8ormW option. ,n ,dd &ew Item dialog will appear with the Windows 8orm template selected. -hange the name of the form to frm!ain.vb 3we use frm!ain instead of frm"tatus because the is the primary form in our application6 and click the ,dd button. %nce Visual "tudio successfully creates the new form, you should be presented with a new blank form to work with. Designing the +tatus Form In our initial version, this form will have three controls, a datagrid to display query results and two buttons to refresh the datagrid and update our status. 8irst we will add a >ata?ridView control to our form/ find the datagridview in the left(hand toolbox, click it, and then drag the shape you want the datagridview to be on your form 3aim to have the datagridview occupy the bottom L0X of the form6. In the properties of the datagrid, change the name of the datagrid to dgv+tatus, and while setting properties, set the text property of the form to In()ut H +tatus 1ie3. In addition, add two buttons named cmdRe%resh and cmdUpdate, and set their text properties to Re%resh and Update +tatus, respectively. Creating a Connection +tring !roperty %ur -onnector.&E' connection string was created in frm$ogin, and we will need to pass it to frm!ain when we change forms so that we can continue to connect to !y"#$. We will do this by creating a public property in frm!ain to store the connection string. -lick the View -ode button to display the source code for frm!ain.vb:

'he View -ode button is the highlighted button located at the top of the "olution Explorer. ,t first this should be all that is shown in the code view:
"ABlic $l6ss Crm#6in &n; $l6ss

Within the class definition, type the following:


"ABlic $l6ss Crm#6in "ri>6te my$onn0trin? 's 0trin? &n; $l6ss

'his string will store the connection string generated by frm$ogin. 'he <rivate keyword means that other forms cannot access or modify the connection string directly. 'o allow access to the connection string for writing, we create a <roperty. 'ype <ublic Write%nly <roperty conn"tring ,s "tring after the string definition, and the following will be filled in for you:
"ABlic Krite2nly "ro@erty connection0trin?() 's 0trin? 0et()yE6l >6lAe 's 0trin?) &n; 0et &n; "ro@erty

'his is a code template for a write only property, meaning an external form can assign the connection string, but the connection string cannot be read externally once assigned. Within the "et block we will add a single line of code:
"ABlic $l6ss Crm#6in "ri>6te my$onn0trin? 's 0trin? "ABlic Krite2nly "ro@erty connection0trin?() 's 0trin? 0et()yE6l >6lAe 's 0trin?) my$onn0trin? = >6lAe &n; 0et &n; "ro@erty &n; $l6ss

%ur form can now have a connection string assigned to it. efore we can assign a connection string, we first move the connection frm$ogin connection string to a separate variable named my-onn"tring:
.im my$onn0trin? 's 0trin? my$onn0trin? = Hser>er=H I tGt0er>er.(eGt I H;H , I HAser i;=H I tGt+sern6me.(eGt I H;H , I H@6sswor;=H I tGt"6sswor;.(eGt I H;H , I H;6t6B6se=in,oAtH

conn.$onnection0trin? = my$onn0trin?

Instantiating and +ho3ing the Form &ow that we can assign a connection string to frm!ain, we need to update the code in frm$ogin for a successful login. Instead of showing a message box congratulating the user on a successful login, we will want to create an instance of frm!ain, pass the connection string to it, and then close frm$ogin. 8irst we need to change the "hutdown mode of our application. y default an application closes when its startup form closes, but in our case we will be closing frm$ogin early on and this will not work 3there are alternatives such as having frm!ain call frm$ogin at startup and closing it upon a successful login6. -hoose the <roperties option of the <ro*ect menu and make sure the ,pplication tab is active. -hange the "hutdown mode from .en startup 7orm *)oses to When last form closes and close the properties window. %ur application will now wait until the last form closes before ending. We need to make sure that all forms are closed when we are finished with them and not *ust hidden. %ne hidden but not closed form could leave our application running when the end user thinks the application has terminated. Fere is the new '=9 7 -,'-F block for the $ogin button of our login form:
(ry conn.2@en() conn.$lose() .im m6in8orm 's /ew Crm#6in m6in8orm.connection0trin? = my$onn0trin? m6in8orm.09ow() #e.7i;e() #e.$lose() $6tc9 myerror 's #y0ql&Gce@tion #ess6?e)oG.09ow(H&rror $onnectin? to .6t6B6se: H I myerror.#ess6?e) conn..is@ose() &n; (ry

8irst we open and close the connection to allow for an error to trigger the -,'-F block. If we are still in the '=9 block we instantiate the frm!ain class and assign the my-onn"tring variable. %nce the connection"tring property is set, we show the main form, then hide and close the login form.

2uerying Data %rom 1-&'ET

We have already dealt with the !y"ql-onnection class when creating our login form, and we will now be introduced to three additional classes: !y"ql-ommand, !y"ql>ata,dapter and >ata'able. The #y+qlCommand Class 'he !y"ql-ommand class contains the query or statement we will be sending to !y"#$. 'he !y"ql-ommand ob*ect helps with performance by allowing us to build queries using parameters when our queries contain static and dynamic elements. ,fter our query is created in the !y"ql-ommand ob*ect, we will pass the !y"ql-ommand ob*ect to the !y"ql>ata,dapter ob*ect for execution. The #y+qlDataAdapter Class 'he !y"ql>ata,dapter class is used as an adapter between the !y"ql-onnection class and the >ata'able class. 'he !y"ql>ata,dapter can query a database, then load the resulting information into a >ata'able. It can later update the database with changes that have occurred in the >ata'able. 'he !y"ql>ata,dapter is what enables us to bind data to a control on our form so that the control is automatically filled with our data. The DataTa le Class 'he >ata'able class is used to hold the contents of a single query. It is filled by the !y"ql>ata,dapter and can then be modified or bound to a control on your form. Data -inding vs& #anual Data Loading V .&E' provides data binding as a way to easily populate components on a form by linking the components directly to the data. With data binding we can quickly and easily populate a form of controls, and any changes we make to the data in the components can easily be applied back to the database. While data binding allows for fast development, I often find that there is a greater degree of control available when I read the data and assign it to a form+s controls manually. 'hat being said, I am first going to demonstrate data binding with the >ata?ridView class, and I will demonstrate manual data loading in a later article. Adding Re%erences and Import +tatements 'he >ata'able class is not part of the -onnector.&E' library but is part of the "ystem.Q!$ assembly. ecause of this we need to add a new reference to our pro*ect. -hoose the ,dd =eferenceW option of the <ro*ect menu. In the .&E' tab, scroll down and choose the "ystem.Qml.dll component and click the %I button. 'o make it easier to work with our classes, lets add a pair of Imports statements to the top of our form code:

-m@orts #y0ql..6t6.#y0ql$lient -m@orts 0ystem..6t6

,s noted previously, these allow us to reference the !y"ql>ata,dapter without having to use the !y"ql.>ata.!y"ql-lient.!y"ql>ata,dapter syntax. Declaring )ur ) Aects We first need to declare 3and instantiate6 our database ob*ects. 'his will be done within the -lick event of the Re%resh button, so start by double(clicking on the =efresh button to create a sub for the click event, then add this code:
"ri>6te 0AB cm;%eCres9,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;%eCres9.$licD .im conn 's /ew #y0ql$onnection .im my$omm6n; 's /ew #y0ql$omm6n; .im my';6@ter 's /ew #y0ql.6t6';6@ter .im my.6t6 's /ew .6t6(6Ble .im 0O* 's 0trin? &n; 0AB

We create a !y"ql-onnection to connect to the database, a !y"ql-ommand ob*ect to hold our query, a !y"ql>ata,dapter to run our query and load the data into a >ata'able, and a "#$ string to store our query. We instantiate the ob*ects by using the &ew keyword so that we can use them in our code 3consider a class to be a blueprint, and an ob*ect to be the home that is built from the blueprint6. 8or the purpose of this tutorial, consider a class that is uninstantiated to be unusable 3this is not entirely true, but we+ll get to that later6. Entering )ur 2uery %nce we have declared our ob*ects and variables, we can enter the "#$ query we built earlier into the "#$ string we declared:
0O* = HSELECT CONCAT(u.lastname, ', ', u.firstname) AS Name, s.message AS Message, " _ & "DATE_ O!MAT(e.timestam",'#$ #% #& ' #r') AS DateTime, e.status AS Status H , I H8%2# e>ent e *&8( L2-/ st6tAs s 2/ e.mess6?e,i; = s.mess6?e,i;5 Aser A H , I HK7&%&(e.Aser,i; = A.Aser,i;) H , I H'/. e>ent,i; -/( H , I H0&*&$( #'N(e.e>ent,i;) H , I H8%2# e>ent e H , I H1%2+" )3 e.Aser,i;) H , I H2%.&% )3 n6meH

'he underscore 3/6 character tells the compiler that the "#$ N line is being continued on the next line, allowing us to keep the code readable. 'he ampersand 3J6 character is used to *oin the individual string together on each line. In the end "#$ is filled with our query, and we still have neat, readable code 3as opposed to if we had placed the entire query on

a single line, requiring a lot of hori)ontal scrolling6. In general I try to avoid having code wider than L1 characters, making my code easy to read and easy to print out. )pening the Connection efore we can query the database, we need to open our connection to the database by using the !y"ql-onnection.%pen method. efore we can open the connection, we need to specify the connection string 3stored in the !y-onn"tring property as you recall6:
conn.$onnection0trin? = my$onn0trin? (ry conn.2@en() $6tc9 myerror 's #y0ql&Gce@tion #ess6?e)oG.09ow(H&rror $onnectin? to .6t6B6se: H I myerror.#ess6?e) &n; (ry

%nce again, the opening of the connection is not guaranteed to work, so we place it within a '=9 7 -,'-F block. Con%iguring the #y+qlCommand ) Aect 'he next step in the query process is to configure the command ob*ect. 8or a basic, static query all we need to do is assign the query text and tell the command ob*ect which connection ob*ect to use:
my$omm6n;.$onnection = conn my$omm6n;.$omm6n;(eGt = 0O*

%ur command is now ready to be passed to the !y"ql>ata,dapter ob*ect. Using the #y+qlDataAdapter ) Aect %nce we have configured the connection ob*ect, opened the connection, and configured the command ob*ect, we are ready to perform the actual query. 8irst we tell the !y"ql>ata,dapter ob*ect which command ob*ect to use to perform the query:
my';6@ter.0elect$omm6n; = my$omm6n;

'hat is all the !y"ql>ata,dapter ob*ect needs to know to query the database. We perform the query by using the 8ill method of the ob*ect, and we pass the name of our >ata'able ob*ect so the adapter has something to place the query results into:
my';6@ter.8ill(my.6t6)

'he results of our query are now located within the my>ata ob*ect 3of the >ata'able class6. 'his can then be used to bind to the >ata?ridView control on our form.

-inding the Results &ow that our data is locally stored in memory, we can bind the data to our >ata?ridView control so that our users can see the data:
;?>0t6tAs..6t60oArce = my.6t6

'hat it, one line of code and our data is now visible to the user:

9ou might notice that the timestamp column is a little truncated. 'his is caused by each column being the exact same width 3notice the status column has far too much room6. We can fix this by instructing the >ata?ridView to resi)e the columns after loading the data so that each column has the proper width:
;?>0t6tAs.'Ato0iPe%ows#o;e = .6t61ri;Eiew'Ato0iPe%ows#o;e.'ll$ells

'he command is a bit verbose, but the auto complete feature of Visual "tudio means you actually have little typing to do, and you can select the proper option from a drop(down list. 'he results are much nicer:

Error Catching We want to also catch errors in the code that does the query, so we will place the preceding code in a '=9 7 -,'-F block. %f course, we don+t want to try any of this if the attempt to open the connection fails, so we will actually embed this '=9 7 -,'-F block within the previous one. Fere is the entire procedure:
"ri>6te 0AB cm;%eCres9,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;%eCres9.$licD .im conn 's /ew #y0ql$onnection .im my$omm6n; 's /ew #y0ql$omm6n; .im my';6@ter 's /ew #y0ql.6t6';6@ter .im my.6t6 's /ew .6t6(6Ble

.im 0O* 's 0trin? 0O* = HSELECT CONCAT(u.lastname, ', ', u.firstname) AS Name, s.message AS Message, " _ & "DATE_ O!MAT(e.timestam",'#$ #% #& ' #r') AS DateTime, e.status AS Status H , I H8%2# e>ent e *&8( L2-/ st6tAs s 2/ e.mess6?e,i; = s.mess6?e,i;5 Aser A H , I HK7&%&(e.Aser,i; = A.Aser,i;) H , I H'/. e>ent,i; -/( H , I H0&*&$( #'N(e.e>ent,i;) H , I H8%2# e>ent e H , I H1%2+" )3 e.Aser,i;) H , I H2%.&% )3 n6meH conn.$onnection0trin? = my$onn0trin? (ry conn.2@en() (ry my$omm6n;.$onnection = conn my$omm6n;.$omm6n;(eGt = 0O* my';6@ter.0elect$omm6n; = my$omm6n; my';6@ter.8ill(my.6t6) ;?>0t6tAs..6t60oArce = my.6t6 ;?>0t6tAs.'Ato0iPe%ows#o;e = .6t61ri;Eiew'Ato0iPe%ows#o;e.'ll$ells $6tc9 myerror 's #y0ql&Gce@tion #s?)oG(H(9ere w6s 6n error re6;in? Crom t9e ;6t6B6se: H I myerror.#ess6?e) &n; (ry $6tc9 myerror 's #y0ql&Gce@tion #ess6?e)oG.09ow(H&rror connectin? to t9e ;6t6B6se: H I myerror.#ess6?e) 8in6lly -C conn.0t6te Q> $onnection0t6te.$lose; (9en conn.$lose() &n; (ry &n; 0AB

If there is an error connecting, the user will see a message box that reports an error connecting to the database. If there is an error executing the query, the user will see a message box that reports an error reading from the database.

Updating a User>s +tatus


&ow that we can see what the status of our users is, it would be good to add the ability to update our own status. 'o do this we need to show the user a list of possible status messages using a combo box. In addition we should add a second combo box showing the two possible status messages. %ur combo boxes will look something like this:

Creating the +tatus and #essage Com o o9 Controls ,dd the comboboxes to the form by dragging them from the toolbox. In the properties window, change the names of these comboboxes to cbo"tatus and cbo!essage. 9ou form should look something like the following:

%n the left is cbo"tatus, on the right is cbo!essage. We will populate these two comboboxes in the form load event, which can be created by double(clicking on the form background. We populate the cbo"tatus combobox with the following code:
cBo0t6tAs.-tems.';;(H-nH) cBo0t6tAs.-tems.';;(H2AtH) cBo0t6tAs.0electe;-n;eG = 0

'his adds two items 3In and )ut6 and sets the combobox to pre(select the In item 3the list starts at 1 and counts up, so In is 1 and )ut is 26. We will populate the cbo!essage box using data binding. 'he principal is the same as we covered for the data grid in the previous section, so I am *ust going to display the code:
"ri>6te 0AB Crm#6in,*o6;()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les #y)6se.*o6; cBo0t6tAs.-tems.';;(H-nH) cBo0t6tAs.-tems.';;(H2AtH) cBo0t6tAs.0electe;-n;eG = 0 ;?>0t6tAs.%e6;2nly = (rAe .im .im .im .im .im conn 's /ew #y0ql$onnection my$omm6n; 's /ew #y0ql$omm6n; my';6@ter 's /ew #y0ql.6t6';6@ter my.6t6 's /ew .6t6(6Ble 0O* 's 0trin?

0O* = H0&*&$( s.mess6?e,i;5 s.mess6?e H , I H8%2# in,oAt.st6tAs s H , I HK7&%& Aser,i; -0 /+** 6n; ;elete; = <86lse<H conn.$onnection0trin? = my$onn0trin? (ry conn.2@en() (ry my$omm6n;.$onnection = conn my$omm6n;.$omm6n;(eGt = 0O* my';6@ter.0elect$omm6n; = my$omm6n; my';6@ter.8ill(my.6t6) cBo#ess6?e..6t60oArce = my.6t6 cBo#ess6?e..is@l6y#emBer = Hmess6?eH cBo#ess6?e.E6lAe#emBer = Hmess6?e,i;H $6tc9 myerror 's #y0ql&Gce@tion #s?)oG(H(9ere w6s 6n error re6;in? Crom t9e ;6t6B6se: H I myerror.#ess6?e) &n; (ry $6tc9 myerror 's #y0ql&Gce@tion

#ess6?e)oG.09ow(H&rror connectin? to t9e ;6t6B6se: H I myerror.#ess6?e) 8in6lly -C conn.0t6te Q> $onnection0t6te.$lose; (9en conn.$lose() &n; (ry &n; 0AB

'he primary difference is the following two lines:


cBo#ess6?e..is@l6y#emBer = Hmess6?eH cBo#ess6?e.E6lAe#emBer = Hmess6?e,i;H

,fter setting the source for data binding, we first specify that the message column of our query will be shown in the combobox, and that we can identify which item was selected through the message/id column, as I will later demonstrate. 'his does present one problem: in its current form, the combobox will not allow us to set a &;$$ status, because the combobox is only populated with entries from the status table 3and only those messages that have no userEid and which are not marked as deleted6. 8or the moment we will ignore this limitation and will address it in a future article. 'he dgv"tatus.=ead%nly N 'rue line is added to prevent users from trying to edit the contents of the datagrid. With this code added, our comboboxes will be pre(populated and ready for us to use to update the status. Retrieving the User ID ,ith A !arameteri*ed 2uery 'o update a user+s status we need to create a new row in the event table by using an I&"E=' query. In order to build the query we need to know the userEid value associated with our user. We can do this by querying the user table, and since we may use this multiple times in a single application run, lets perform the query as part of the login form. Fere is our updated login form:
"ABlic $l6ss Crm*o?in "ri>6te 0AB cm;$6ncel,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;$6ncel.$licD '@@lic6tion.&Git() &n; 0AB "ri>6te 0AB cm;*o?in,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;*o?in.$licD .im conn 's /ew #y0ql$onnection .im my$omm6n; 's /ew #y0ql$omm6n; .im my$onn0trin? 's 0trin? .im +ser-. 's -nte?er my$onn0trin? = Hser>er=H I tGt0er>er.(eGt I H;H ,

I HAser i;=H I tGt+sern6me.(eGt I H;H , I H@6sswor;=H I tGt"6sswor;.(eGt I H;H , I H;6t6B6se=in,oAtH conn.$onnection0trin? = my$onn0trin? (ry conn.2@en()

my$omm6n;.$onnection = conn my$omm6n;.$omm6n;(eGt = H0&*&$( Aser,i; 8%2# Aser K7&%& )-/'%3 Asern6me = ?+sern6meH my$omm6n;."6r6meters.';;(H?+sern6meH5 tGt+sern6me.(eGt) +ser-. = my$omm6n;.&GecAte0c6l6r conn.$lose() .im m6in8orm 's /ew Crm#6in m6in8orm.+ser-. = +ser-. m6in8orm.connection0trin? = my$onn0trin? m6in8orm.09ow() #e.7i;e() #e.$lose() $6tc9 myerror 's #y0ql&Gce@tion #ess6?e)oG.09ow(H&rror $onnectin? to .6t6B6se: H I myerror.#ess6?e) conn..is@ose() &n; (ry &n; 0AB &n; $l6ss

%f interest are the following lines:


my$omm6n;.$onnection = conn my$omm6n;.$omm6n;(eGt = H0&*&$( Aser,i; 8%2# Aser K7&%& )-/'%3 Asern6me = ?+sern6meH my$omm6n;."6r6meters.';;(H?+sern6meH5 tGt+sern6me.(eGt) +ser-. = my$omm6n;.&GecAte0c6l6r

I added a command ob*ect to the code, set the conn ob*ect as it+s connection, and specified a query in the -ommand'ext. 'his query is the first query in our application that incorporates user input, and as such it requires extra care. When we deal with data entered by the user we cannot blindly trust the user to provide information that is safe and syntactically correct, otherwise the user could compromise security through a method known as "#$ In*ection 3see my article at http:..www.vbmysql.com.articles.security.sqlin*ection.html for more information6. We cannot trust user input and we need to saniti)e all user input before it reaches the !y"#$ server.

-onnector.&E' handles saniti)ing of user input for us through the use of parameteri)ed queries. &otice that within the query above the username is compared to a external value with the syntax WFE=E -I'AR$ username K LUsername. 'he LUsername element is a placeholder that we fill with a parameter that is derived from the txt;sername. When the query is executed the !y"ql-ommand ob*ect reads the text of the username textbox and fills in the query, saniti)ing it in the process. 'he query is executed using the Execute"calar method of the !y"ql-ommand ob*ect, which can be used to execute queries that will return only a single value. In this case we use it for userEid, it could also be used to return a query that counts rows in a table, or any other query that returns a single value. 'he user id is passed to the frm!ain by way of a parameter, *ust like the connection string. I have added a parameter to the frm!ain to accommodate this:
"ABlic $l6ss Crm#6in "ri>6te my$onn0trin? 's 0trin? "ri>6te my+ser-. 's -nte?er "ABlic Krite2nly "ro@erty connection0trin?() 's 0trin? 0et()yE6l >6lAe 's 0trin?) my$onn0trin? = >6lAe &n; 0et &n; "ro@erty "ABlic Krite2nly "ro@erty +ser-.() 's -nte?er 0et()yE6l >6lAe 's -nte?er) my+ser-. = >6lAe &n; 0et &n; "ro@erty

'he new property is the same as the connection string property, except the user id is stored as an integer instead of a string. Inserting a 'e3 Ro3 in the Event Ta le &ow that we have the user id of the logged in user, and the status and message information from our pair of combobox controls, we can insert a new row that will update the status of our user. Fere is an example of the I&"E=' statement we want to generate:
-/0&%( -/(2 e>ent (Aser,i;5 mess6?e,i;5 timest6m@5 st6tAs5 cre6tor) E'*+&0(15 15 /2K()5 <2At<5 1)

We do not specify an eventEid because, as an ,;'% I&-=E!E&' column, it will be filled in automatically. 'he userEid is 2 in this case, along with the creator because we are setting our own event. 'he timestamp is assigned to &%W36, which represents the current server time. 8inally, status is set to @%ut+ to indicate the user is signing out.

%f course, we cannot simply place this query into our application, otherwise everyone who ever hit the ;pdate "tatus button would simply sign out user 2. Instead we will once again build a collection of parameters, bound to the controls on our form:
"ri>6te 0AB cm;+@;6te,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;+@;6te.$licD .im conn 's /ew #y0ql$onnection .im my$omm6n; 's /ew #y0ql$omm6n; conn.$onnection0trin? = my$onn0trin? my$omm6n;.$onnection = conn my$omm6n;.$omm6n;(eGt = H-/0&%( -/(2 e>ent(Aser,i;5 mess6?e,i;5 timest6m@5 st6tAs5 cre6tor)H , I HE'*+&0(?+ser-.5 ?#ess6?e-.5 /2K()5 ?0t6tAs5 ? $re6tor)H my$omm6n;."6r6meters.';;(H?+ser-.H5 my+ser-.) my$omm6n;."6r6meters.';;(H?#ess6?e-.H5 cBo#ess6?e.0electe;E6lAe) my$omm6n;."6r6meters.';;(H?0t6tAsH5 cBo0t6tAs.0electe;-tem) my$omm6n;."6r6meters.';;(H?$re6torH5 my+ser-.) (ry conn.2@en() my$omm6n;.&GecAte/onOAery() $6tc9 myerror 's #y0ql&Gce@tion #s?)oG(H(9ere w6s 6n error A@;6tin? t9e ;6t6B6se: H I myerror.#ess6?e) &n; (ry &n; 0AB

=emember when we set the Value!ember property of cbo!essage to bind to the messageEid of the status tableA 'his allowed us to access the messageEid through the "electedValue property in order to use it as a parameter in our I&"E=' statement. cbo"tatus is not a bound control, so we use the "electedItem property instead when binding it. &ote that we can also variables as parameters 3my;serI>6, and even use a variable more than once in a parameter set.

Testing the Application


&ow that all code is in place, you can try a quick test. "tart the application, enter your server I<, username, and password. 'ry entering the wrong I<, username, or password and make sure you see an error message 3without your application crashing6. %nce you are connected, click the =efresh button and make sure you can see the list of users. -hoose a status and message and click the ;pdate "tatus button, then click the =efresh button to see your status update.

#inor UI T3ea;s
,fter testing the application you may have noticed a couple of improvements that we can make to the ;I. 8irst, when we update your status, a refresh should be fired automatically so we instantly see the new status. 'his can be accomplished by adding the following line to the bottom of your update code:
cm;%eCres9."erCorm$licD()

'his will fire the click event of the =efresh button, refreshing the data after the status is updated. ,nother improvement that could be made is with regards to keyboard shortcuts on the login form. ;ser expect that the Escape key will cancel out of a form, while the enter key will submit form information. 'his requires very little coding:
"ri>6te 0AB Crm*o?in,*o6;()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les #y)6se.*o6; #e.'cce@t)Atton = cm;*o?in #e.$6ncel)Atton = cm;$6ncel &n; 0AB

y setting the ,ccept utton and -ancel utton properties of the form, V .&E' will now watch for Enter and Escape keystrokes and will fire the button events accordingly.

Application Improvements
'here are a few things that need improving in this application. 8irst of all, we still need a less tedious login that stores the database I< address and which remembers the last username entered. %ur datagrid is not particularly attractive and could use cosmetic improvements, and we need to add support for having a blank status message or a custom status message. 8inally, it would be nice is the data refreshed automatically rather than requiring our users to repeatedly click the =efresh button. 'hese will all be addressed in future articles.

Conclusion
%ur application is really moving along. We can now see a list of the users and their current status, and update our own status to one of the pre(defined status messages. We have populated some sample data to start off with, and added a new event through our application. In our next article, we will improve the application by adding automatic data refreshing and persistent server information so that our users need not enter their server I< and username information every time they use the application.

Introduction
'his article is fifth in a series of articles describing how to create a simple Windows application that queries and updates a !y"#$ database. "o far we have designed our application and our database, installed !y"#$ and V .&E', created our database, and created a basic application. 'his application is capable of querying event data from the database and updating it. ,t the end of the fourth article of this series, I listed some improvements that could be made to our application. 'hese included automatic refreshing of the data and storage of the !y"#$ server I< address in a configuration file.

Automating Data Re%resh


%ne of the drawbacks of our current application is that it requires our user to manually click the Re%resh button every time they want to see the latest information. 'his means that a change in status can go unnoticed if the user does not click the refresh button. In addition, we cannot allow for one user to set a watch for another user to login if the first user has to regularly click the =efresh button. Well will be automating the refreshing of our status information through the use of a V .&E' Timer control. , timer fires at a configurable interval and executes a block of code. 'imers are very useful in application programming and can be used for a variety of tasks. A stracting the Re%resh Code efore we create a 'imer, we should make our refresh code more generic. -urrently the code for refreshing the status data is located directly within the event handler for a click of the =efresh button. We can move this code to a function, whttp:..www.vbmysql.com.wp(content.uploads.vb(mysql(tutorial(part(0.)ip vb(mysql(tutorial(part(0.)iphere it can then be called from the click event and the timer. 'he new function will contain all the code that was previously in the =efresh button click event, with one small change:
"ri>6te 0AB .im .im .im .im .im reCres90t6tAs()y%eC st6tAsEiew 's .6t61ri;Eiew) conn 's /ew #y0ql$onnection my$omm6n; 's /ew #y0ql$omm6n; my';6@ter 's /ew #y0ql.6t6';6@ter my.6t6 's /ew .6t6(6Ble 0O* 's 0trin?

0O* = H0&*&$( $2/$'((A.l6stn6me5 <5 <5 A.Cirstn6me) '0 /6me5 s.mess6?e '0 #ess6?e5 H , I H.'(&,82%#'((e.timest6m@5<MB M; M3 - Mr<) '0 .6te(ime5 e.st6tAs '0 0t6tAs H ,

I H8%2# e>ent e *&8( L2-/ st6tAs s 2/ e.mess6?e,i; = s.mess6?e,i;5 Aser A H , I HK7&%&(e.Aser,i; = A.Aser,i;) H , I H'/. e>ent,i; -/( H , I H0&*&$( #'N(e.e>ent,i;) H , I H8%2# e>ent e H , I H1%2+" )3 e.Aser,i;) H , I H2%.&% )3 n6meH conn.$onnection0trin? = my$onn0trin? (ry conn.2@en() (ry my$omm6n;.$onnection = conn my$omm6n;.$omm6n;(eGt = 0O* my';6@ter.0elect$omm6n; = my$omm6n; my';6@ter.8ill(my.6t6) st6tAsEiew..6t60oArce = my.6t6 ;?>0t6tAs.'Ato0iPe%ows#o;e = .6t61ri;Eiew'Ato0iPe%ows#o;e.'ll$ells $6tc9 myerror 's #y0ql&Gce@tion #s?)oG(H(9ere w6s 6n error re6;in? Crom t9e ;6t6B6se: H I myerror.#ess6?e) &n; (ry $6tc9 myerror 's #y0ql&Gce@tion #ess6?e)oG.09ow(H&rror connectin? to t9e ;6t6B6se: H I myerror.#ess6?e) 8in6lly -C conn.0t6te Q> $onnection0t6te.$lose; (9en conn.$lose() &n; (ry &n; 0AB

In the declaration of the function we pass an argument named status1ie3 that will point to the >ata?ridView on our form. In the code, we change the code that binds the >ata?ridView to bind against the statusView. 'his way we can change the name of the >ata?ridView without changing the code within the function. %ur click event is also changed to call this function:
"ri>6te 0AB cm;%eCres9,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;%eCres9.$licD reCres90t6tAs(;?>0t6tAs) &n; 0AB

-licking the Re%resh button now calls the re%resh+tatus function. In addition, we can change the following line of our update code:
cm;%eCres9."erCorm$licD()

to
reCres90t6tAs(;?>0t6tAs)

'his is a much better approach when the same code is executed in multiple places. In addition, add the call to re%resh+tatus to your %rm#ain/load event so that the user is presented with a view of the status from the beginning. Adding the Timer &ow that our refresh code is moved, we can call it from a timer. 'he 'imer control is located in the toolbox:

-lick and drag the timer control onto your form. ;nlike other controls, the 'imer control does not stay on your form, but is automatically moved to a special area below the form:

'his area is reserved for controls that are not visible, such as the timer and common dialogs. -hange the name of the timer to tmr=efresh using the properties window and set the Ena led property to True. Choosing an Interval 'he Interval property of the timer determines how often the timer will trigger an event. 'he Interval is measured in milliseconds, with 2111 milliseconds to a second. We do not want to fire this event too often, or we produce excessive load on our server. We also do not want to wait too long before firing our event, or our users may not see updated status information fast enough. Ideally we want to have an interval of about one minute, or B1,111 milliseconds. "et the Interval property of our timer to B1111. Catching the Tic; Event When the Interval set previously is reached, the timer triggers the Tic; event. We can catch the 'ick event and add code to it, allowing that code to be run every time the interval is reached, or once every minute. >ouble(click on the timer and the following code template is generated:
"ri>6te 0AB tmr%eCres9,(icD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les tmr%eCres9.(icD &n; 0AB

We can add a call to our refresh"tatus function within this function to cause the status information to refresh automatically:
"ri>6te 0AB tmr%eCres9,(icD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les tmr%eCres9.(icD reCres90t6tAs(;?>0t6tAs) &n; 0AB

'his will cause our status information to be refreshed once per minute, regardless of whether the user clicks the Re%resh button or not. 9ou can test this by launching the application, then adding a row to the event table using the !y"#$ #uery rowser. Within one minute the status display will change.

+toring Application +ettings


&ow that we have added a timer to frm!ain, let+s make some improvements to frm$ogin. -urrently, our users need to manually enter the !y"#$ server I< address every time they use our application. In addition, there is no option to remember a username for future use. 'o solve both of these problems we need a way to persist information for future use. 'here are a variety of options for persisting data, including I&I files, the registry, and Q!$ files. !ost of the options mentioned above require either the use of ,<I calls or file handling code to set and retrieve data. With V .&E' G110 a new !y."ettings syntax has been added that greatly simplifies the storage and retrieval of application settings. The ne3 #y&+ettings +ynta9 With #y&+ettings, we can application and user(level settings to our application and easily access them without using any ,<I or file handling code. "ettings are stored in Q!$ files located either with the application executable or in the Do*uments an, Settin2s?Username directory, depending on the scope of the setting. "etting can have either a User or Application scope, depending on whether an setting applies to the application in general or to a specific user. 'o use #y&+ettings, we first create the setting within the V .&E' I>E, then access the setting through code. +toring the 0ost I! 3ith an Application +etting 'he first step in using an application(level setting is to create is using the setting designer. -hoose <roperties from the <ro*ect menu and choose the "ettings tab. , grid with available settings is displayed:

Within this grid we create a new 0ostI! setting as a "tring, with an Application scope. "et the value to the I< address of your !y"#$ server machine. %nce your setting is in place you can close the <ro*ect <roperties window. ,fter closing the window you will be prompted to save the application settings file, click 9es and you will be returned to the form designer. 'he following is added to the ,pp.-onfig file:
Q6@@lic6tion0ettin?s> Q-n,2At.#y0ettin?s> Qsettin? n6me=H7ost-"H seri6liPe's=H0trin?H> Q>6lAe>192.1:!.1.10Q >6lAe> Q settin?>

Q -n,2At.#y0ettin?s> Q 6@@lic6tion0ettin?s>

'his file will be compiled into a file named in(out.exe.config file when you build your pro*ect, and will be located in the bin folder of your pro*ect. Accessing the 0ost I! In%ormation in %rmLogin ,fter the FostI< setting is stored we can access it from within our code. 8irst change the form design by deleting the controls related to the server I< address:

%nce the form is redesigned, we can modify the login code, specifically the connection string::
0O* = Hser>er=H I I I I #y.0ettin?s.7ost-" I H;H , HAser i;=H I tGt+sern6me.(eGt I H;H , H@6sswor;=H I tGt"6sswor;.(eGt I H;H , H;6t6B6se=in,oAtH

%ur login code will now use the stored I< address when connecting to the server. +toring the User 'ame 3ith a User +etting &ow that our host I< is taken care of, let+s look at optionally storing the username to save time for our users. We can store the username in a User scope setting, which will allow it to be read and written to a file in the Do*uments an, Settin2s directory 3Application scope settings are rea,;on)% though the #y&+ettings interface6. %ne advantage of this approach is that even when our application is used with multiple Windows user accounts, each user can have their username stored without overwriting the previous user+s information. We create a ;ser scope setting with the same interface as we used to create a ,pplication scope setting. When creating the User'ame setting, create it as a string, set a ;ser scope, and leave the Value blank. 'he "ettings section of the ,pp.-onfig file will look like this:

Q6@@lic6tion0ettin?s> Q-n,2At.#y0ettin?s> Qsettin? n6me=H7ost-"H seri6liPe's=H0trin?H> Q>6lAe>192.1:!.1.10Q >6lAe> Q settin?> Q -n,2At.#y0ettin?s> Q 6@@lic6tion0ettin?s> QAser0ettin?s> Q-n,2At.#y0ettin?s> Qsettin? n6me=H+sern6meH seri6liPe's=H0trin?H> Q>6lAe > Q settin?> Q -n,2At.#y0ettin?s> Q Aser0ettin?s>

Accessing the User 'ame in %rmLogin ,s an added convenience to our user, let+s allow the username to be optionally stored by means of a checkbox. 'his will save time when the user logs in. 8irst lets redesign our login form and add a -heck ox control:

&ame the -heck ox ch;Remem er and leave it unchecked by default 3set the text property @=emember !e+6. 8irst let+s add code to the formEload event to check if we have a saved username:
-C #y.0ettin?s.+sern6me Q> HH (9en tGt+sern6me.(eGt = #y.0ettin?s.+sern6me c9D%ememBer.$9ecDe; = (rAe &n; -C

'his code checks for a blank username. If the ;sername setting is not blank, it fills the t9tUsername 'ext property and checks ch;Remem er. &ext we edit the login code for the form. We only save the username if chk=emember is checked and the username is successfully used to login. If chk=emember is not checked, we blank out the ;sername setting:
"ri>6te 0AB cm;*o?in,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;*o?in.$licD .im conn 's /ew #y0ql$onnection .im my$omm6n; 's /ew #y0ql$omm6n; .im my$onn0trin? 's 0trin? .im +ser-. 's -nte?er my$onn0trin? = Hser>er=H I #y.0ettin?s.7ost-" I H;H , I HAser i;=H I tGt+sern6me.(eGt I H;H , I H@6sswor;=H I tGt"6sswor;.(eGt I H;H ,

I H;6t6B6se=in,oAtH conn.$onnection0trin? = my$onn0trin? (ry conn.2@en()

my$omm6n;.$onnection = conn my$omm6n;.$omm6n;(eGt = H0&*&$( Aser,i; 8%2# Aser K7&%& )-/'%3 Asern6me = ?+sern6meH my$omm6n;."6r6meters.';;(H?+sern6meH5 tGt+sern6me.(eGt) +ser-. = my$omm6n;.&GecAte0c6l6r conn.$lose() (f )*+!emem$er.C*e)+e% T*en M,.Settings.-sername . t/t-sername.Te/t M,.Settings.Sa0e() Else M,.Settings.-sername . "" En% (f .im m6in8orm 's /ew Crm#6in m6in8orm.+ser-. = +ser-. m6in8orm.connection0trin? = my$onn0trin? m6in8orm.09ow() #e.7i;e() #e.$lose() $6tc9 myerror 's #y0ql&Gce@tion #ess6?e)oG.09ow(H&rror $onnectin? to .6t6B6se: H I myerror.#ess6?e) conn..is@ose() &n; (ry &n; 0AB

%ur frm$ogin class should now look like this:


"ABlic $l6ss Crm*o?in "ri>6te 0AB cm;$6ncel,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;$6ncel.$licD '@@lic6tion.&Git() &n; 0AB "ri>6te 0AB cm;*o?in,$licD()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les cm;*o?in.$licD .im conn 's /ew #y0ql$onnection .im my$omm6n; 's /ew #y0ql$omm6n; .im my$onn0trin? 's 0trin? .im +ser-. 's -nte?er my$onn0trin? = Hser>er=H I #y.0ettin?s.7ost-" I H;H , I HAser i;=H I tGt+sern6me.(eGt I H;H , I H@6sswor;=H I tGt"6sswor;.(eGt I H;H ,

I H;6t6B6se=in,oAtH conn.$onnection0trin? = my$onn0trin? (ry conn.2@en()

my$omm6n;.$onnection = conn my$omm6n;.$omm6n;(eGt = H0&*&$( Aser,i; 8%2# Aser K7&%& )-/'%3 Asern6me = ?+sern6meH my$omm6n;."6r6meters.';;(H?+sern6meH5 tGt+sern6me.(eGt) +ser-. = my$omm6n;.&GecAte0c6l6r conn.$lose() -C c9D%ememBer.$9ecDe; (9en #y.0ettin?s.+sern6me = tGt+sern6me.(eGt #y.0ettin?s.06>e() &lse #y.0ettin?s.+sern6me = HH &n; -C .im m6in8orm 's /ew Crm#6in m6in8orm.+ser-. = +ser-. m6in8orm.connection0trin? = my$onn0trin? m6in8orm.09ow() #e.7i;e() #e.$lose() $6tc9 myerror 's #y0ql&Gce@tion #ess6?e)oG.09ow(H&rror $onnectin? to .6t6B6se: H I myerror.#ess6?e) conn..is@ose() &n; (ry &n; 0AB "ri>6te 0AB Crm*o?in,*o6;()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les #y)6se.*o6; #e.'cce@t)Atton = cm;*o?in #e.$6ncel)Atton = cm;$6ncel -C #y.0ettin?s.+sern6me Q> HH (9en tGt+sern6me.(eGt = #y.0ettin?s.+sern6me c9D%ememBer.$9ecDe; = (rAe &n; -C

&n; 0AB &n; $l6ss

9ou can test our setting code by running the application, checking the =emember !e box, and logging in. -lose the application and restart it, your username should be present.

0iding the !ass3ord

%ne last tweak we can make before leaving frm$ogin has to do with txt<assword. ,t present, any password entered is in plain sight, and could be read by an observer of the user. 'o hide the text of a textbox, add the following line to the formEload event:
"ri>6te 0AB Crm*o?in,*o6;()yE6l sen;er 's 0ystem.2BFect5 )yE6l e 's 0ystem.&>ent'r?s) 76n;les #y)6se.*o6; #e.'cce@t)Atton = cm;*o?in #e.$6ncel)Atton = cm;$6ncel -C #y.0ettin?s.+sern6me Q> HH (9en tGt+sern6me.(eGt = #y.0ettin?s.+sern6me c9D%ememBer.$9ecDe; = (rAe &n; -C &n; 0AB t/t1ass23r%.1ass23r%C*ar . "4"

,ll text entered is replaced with E, preventing casual observers from seeing sensitive information.

Conclusion
In this article we have tweaked our application to improve its ease of use. "pecifically, we have added a 'imer control to frm!ain to allow for automatic refreshing of event data, and made use of the !y."ettings system to store I< and username information. ;sername information is stored in a file that is unique to each Windows user account, allowing multiple users to store username information without overwriting each other. In our next article we will improve the event display and generation code to allow for the creation of custom status messages and to support blank status messages. We will also explore the creation of custom classes and data binding to an ob*ect. ,n archive of the code so far can be found at http:..www.vbmysql.com.wp( content.uploads.vb(mysql(tutorial(part(0.)ip.

Potrebbero piacerti anche