tài liệu using coldfusion mx with databases tài liệu, giáo án, bài giảng , luận văn, luận án, đồ án, bài tập lớn về tất...
208 Part II ✦ Using ColdFusion MX with Databases Listing 10-2 (continued) RAISERROR 50001 ‘The OrderItem could not be inserted.’ ROLLBACK TRANSACTION RETURN END UPDATE InventoryItem SET AvailableToSell = AvailableToSell - 10 WHERE ItemNumber = ‘CAS30-BLK’ IF @@ERROR != 0 BEGIN RAISERROR 50002 ‘The InventoryItem could not be updated.’ ROLLBACK TRANSACTION RETURN END COMMIT TRANSACTION The syntax is a little different, but the principles are very similar, aren’t they? It’s really just a matter of learning both methods for implementing transactions and then controlling them as close to the database server as your application enables you to do so. A good example of when you need to control a transaction within ColdFusion is whenever you are passing multiple rows of data from ColdFusion to the database server, and you want to encapsulate all those queries into a single transaction. Listing 10-3 illustrates this example. Listing 10-3: Inserting multiple rows within a single transaction <cftransaction> <cfloop index=”i” from=”1” to=”#ArrayLen(arItemNumber)#”> <cfquery name=”InsertOrderItems” datasource=”CFMXBible”> INSERT INTO OrderItem ( SalesOrderID, ItemNumber, Description, UnitPrice, Quantity ) VALUES ( #Val(arSalesOrderID[i])#, ‘#Trim(arItemNumber[i])#’, ‘#Trim(arDescription[i])#’, #Val(arUnitPrice[i])#, #Val(arQuantity[i])# 13546228 ch10.F 1/30/03 10:50 AM Page 208 209 Chapter 10 ✦ Using Advanced Database Techniques ) </cfquery> </cfloop> </cftransaction> You currently have no easy and effective way to directly send multidimensional data from ColdFusion to a database server in a single statement. Until such a method exists, you must loop over ColdFusion arrays or structures and call CFQUERY once for each unit of data to be stored in the database. CFTRANSACTION and exception handling The default behavior of the CFTRANSACTION tag is such that if you do not explicitly command it to commit or rollback, it does so implicitly for you. This is the technique shown in Listings 10-1 and 10-3. Many ColdFusion developers are used to coding that way, but if you rely on the implicit behavior that CFTRANSACTION automatically commits and rolls back for you, stop doing so right now. In our own tests, ColdFusion MX slows to a crawl if CFTRANSACTION tags are not explicitly coded with BEGIN, COMMIT, and ROLLBACK commands. To make sure that you are committing only if everything works correctly and rolling back only if it doesn’t, you should be very aware of CFTRANSACTION’s behavior with respect to exception handling and also how to correctly nest CFTRANSACTION and CFTRY tags. The best practice for coding CFTRANSACTION is as follows: 1. CFSET a flag variable TRUE. 2. Begin a CFTRANSACTION. 3. Open a CFTRY block. 4. Code any database queries you need. 5. Test for exceptions with CFCATCH blocks as necessary. 6. Within any and all CFCATCH blocks that would indicate a failure of any part of the trans- action, CFSET the flag FALSE. 7. Close the CFTRY block. 8. Test the flag: Commit the transaction if TRUE and roll it back if FALSE. 9. Close the CFTRANSACTION. Listing 10-4 rewrites Listing 10-1 to incorporate CFTRANSACTION best practices. Listing 10-4: Combining CFTRANSACTION with CFTRY and CFCATCH <cfset OKtoCommit = TRUE> <cftransaction action=”BEGIN”> <cftry> <cfquery name=”InsertOrderItem” datasource=”CFMXBible”> INSERT INTO OrderItem ( Continued 13546228 ch10.F 1/30/03 10:50 AM Page 209 210 Part II ✦ Using ColdFusion MX with Databases Listing 10-4 (continued) SalesOrderID, ItemNumber, Description, UnitPrice, Quantity ) VALUES ( 1, ‘CAS30-BLK’, ‘30-Minute Cassette, Black Case’, 1.05, 10 ) </cfquery> <! If an error occurs after the first query, control immediately falls to CFCATCH > <cfquery name=”UpdateInventory” datasource=”CFMXBible”> UPDATE InventoryItem SET AvailableToSell = AvailableToSell - 10 WHERE ItemNumber = ‘CAS30-BLK’ </cfquery> <cfcatch type=”Any”> <cfset OKtoCommit = FALSE> </cfcatch> </cftry> <cfif OKtoCommit> <cftransaction action=”COMMIT”/> <cfelse> <cftransaction action=”ROLLBACK”/> </cfif> </cftransaction> As soon as any one of the queries throws an exception, program flow falls immediately to the applicable CFCATCH block, which then takes control and sets the OKtoCommit flag to FALSE. This circumvents any attempts to execute any other queries in the transaction. After the CFTRY block, the flag is tested and the entire transaction is either committed to disk or rolled back as if nothing ever happened. If you’re upgrading from an earlier version of ColdFusion Server, go right now and run the Find command on all your code for CFTRANSACTION, inspect your code, and determine whether you need to shore up your CFTRANSACTION tags. 13546228 ch10.F 1/30/03 10:50 AM Page 210 211 Chapter 10 ✦ Using Advanced Database Techniques Transaction isolation Transaction isolation is one of the most misunderstood concepts of transactions, possibly because it forces you to think in terms of multiple users executing various multitable transac- tions over time, and that isn’t easy. If you don’t quite get it at first, that’s okay — don’t be so hard on yourself. Transaction isolation is the degree to which the effects of one transaction are isolated from those of other transactions that are also trying to execute at the same time. Isolating the effects of one transaction from those of another is controlled through the database server’s data-locking mechanisms, and these are, in turn, controlled through the SET TRANSACTION ISOLATION LEVEL command. Transaction isolation is a balancing act between concurrency and consistency. In other words, “how many people can bang on it at once” versus “how accurate is the data at any given point in time.” At one extreme, you can have everyone accessing data simultaneously (high concurrency) but at the cost of them working with data that may still be undergoing change as they access it (low consistency). At the other extreme, you can have each transaction absolutely separated from every other transaction such that everything appears to have been performed on a single-user machine (high consistency) but at the cost of considerably slowing down multi-user access (low concurrency). The key to a high performance database is executing each transaction with the minimum amount of locking possible to enable sufficiently consistent data. Doing so enables your database to operate with the highest possibly concurrency while affording it the data consistency that it requires. All in all, you have the following four levels of transaction isolation: ✦ Read Uncommitted ✦ Read Committed ✦ Repeatable Read ✦ Serializable Various database servers have various degrees of support for transaction isolation: Some support all four levels, some support none, and most serious database products support only two or three, so carefully read the documentation for both your database product and its driver before you attempt to set a transaction’s isolation level. The following sections describe what each of these levels mean. Read Uncommitted Read Uncommitted is the least-used level of isolation, because it is the least safe of all. Remember that all data operations (selects, inserts, updates, deletes) take place in the database server’s RAM and not on its disk. After data is requested, the database engine first looks for it in RAM — the data is possibly already there from another user’s request — and if the data isn’t there, it retrieves the necessary data from disk and loads it into RAM for use. If the data is being modified, after the database server sees that its operations in RAM are successful, it becomes “committed” to writing such modifications to disk, where they become permanent changes. Between the time that data is being modified in RAM and the time that the database server is committed to writing such modifications to disk, the data is said to be in an uncommitted state. If you set the isolation level to Read Uncommitted and then perform a query of data 13546228 ch10.F 1/30/03 10:50 AM Page 211 212 Part II ✦ Using ColdFusion MX with Databases that is in the process of being updated by another user, the result set contains data exactly as it appears with the other user’s modifications at that moment — even if those modifications have not yet been written to disk. If the original transaction rolls back, its modifications never really existed, and therefore the second transaction’s read of its uncommitted data was “dirty.” This is what the term dirty read means. Read Uncommitted can perform dirty reads because it has nothing at all to do with locks of any kind. At this level of isolation, locks are neither set by a transaction nor are they honored if set by another transaction. If you query the database to give you a set of data, it returns to you whatever values are in that data set at the very moment that it is requested, regardless of anything else that’s going on at the time. Read Uncommitted has the highest concurrency and the lowest consistency. It is set as follows: SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED go BEGIN TRANSACTION . . . Oracle doesn’t support the Read Uncommitted isolation level. Read Committed If you take one step up in the consistency department, you take one corresponding step down in the concurrency department, and you end up at the Read Committed isolation level. The difference between Read Uncommitted and Read Committed is that Read Committed sees only data that has been committed to being written out to disk — in other words, locks placed on the data modified by the first user’s transaction are honored by the second user’s transaction, so data read by the second transaction is “clean” rather than “dirty.” Although Read Committed doesn’t permit dirty reads, it does permit what are called nonrepeatable reads, which can also become a problem. If you have a transaction that reads a set of data, performs some operation, and then reads the same set of data, for example, another user’s transaction could have modified your set of data in between the two times that it was read, thereby causing the second read to appear different from the first. In other words, the read was nonrepeatable. Read Committed is the default level of isolation for most database products and offers a good combination of concurrency and consistency for most applications. It is set as follows: SET TRANSACTION ISOLATION LEVEL READ COMMITTED go BEGIN TRANSACTION . . . Repeatable Read If your isolation level is set to Repeatable Read, no one else’s transactions can affect the consistency between one read of a data set at the beginning of the transaction and another read toward its end. By data set, we are talking about exactly the same collection of rows — not the results of a repeat of the SELECT statement that produced those rows. You see, a repeat of the SELECT statement may return rows that satisfy the WHERE clause that were inserted mid-transaction by another user. Such newly inserted rows are called phantom rows and are permitted under the Repeatable Read isolation level, because Repeatable Read preserves only the exact collection of rows originally read at the beginning of the transaction. Note 13546228 ch10.F 1/30/03 10:50 AM Page 212 213 Chapter 10 ✦ Using Advanced Database Techniques Repeatable Read involves more locks on data across transactions, so although it increases data consistency, concurrency takes a performance hit. It is set as follows: SET TRANSACTION ISOLATION LEVEL REPEATABLE READ go BEGIN TRANSACTION . . . Oracle doesn’t support the Repeatable Read isolation level. Serializable Now you come to the most isolated level of all. Serializable means that all transactions operate as if they were executed in series, or one after the other. In other words, the Serializable isolation level reduces your big, expensive, multi-user database server to a single-user machine so that the effects of one user’s operations are totally unaffected by those of others. That’s because each user, in effect, hogs the entire database while he is using it and doesn’t give it up until he finishes. The Serializable isolation level is the only one that prevents phantom rows, but you should ask yourself whether your transactions really care about this. As you can imagine, the Serializable level inflicts a serious and sometimes deadly hit to the multi-user performance of your system and, as such, should be used only if you have an absolutely compelling reason for it. The Serializable isolation level is set as follows: SET TRANSACTION ISOLATION LEVEL SERIALIZABLE go BEGIN TRANSACTION . . . The big picture of transaction isolation Having a chart that plots isolation level against behavior may be useful, so check out the following table. Transaction isolation levels versus behaviors Isolation Level Dirty Read Nonrepeatable Read Phantom Read Uncommitted XX X Read Committed XX Repeatable Read X Serializable With all this about transaction isolation level said, how do you choose the appropriate isolation level for your transactions? By carefully studying the code in your transactions and considering their minimum consistency needs with respect to every other possible transaction that could be occurring simultaneously in the system. It’s not a simple task, but the following list gives you the basics: Note 13546228 ch10.F 1/30/03 10:50 AM Page 213 214 Part II ✦ Using ColdFusion MX with Databases 1. Start with your default transaction isolation level set to Read Committed. 2. Examine your first transaction’s code for the tables that it reads from and writes to. 3. If you don’t care whether the data being read is dirty, if the transaction’s modifications could take place anyway despite other concurrently running transactions, and if no further tests require a higher level of isolation, you can reduce the isolation level to Read Uncommitted for that transaction. 4. If you read from the same table twice during the same transaction and whether the first and second reads are different makes a difference to you, increase your isolation level to Repeatable Read on that transaction. 5. If you have any transactions that must not see others rows that are inserted while your transaction is executing, increase the isolation level of that transaction to Serializable. 6. Examine the rest of the transactions in your system by using this technique. Remember that you must individually consider each of your transactions in parallel with all other database operations that could possibly occur at the same time with multiple users — doing so is what makes fine-tuning transaction isolation such a difficult task. By the way, many developers just go as far as Step 1 and leave it at that. But if you’re going to use your database in a high-transaction throughput system, it serves you well to take the hours necessary to carefully analyze your transactions for their optimum isolation level and dial them in accordingly. Views As we state in Chapter 9, no matter how many tables a relational join traverses, regardless of whether it contains a GROUP BY clause (or anything else for that matter), all query result sets manifest themselves as one or more rows that contain an identical collection of one or more columns. So, in a way, query results are virtual tables based on underlying physical tables of data. Now imagine if you could take a query statement and define it as a formal database object that could be accessed just as a table can. Well, you can — that is what is called a view. Listing 10-5 defines a database view. Listing 10-5: Defining a database view CREATE VIEW vwEmployee AS SELECT SSN, CompanyID, Firstname, Lastname, DateOfBirth FROM Employee 13546228 ch10.F 1/30/03 10:50 AM Page 214 215 Chapter 10 ✦ Using Advanced Database Techniques The boldfaced code in Listing 10-5 is just a standard SELECT statement; the rest of the listing simply encapsulates that SELECT statement as a formal, reusable database object — a view — with a formal name. Now, whenever you want to see all employees without their salaries, you can simply perform the following: SELECT * FROM vwEmployee Similarly, you can perform more elaborate queries on a view, as if it was a table, as follows: SELECT SSN, Firstname + ‘ ‘ + Lastname AS Fullname, DateOfBirth FROM vwEmployee WHERE DateOfBirth > ‘01/01/1960’ ORDER BY DateOfBirth DESC In fact, views can be filtered, sorted, and joined just as any physical table can. Horizontal and vertical masking Views typically hide elements of data so that what is returned exposes only the data that is needed. The elements being hid can be specific columns of a table, rows in that table that do not satisfy a WHERE clause, or a combination of both. Basically, a view masks unneeded data from your application. These masks can be either vertical or horizontal. A vertical mask shields specific columns of a table from returning and is simply the defined collection of underlying table columns that your view contains. A horizontal mask shields specific rows of a table from returning and is simply the WHERE clause that supplies the filtering criteria for the view. You can combine both vertical and horizontal masking in the same view. Listing 10-6, for example, returns only the SalesOrderID, SaleDate, and OrderTotal columns of only those sales orders with a Status of 20. Listing 10-6: A view that combines both vertical and horizontal masking CREATE VIEW vwFinishedSalesOrder AS SELECT SalesOrderID, SaleDate, Total FROM SalesOrder WHERE Status = 20 13546228 ch10.F 1/30/03 10:50 AM Page 215 216 Part II ✦ Using ColdFusion MX with Databases Relational views Views don’t stop at a single table. In fact, views are often used to simplify complicated relational joins across multiple tables so that the application developer doesn’t need to concern himself with such complexities and can instead concentrate on simply displaying relevant data. Listing 10-7 defines such a relational view. Listing 10-7: Defining a view across multiple joined tables CREATE VIEW vwEmployeeCompany AS SELECT e.SSN, e.Firstname + ‘ ‘ + e.Lastname AS Fullname, e.DateOfBirth, c.CompanyName FROM Employee e INNER JOIN Company c ON e.CompanyID = c.CompanyID The ColdFusion developer now can select all employees born earlier than 1960, hide the salary column, and display the name of the company for which each works, all by simply doing the following: SELECT * FROM vwEmployeeCompany Similarly, suppose that you tried to perform a SELECT against this view that included the Salary column, as follows: SELECT SSN, Fullname, DateOfBirth, CompanyName FROM VwEmployeeCompany WHERE Salary > 100000 It would fail, because although the Salary column is a part of the underlying Employee table, it is not a part of the vwEmployeeCompany view being queried. Precompiling queries So why define views at all? Why not just CFINCLUDE a common CFQUERY call wherever that query is needed? Isn’t that just as good? No, it isn’t. Not by a long shot. You see, if you send a query to your database server, the server goes through the complexities and processing expense of parsing the syntax with which the query is defined, optimizing the query for best performance, and compiling an execution plan. This very expensive process is what happens almost every time that you send a plain query to your database — just so that it can run the query! 13546228 ch10.F 1/30/03 10:50 AM Page 216 217 Chapter 10 ✦ Using Advanced Database Techniques If you define a view, your database performs the same parse/optimize/compile process on the query for which the view is defined. If you call the view, the database server knows that an appropriate execution plan already exists and executes it directly, thereby eliminating virtually all the overhead necessary to execute the query and, in the bargain, increasing performance. You realize the biggest increases in performance by defining views on very complex relational queries, because these queries are the most difficult and time-consuming to parse and optimize. Enhancing security Another benefit that views afford you is the capability to lock down security on your database. Because a view is a formal database object, you can grant user and group privileges to it just as you would a table or a stored procedure. If fact, to denying direct access to physical tables and granting them only on views and stored procedures is a good practice overall — that way, you are absolutely certain that ColdFusion developers touch only the data that you want them to access. Caveats Views are great for easily selecting only that data that you want to access, but they can also be used to insert and update data. This can be both a help and a hindrance, depending on the view. Suppose that your view is a simple, single-table view containing all the NOT NULL columns of a table, as shown in Figure 10-1. Figure 10-1: A simple view containing all critical columns of a single table. In such a case, you can easily insert and update data through this view, as shown in Listing 10-8. Listing 10-8: Examples of inserting and updating through a simple view INSERT INTO vwInventoryItem ( ItemNumber, Description, UnitPrice, AvailableToSell ) VALUES ( ‘CAS30-BLK’, Continued InventoryItem.ItemNumber (vPK) InventoryItem.Description InventoryItem.UnitPrice InventoryItem.AvailableToSell InventoryItem.ReorderLevel InventoryItem.ReorderQuantity ItemNumber VARCHAR(15) NOT NULL Description VARCHAR(40) NOT NULL UnitPrice NUMERIC(12,2) NOT NULL AvailableToSell INTEGER NOT NULL ReorderLevel INTEGER NOT NULL ReorderQuantity INTEGER NOT NULL Comments VARCHAR(200) NULL InventoryItem vwInventoryItem 13546228 ch10.F 1/30/03 10:50 AM Page 217 [...]... 1/30/03 10:50 AM Page 224 Part II ✦ Using ColdFusion MX with Databases Note You may need to prefix the stored procedure object with the owner’s name, followed by a period BLOCKFACTOR controls how many rows at a time are returned from the database server to ColdFusion server The range of values... by including a call to CFPROCRESULT, you can bind that cursor to a standard ColdFusion query object and make use of it in your ColdFusion applications By using this method through the Type 4 JDBC Oracle database driver available in ColdFusion MX Enterprise or Oracle’s free OIC database driver in conjunction with ColdFusion MX Professional, Oracle can return multiple result sets from Oracle packages,... 10:50 AM Page 220 Part II ✦ Using ColdFusion MX with Databases Listing 10-10 (continued) CompanyName, ZipCode FROM Company WHERE State = @State ORDER BY ZipCode ASC RETURN After you execute Listing 10-10 against the database, you have a precompiled stored procedure object, sp_GetCompanies, that you can call from your ColdFusion application To call this stored procedure from ColdFusion, you use a CFSTOREDPROC... 10:50 AM Page 228 Part II ✦ Using ColdFusion MX with Databases Listing 10-20 (continued) SELECT @AverageSalary = Avg(Salary) FROM Employee RETURN The OUTPUT qualifier next to a parameter is what instructs the stored procedure to expose that parameter to the calling program (in this case, your ColdFusion application) after it has finished executing Listing 10-21 shows the ColdFusion code that executes... 13546228 ch10.F 230 1/30/03 10:50 AM Page 230 Part II ✦ Using ColdFusion MX with Databases Listing 10-23 shows the ColdFusion code that executes the stored procedure and then displays the InOut parameter Listing 10-23: Calling the stored procedure from Listing 10-22 and displaying the InOut parameter @AmountDue Numeric(12,2) OUTPUT ) . AM Page 211 212 Part II ✦ Using ColdFusion MX with Databases that is in the process of being updated by another user, the result set contains data exactly as it appears with the other user’s modifications. you the basics: Note 13546228 ch10.F 1/30/03 10:50 AM Page 213 214 Part II ✦ Using ColdFusion MX with Databases 1. Start with your default transaction isolation level set to Read Committed. 2. Examine. ch10.F 1/30/03 10:50 AM Page 223 224 Part II ✦ Using ColdFusion MX with Databases <cfstoredproc procedure=”sp_SomeStoredProcedure” datasource=”CFMXBible” blockfactor=”10” returncode=”Yes”> You