`
choelea
  • 浏览: 75218 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Data management and persistence in J2EE applications

阅读更多

Data management and persistence in J2EE applications

 

This article examines two data management strategies available on the Java platform: Java object serialization and Java Database Connectivity (JDBC). While neither data management strategy is inherently superior or inferior to the other, when it comes to managing enterprise information systems, JDBC wins hands down. In this article Java developers G.V.B. Subrahmanyam and Shankar Itchapurapu look at both serialization and JDBC, and through discussion and example show you why JDBC is your best bet.

 

When you're building an enterprise information system, you need to make sure that your enterprise's data is stored, retrieved, and presented in an efficient way. Data is the single largest asset for any business. All software systems deal with data, and thus its importance cannot be over-emphasized.

An application's data management functions include the four basic operations that are generally carried out on enterprise data: create, retrieve, update,and delete(aka CRUD). Managing data in enterprise systems involves performing the CRUD operations consistently and successfully for a long period of time without having to frequently change the code that actually performs the operations. To put it another way, managing data means developing robust, scalable, and maintainable software systems for successful CRUD operations that are guaranteed to perform consistently through the life of the software.

This article discusses two data management strategies available with the J2EE: Java object serialization and Java Database Connectivity (JDBC). We'll look at the strengths and weaknesses of both of these approaches. No data management strategy is inherently superior or inferior to others. The applicability of a strategy in a particular implementation depends on the scope(the spectrum of activities occurring in the system environment) of the project, the contextof the system (the set of values that drive the runtime of the system/subsystem), and other external factors. However, Java serialization is ill-suited for enterprise systems in which data needs to be organized in a well-defined structure (for example, an RDBMS). We'll first walk through a quick overview of Java object serialization, then walk through some of the more important aspects of JDBC to see how the latter implements crucial features that the former lacks.

This article is not intended to be a comprehensive introduction to either Java object serialization or JDBC. For more information on both technologies, review the Resourcessection.

Java object serialization

Object serializationis the simplest of Java persistence strategies. Object serialization is a process of flattening object graphs to a linear sequence of bytes. Objects graphs are relations realized as a result of the inheritance, association, and aggregation of objects. An object's non-transient instance properties will be written to persistent storage in the form of bytes. The values of instance properties will be the values in the memory at the time serialization is performed. For a Java object to be serializable, it must at minimum implement the java.io.Serializable interface , which has the structure shown below:

 

package java.io;
public interface Serializable
{}

As you can see, the java.io.Serializable interface doesn't declare any methods. It is a marker,or tagged,interface. It tells the Java Runtime Environment that the implementing class can be serialized. Listing 1 shows a sample class that implements this interface.


Listing 1. MySerializableObject.java

import java.io.Serializable;
public class MySerializableObject extends MySuperClass implements Serializable
{
	private String property1 = null;
private String property2 = null;
public String getProperty1()
{
	return property1;
}
public void setProperty1(String val)
{
	property1 = val;
}
public String getProperty2()
{
	return property2;
}
public void setProperty2(String val)
{
      property2 = val;
}
      private void writeObject(ObjectOutputStream out)
      throws IOException
      {
      	out.writeObject (getProperty1 ());
     	out.writeObject (getProperty2 ());
      }
      private void readObject (ObjectInputStream in)
      throws IOException, ClassNotFoundException
      {
       setProperty1 ((String) in.readObject ());
       setProperty2 ((String) in.readObject ());
      }
}

You don't need to implement the writeObject(...) and readObject(...) methods yourself to perform serialization; the Java Runtime Environment will have the default implementation of these methods available. However, you can override these methods and provide your own implementation of how the object state is to be stored.

There are some points about serialization to keep in mind. First, the entire object graph (that is, all parent and referenced classes) will be serialized during serialization. Second, all instance variables of a Serializable class should themselves be Serializable unless they have been specifically declared transient, or the writeObject(...) and readObject(...) methods have been overridden to serialize only serializable instance variables. If this latter rule is violated, an exception will be thrown at runtime.

Each succeeding release of J2SE has made small additions to the object serialization system. J2SE 1.4 added writeUnshared() and readUnshared() methods to ObjectOutputStream and ObjectInputStream , respectively. Normally, a serialized stream contains only one serialized instance of any given object and makes back references to it from other objects that share references to it. It is often desirable to serialize an object independent of any references that other objects may maintain to it. The unshared read and write methods allow objects to be serialized as new and unique objects, achieving an effect similar to object cloning but with less overhead.

What's wrong with Java object serialization?

Serialization involves externalizing object graphs from memory to persistent storage (like a hard disk). This involves a lot of I/O overhead. Generally, serialization is not the optimal choice for applications that:

  • Manage hundreds of thousands of megabytes of persistent data
  • Update serializable objects frequently

Serialization is a bad choice for storing enterprise data because:

  • Serialized streams of bytes are readable only by the Java language. This is a major drawback because enterprise systems are generally heterogeneous, with many applications cooperating with each other and working on the same data.

  • Object retrieval involves lot of I/O overhead.

  • There is no query language for retrieving data from a serialized object graph.

  • Serialization has no built-in security mechanism.

  • Serialization does not offer any transaction control mechanisms per se, so it cannot be used within applications that need concurrent access without making use of additional APIs.

 




回页首

 

Java Database Connectivity (JDBC)

Java Database Connectivity(JDBC) is a standard API for interacting with databases using the Java programming language. Call-level interfaces such as JDBC are programming interfaces that allow external access to SQL commands that manipulate and update data in a database. They allow the integration of SQL calls into a general programming environment by providing library routines that interface with the database. In particular, JDBC has a rich collection of routines that make such an interface extremely simple and intuitive.

Over the next few sections, we'll look at the steps involved in interfacing with a database through JDBC. We'll do this with a particular focus on how JDBC compares with Java object serialization as an enterprise data management strategy.

Establish a database connection

Before you do anything else with JDBC, you need to obtain a database driver from a driver vendor and add the library to your class path. Once you've done that, you'll use code like that shown below in your Java program to actually make the connection.

 

Class.forName(<your driver class Name>);
Java.sql.Connection conn = DriverManager.getConnection(<connection URL>);

Java object serialization doesn't require this step, because performing persistence operations using serialization does not require a DBMS. Serialization is a file-based mechanism; so you would need to open an I/O stream to the target file system before you can serialize an object.

Creating JDBC Statements and PreparedStatements

A JDBC Statement object is used to send your SQL statements to the database management system (DBMS), and should not be confused with an SQL statement. A JDBC Statement object is associated with an open connection, and not any single SQL statement. You can think of a JDBC Statement object as a channel sitting on a connection, passing one or more of your SQL statements (which you ask it to execute) to the DBMS.

You need an active connection in order to create a Statement object. Using the Connection object con that we created previously, the code below does just that.

 

    Statement stmt = con.createStatement(); 

At this point, a Statement object exists, but it does not have an SQL statement to pass on to the DBMS.

When a database receives a statement, the database engine first parses that statement and looks for syntax errors. Once the statement is parsed, the database needs to figure out the most efficient way to execute it. This can be computationally quite expensive. The database checks what indexes, if any, can help, or whether it should do a full read of all rows in a table. Databases use statistics on the data to figure out the best way. Once a query planis created, then the database engine can execute it.

It takes CPU power to generate such a plan. Ideally, if we send the same statement to the database twice, then we'd like the database to reuse the access plan for the first statement. We can use a PreparedStatement object to achieve this.

There's one main feature that distinguishes PreparedStatement from its superclass Statement : unlike Statement , PreparedStatement is given an SQL statement when it is created. This SQL statement is then sent to the DBMS right away, where it is compiled. Thus, in effect, a PreparedStatement is associated as a channel with a connection and a compiled SQL statement.

What's the advantage? Well, if you need to use the same query or similar queries with different parameters multiple times, then with a PreparedStatement the statement can be compiled and optimized by the DBMS just once. Contrast this with a use of a normal Statement , where each use of the same SQL statement requires a compilation all over again.

PreparedStatement s are also created with a Connection method. The code below shows how to create a parameterized SQL statement with three input parameters.

 

 
   PreparedStatement prepareUpdatePrice = con.prepareStatement( 
      "UPDATE Sells SET price = ? WHERE bar = ? AND beer = ?");

Note that Java serialization does not support a query language like SQL. The only way to access an object property using Java serialization is to deserialize the object and call the getter/accessor methods on the object. Deserializing an entire object can be computationally expensive, especially if your application needs to do it repeatedly throughout the lifetime of your program.

Before we can execute a PreparedStatement , we need to supply values for the parameters. Calling one of the setXXX() methods defined in the PreparedStatement class can do this. The most often used methods are setInt() , setFloat() , setDouble() , and setString() . You can set these values before each execution of the prepared statement.

Executing statements and queries

The way you execute SQL statements in JDBC varies depending on the intention of the SQL statement. DDL (data definition language) statements (such as table creation and table alteration statements) and statements that update table contents are all executed using executeUpdate() . Listing 2 contains examples of executeUpdate() statements.


Listing 2. executeUpdate() in action

   Statement stmt = con.createStatement();
   stmt.executeUpdate("CREATE TABLE Sells " +
      "(bar VARCHAR2(40), beer VARCHAR2(40), price REAL)" );
   stmt.executeUpdate("INSERT INTO Sells " +
      "VALUES ('Bar Of Foo', 'BudLite', 2.00)" );
   String sqlString = "CREATE TABLE Bars " +
      "(name VARCHAR2(40), address VARCHAR2(80), license INT)" ;
   stmt.executeUpdate(sqlString);

We would execute a PreparedStatement by first plugging in the values of the parameters (as seen above), and then invoking executeUpdate() on it, as shown below:

 

      int n = prepareUpdatePrice.executeUpdate() ;

In contrast, a query is expected to return a set of rows as its result, and not change the state of the database. There is a corresponding method called executeQuery() , which returns its results as a ResultSet object, as shown in Listing 3.


Listing 3. Executing a query

   String bar, beer ;
   float price ;
   ResultSet rs = stmt.executeQuery("SELECT * FROM Sells");
   while ( rs.next() ) {
      bar = rs.getString("bar");
      beer = rs.getString("beer");
      price = rs.getFloat("price");
      System.out.println(bar + " sells " + beer + " for " + price + " Dollars.");
   }

The bag of rows resulting from the query is contained in the variable rs , which is an instance of ResultSet . A set is not of much use to us unless we can access each row and the attributes in each row. The ResultSet provides a cursor that can be used to access each row in turn. The cursor is initially set just before the first row. Each invocation of the method causes the cursor to move to the next row, returning true if that row exists or false if there is no remaining row.

We can use the getXXX() method of the appropriate type to retrieve the attributes of a row. In the previous example, we used the getString() and getFloat() methods to access the column values. Notice that we provided the name of the column whose value is desired as a parameter to the method; we could have specified the column numbers instead of the column name. The column numbers would be 1 for the first column retrieved, 2 for the second, and so on.

While working with a PreparedStatement , we would execute a query by first plugging in the values of the parameters, and then invoking executeQuery() on it, as shown below:

 

      ResultSet rs = prepareUpdatePrice.executeQuery() ;

Notes on accessing a ResultSet
JDBC also offers you a number of methods to find out where you are in the result set: getRow() , isFirst() , isBeforeFirst() , isLast() , and isAfterLast() .

There are also means to give scrollable cursors free access to any row in the result set. By default, cursors scroll forward only and are read only. When creating a Statement for a Connection , you can change the type of ResultSet to a more flexible scrolling or updatable model, as shown here:

 

      Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
<!-- code sample is too wide -->      ResultSet rs = stmt.executeQuery("SELECT * FROM Sells");

The different options for types are TYPE_FORWARD_ONLY , TYPE_SCROLL_INSENSITIVE , and TYPE_SCROLL_SENSITIVE . You can choose whether the cursor is read-only or updateable using the options CONCUR_READ_ONLY and CONCUR_UPDATABLE . With the default cursor, you can scroll forward using rs.next() . With scrollable cursors you have more options, as shown below:

 

      rs.absolute(3);          // moves to the third retrieved row
      rs.previous();           // moves back one row in the retrieved result set
      rs.relative(2);          // moves forward two rows in the retrieved result set
      rs.relative(-3);         // moves back three rows in the retrieved result set 

There are a great many more details to the workings of scrollable cursors. Scrollable cursors, though useful for certain applications, incur an extremely high performance penalty, and should be used with restraint and caution. You can find more information about scrollable ResultSet s in the Resourcessection.

There is no serialization counterpart to JDBC's ResultSet . Serialization and JDBC view underlying data from different perspectives. JDBC assumes (most commonly) a relational structure of data; serialization assumes an object graph. The underlying data structures for the two techniques differ significantly. JDBC's Set structure does not map naturally to the object graph structure of serialization, and vice versa. When a Java object is persisted using serialization semantics, the underlying structure of the data becomes a stream of bytes representing the associations between various internal objects of the core object that has been serialized.

ResultSet navigation in JDBC is the process of moving from one Set element to another. This is not possible in the case of object serialization because serialization involves object associations rather than a group of rows packed into an entity set. Thus, Java object serialization does not offer you the ability to access only discrete portions of a data set in this way.

Transactions

JDBC allows SQL statements to be grouped together into a single transaction. Thus, we can ensure the ACID properties using JDBC transactional features.

The Connection object performs transaction control. When a connection is created, by default it is in the auto-commit mode. This means that each individual SQL statement is treated as a transaction by itself, and will be committed as soon as its execution finished.

You turn auto-commit mode for an active connection off and on as shown here:

 

      con.setAutoCommit(false) ; 
      con.setAutoCommit(true) ; 

Once auto-commit is off, no SQL statements will be committed (that is, the database will not be permanently updated) until you have explicitly told it to commit by invoking the commit() method. At any point before commit, we may invoke rollback() to roll back the transaction and restore values to the last commit point (before the attempted updates).

We can also set transaction isolation levels as desired. For example, we can set the transaction isolation level to TRANSACTION_READ_COMMITTED , which will not allow a value to be accessed until after it has been committed, and which will forbid dirty reads. There are five such values for isolation levels provided in the Connection interface. By default, the isolation level is serializable. JDBC allows us to find out what transaction isolation level the database is set to (using the Connection method getTransactionIsolation() ) and set the appropriate level (using the Connection method setTransactionIsolation() ).

Rollback will usually be used in combination with the Java language's exception handling ability. Such a combination provides an excellent and easy mechanism for handling data integrity. We'll study error handling using JDBC in the next section.

Note that Java object serialization does not have direct support for transaction management. If you're using serialization, you'll need to fall back on other APIs, such as JTA, for achieving this effect. However, to achieve the effect of transaction isolation, you may choose to synchronize the serialized object when performing an update on it, as shown here:

 

		Synchronized(my_deserialized_object) {
		//Perform the updates etc...
		}

Handling errors with exceptions

Errors always occur in software programs. Often, database programs are critical applications, and it is imperative that errors be caught and handled gracefully. Programs should recover and leave the database in a consistent state. Rollbacks used in conjunction with Java exception handlers are a clean way of achieving such a requirement.

The client (program) accessing a server (database) needs to be aware of any errors returned from the server. JDBC gives access to such information by providing two levels of error conditions: SQLException and SQLWarning . SQLException s are Java exceptions, which, if not handled, will terminate the application. SQLWarning s are subclasses of SQLException , but they represent nonfatal errors or unexpected conditions, and as such, can be ignored.

In Java code, statements that are expected to throw an exception or a warning are enclosed in a try block. If a statement in the try block throws an exception or a warning, it can be caught in one of the corresponding catch statements. Each catch statement specifies the exceptions that it is ready to catch.

Alternatively, if your data types are correct, an exception might be thrown if your database size goes over its space quota and is unable to construct a new table. SQLWarning s can be retrieved from Connection , Statement , and ResultSet objects. Each only stores the most recent SQLWarning . So if you execute another statement through your Statement object, for instance, any earlier warnings will be discarded. Listing 4 illustrates the use of SQLWarning s.


Listing 4. SQLWarnings in action

      ResultSet rs = stmt.executeQuery("SELECT bar FROM Sells") ;
      SQLWarning warn = stmt.getWarnings() ;
      if (warn != null)
         System.out.println("Message: " + warn.getMessage()) ;
      SQLWarning warning = rs.getWarnings() ;
      if (warning != null)
         warning = warning.getNextWarning() ;
      if (warning != null)
         System.out.println("Message: " + warn.getMessage()) ;

SQLWarning s are actually somewhat rarer than SQLException s. The most common is a DataTruncation warning, which indicates that there was a problem while reading or writing data from the database.

Java does not provide special exception classes to be used with serialization. Most of the exceptions that occur when serialization is used are related the I/O operations performed, so the I/O exception classes will suffice in those cases.

Batch processing

JDBC 2.0 provides a powerful API for batch processing. Batch processing allows for piling up a group of SQL statements and sending them for processing in one go. A typical batch processing scenario might involve a banking application that updates a number of accounts every quarter. Batch processing is a powerful feature in that it reduces the number of round trips from Java code to a database.

The Statement interface provides an addBatch(String) method for adding an SQL statement to a batch. Once you've added all your SQL statements to the batch, you can execute them in one go using the executeBatch() method.

The executeBatch() method then executes the SQL statements and returns an array of int values. This array contains the number of rows affected by each statement. Putting a SELECT statement or other ResultSet -returning SQL in a batch will result in a SQLException .

A simple example of batch processing with java.sql.Statement is shown in Listing 5.


Listing 5. Batch processing in action

Statement stmt = conn.createStatement();
stmt.insert("DELETE FROM Users");
stmt.insert("INSERT INTO Users VALUES('rod', 37, 'circle')");
stmt.insert("INSERT INTO Users VALUES('jane', 33, 'triangle')");
stmt.insert("INSERT INTO Users VALUES('freddy', 29, 'square')");
int[] counts = stmt.executeBatch();

Batch processing is a nice way to handle SQL code when you don't know how many times a particular statement will run. For instance, if we tried to insert 100 records into a database without batching, the performance may be affected. If we wrote a script to add 10,000 records, things could get nasty. Adding batch processing helps to improve the performance, and in the latter case even improves the readability of the code.

Java object serialization does not support batch processing. Typically, serialization involves working on the extent (the association graph) of an object, so batch processing doesn't make sense in such a case. Batch processing thus affords you a flexibility in terms of the timing and grouping of data updates that is not necessarily available with serialization.

 




回页首

 

Calling a stored procedure from Java code

A stored procedureis a group of SQL statements that form a logical unit and perform a particular task. Stored procedures are used to encapsulate a set of operations or queries that will execute on a database server. Stored procedures are compiled and stored in the database server. So each time a stored procedure in invoked, the DBMS will reuse the compiled binary, and hence the execution will be faster.

JDBC allows you to call a database stored procedure from an Java application. The first step is to create a CallableStatement object. As with Statement and PreparedStatement objects, this is done with an open Connection object. A CallableStatement object contains a call to a stored procedure; it does not contain the stored procedure itself. The first line of code in Listing 6 creates a call to the stored procedure SHOW_ACCOUNT using the connection con . The part that is enclosed in curly braces is the escape syntax for stored procedures. When the driver encounters {call SHOW_ACCOUNT} , it will translate this escape syntax into the native SQL used by the database to call the stored procedure named SHOW_ACCOUNT .


Listing 6. A stored procedure in action

 
CallableStatement cs = con.prepareCall("{call SHOW_ACCOUNT(?)}");
cs.setInt(1,myaccountnumber);
ResultSet rs = cs.executeQuery();

Assume the stored procedure SHOW_ACCOUNT in Sybase has code shown in Listing 7.


Listing 7. SHOW_ACCOUNT stored procedure

 
CREATE PROCEDURE SHOW_ACCOUNT (@Acc int) 
AS
BEGIN
	Select balance from USER_ACCOUNTS where Account_no = @Acc
END

The ResultSet rs will look like this:

 

balance			
----------------		
12000.95

Note that the method used to execute cs is executeQuery() , because cs calls a stored procedure that contains one query and thus produces one result set. If the procedure had contained one update or one DDL statement, the method executeUpdate() would have been the one to use. It is sometimes the case, however, that a stored procedure contains more than one SQL statement, in which case it will produce more than one result set, more than one update count, or some combination of result sets and update counts. In such a case, the method execute() should be used to execute the CallableStatement .

The class CallableStatement is a subclass of PreparedStatement , so a CallableStatement object can take input parameters just as a PreparedStatement object can. In addition, a CallableStatement object can take output parameters or parameters that are for both input and output. INOUT parameters and the execute() method are rarely used. For handling OUT parameters, we need to register an OUT parameter with the stored procedure using the method registerOutParameter(int, int) .

As an example, let's assume that the GET_ACCOUNT procedure contains the code in Listing 8.


Listing 8. GET_ACCOUNT

CREATE PROCEDURE GET_ACCOUNT (@Acc int, @balance float OUTPUT) 
AS
BEGIN
Select @balance = balance from USER_ACCOUNTS where Account_no = 	@Acc
END

In this case, the parameter balance has been declared as an OUT parameter. Now the JDBC code for calling the procedure will be as in Listing 9.


Listing 9. JDBC code for calling a stored procedure

CallableStatement csmt = con.prepareCall("{GET_ACCOUNT(?,?)");
csmt.setInt(1,youraccountnumber);
csmt.registerOutParamter(2,java.sql.Types.FLOAT);
csmt.execute();

When you're using Java serialization, you do not need to access and external system like a DBMS. In other words, serialization is a pure Java language phenomenon, so it doesn't involve executing a compiled code residing in an external environment. Hence, there is no counterpart for JDBC's CallableStatement object in serialization. This means that you can't offload data processing to external systems or components that might be better suited for it.

 




回页首

 

Wrapping up

We hope you'll agree, after reading this article, that JDBC is a much better approach to data management and persistence than Java object serialization.

JDBC is an excellent API for accessing data stores. The best thing about JDBC is that it provides a single set of APIs for accessing a variety of data sources. The user needs to learn only one set of APIs to access any data source, be it relational, hierarchical, or any other format. All that is needed is a JDBC driver to connect to the target data source. JDBC does a great job of wrapping all technical details inside an implementation wrapper and freeing the programmer from vendor-specific hassles.

Table 1 compares the various features of JDBC and Java object serialization.

Table 1. JDBC vs. Java serialization

 

Object serialization JDBC
Data management Uses the filesystem for storing the serialized object forms. Doesn't include a special data management system. The serialized objects (stored in flat files) are generally managed by the underlying OS in its own particular way. Uses an EAI/database for storing data. The EAI or database will have a special Database Management System (DBMS) to manage the data in the data source. JDBC works as an interface between the JVM and the DBMS to send requests to the DBMS. JDBC itself doesn't have any data management functions.
Data structure The underlying data structure is an object graph. Serialization writes the state of a Java object to a filesystem. Underlying data structure can be relational, hierarchical, or a network. But the logic view of the data is most commonly a table.
Data definition The data definition involves creating a serializable object and persisting the object using serialization semantics. The data definition involves creating necessary tables in the target data store and providing clear definition of domain-level relationships between entity sets. This is generally done using the software provided by the target DBMS.
Data retrieval Retrieval involves deserializing the object and reading the object properties using accessor methods. DBMS provides a special data sub-language for data retrieval. The statements written in the data sublanguage will be passed to the target data source through the JDBC API. It is the responsibility of the DBMS to validate, execute and return the results of the statements.
Security There is no well-defined security mechanism available. However, the underlying OS can provide security to the serialized files. A DBMS provides a comprehensive set of security features. It does the job of authentication and authorization. The JDBC API needs to send the credentials to the target DBMS before it can access or manipulate the data on the DBMS.
Transactions There is no specific transaction control mechanism available. Transactions can be maintained programmatically by using other J2EE APIs, such as JTA or JTS. Sophisticated transaction management is provided by the DBMS. The JDBC API provides useful methods to commit and roll back transactions.
Concurrency control There is no specific concurrency control mechanism available. However, by using the synchronization techniques in the Java language, you can achieve the effect of concurrency control. Multiple levels of transaction isolation are provided by the DBMS. We can choose a particular level of isolation using JDBC API methods.

Java object serialization and JDBC are two of many data persistence mechanisms available in the Java technology sphere. Serialization is best suited when data needs to be shared in a Java language-specific format among multiple JVMs (as done in RMI's pass-by-value mechanism). However, Java serialization is ill-suited for enterprise systems where data needs to be organized in a well-defined structure. In such enterprise systems, data needs to be shared between multiple systems and subsystems that may not all be Java language compatible. In such cases, object serialization does not work at all.

JDBC provides a common API for accessing heterogeneous data stores. It acts as the glue between the JVM and the target DBMS. It provides a programmatic way of accessing data stores and maintaining enterprise data using the Java platform. However, all the code required for performing CRUD operations needs to be written by the developer.

To get the best out of JDBC in enterprise environments, architects need to analyze the data in their enterprise and evolve a framework for data persistence. Since the mechanism for persisting data using JDBC is not relevant to the business problem the system intends to address, it is strongly recommended that the data persistence layer be separated from application business logic. Design patterns will greatly help in designing such framework.

分享到:
评论

相关推荐

    利用Simulink实现混合储能系统在直流微网中的下垂控制策略研究:保持直流母线电压稳定的实践与探究,Simulink仿真下的光储直流微网混合储能系统下垂控制策略优化研究(注意版本要求为2021A以上

    利用Simulink实现混合储能系统在直流微网中的下垂控制策略研究:保持直流母线电压稳定的实践与探究,Simulink仿真下的光储直流微网混合储能系统下垂控制策略优化研究(注意版本要求为2021A以上),混合储能系统 光储微网 下垂控制 Simulink仿真 注意版本2021A以上 由光伏发电系统和混合储能系统构成直流微网。 混合储能系统由超级电容器和蓄电池构成,通过控制混合储能系统来维持直流母线电压稳定。 混合储能系统采用下垂控制来实现超级电容和蓄电池的功率分配,蓄电池响应低频量,超级电容响应高频量。 通过改变光照来影响光伏出力,控制混合储能系统保持微网直流母线电压稳定在380V,不受光伏出力变化影响。 ,混合储能系统; 光储微网; 下垂控制; Simulink仿真; 版本2021A; 直流母线电压稳定; 光伏出力变化; 超级电容器; 蓄电池。,2021A+混合储能系统:光储微网下垂控制Simulink仿真研究

    JavaScript入门到精通: 全栈编程语言的基础与进阶学习指南

    内容概要:本文档是针对JavaScript这一跨平台解释型语言的详尽入门手册,首先概述了JavaScript的概念及其重要特性,强调它不仅适用于前端同时也活跃于Node.js的服务器环境之中,从而成为全栈开发的重要技能。紧接着文档阐述了JavaScript的基本语法元素如变量声明、数据类型、运算符及控制结构,让新手理解JavaScript的语法规则,并通过函数与对象操作加深印象。之后介绍了一些常见的实用工具和高级用法,例如模板字符串、解构赋值以及异步编程手段(比如Promise)。对于想要深入探索的应用场景给出了广泛的指引,无论是传统的web开发还是新兴领域的IoT或自动化脚本编写皆有所涉猎。 适合人群:对于那些没有编程背景或有其他编程经验但仍希望了解并擅长运用JavaScript的个人来说非常适合。 使用场景及目标:目的是向初学者提供足够的理论指导和技术实践机会,使他们能够在不同平台上利用JavaScript创造出有意义的作品;不论是想要从事专业软件开发或是业余项目爱好者都能够从中受益。 其他说明:文档还提供了大量权威且有用的外部链接供进一步深造学习,包括但不限于主流的在线课程、权威的技术参考资料及充满活力的支持社区。

    2D3D 中弗里德里希常数和庞加莱常数的计算 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    级联H桥SVG无功补偿系统在不平衡电网中的三层控制策略:电压电流双闭环PI控制、相间与相内电压均衡管理,级联H桥SVG无功补偿系统在不平衡电网中的三层控制策略:电压电流双闭环PI控制、相间与相内电压均

    级联H桥SVG无功补偿系统在不平衡电网中的三层控制策略:电压电流双闭环PI控制、相间与相内电压均衡管理,级联H桥SVG无功补偿系统在不平衡电网中的三层控制策略:电压电流双闭环PI控制、相间与相内电压均衡管理,不平衡电网下的svg无功补偿,级联H桥svg无功补偿statcom,采用三层控制策略。 (1)第一层采用电压电流双闭环pi控制,电压电流正负序分离,电压外环通过产生基波正序有功电流三相所有H桥模块直流侧平均电压恒定,电流内环采用前馈解耦控制; (2)第二层相间电压均衡控制,注入零序电压,控制通过注入零序电压维持相间电压平衡; (3)第三层相内电压均衡控制,使其所有子模块吸收的有功功率与其损耗补,从而保证所有H桥子模块直流侧电压值等于给定值。 有参考资料。 639,核心关键词: 1. 不平衡电网下的SVG无功补偿 2. 级联H桥SVG无功补偿STATCOM 3. 三层控制策略 4. 电压电流双闭环PI控制 5. 电压电流正负序分离 6. 直流侧平均电压恒定 7. 前馈解耦控制 8. 相间电压均衡控制 9. 零序电压注入 10. 相内电压均衡控制 以上十个关键词用分号分隔的格式为:不

    基于时空RBF-NN的混沌时间序列预测 附Matlab代码.rar

    1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

    基于主从博弈的动态定价策略与电动汽车充电管理优化在智能小区的实践(MATLAB+CPLEX gurobi实现),基于主从博弈理论的智能小区电动汽车充电与代理商动态定价策略优化研究,MATLAB代码:基

    基于主从博弈的动态定价策略与电动汽车充电管理优化在智能小区的实践(MATLAB+CPLEX gurobi实现),基于主从博弈理论的智能小区电动汽车充电与代理商动态定价策略优化研究,MATLAB代码:基于主从博弈的智能小区代理商定价策略及电动汽车充电管理 关键词:电动汽车 主从博弈 动态定价 智能小区 充放电优化 参考文档:《基于主从博弈的智能小区代理商定价策略及电动汽车充电管理》基本复现 仿真平台:MATLAB+CPLEX gurobi平台 主要内容:代码主要做的是一个电动汽车充电管理和智能小区代理商动态定价的问题,将代理商和车主各自追求利益最大化建模为主从博弈,上层以代理商的充电电价作为优化变量,下层以电动汽车的充电策略作为优化变量,通过优化得出最优电价策略以及动态充电策略。 ,电动汽车; 主从博弈; 动态定价; 智能小区; 充放电优化; MATLAB; CPLEX; gurobi平台。,基于主从博弈的电动汽车充电管理与定价策略优化MATLAB代码实现

    (程序、GUI、思路)MATLAB打印纸缺陷检测GUI设计.zip

    基于Matlab语言实现的设计项目 2、适用人群:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业或毕业设计中的部分功能,作为“参考资料”使用。 3、解压说明:本资源需要电脑端使用WinRAR、7zip等解压工具进行解压,没有解压工具的自行百度下载即可。 4、免责声明:本资源作为“参考资料”而不是“定制需求”,代码只能作为参考,不能完全复制照搬。不一定能够满足所有人的需求,需要有一定的基础能够看懂代码,能够自行调试代码并解决报错,能够自行添加功能修改代码。由于作者大厂工作较忙,不提供答疑服务,如不存在资源缺失问题概不负责,谢谢理解。

    《基于 Transformer 的恶意软件检测器》(毕业设计,源码,教程)简单部署即可运行。功能完善、操作简单,适合毕设或课程设计.zip

    资源内项目源码是均来自个人的课程设计、毕业设计或者具体项目,代码都测试ok,都是运行成功后才上传资源,答辩评审绝对信服的,拿来就能用。放心下载使用!源码、说明、论文、数据集一站式服务,拿来就能用的绝对好资源!!! 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、大作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 4、如有侵权请私信博主,感谢支持

    Labiew噪音与振动检测模块源码揭秘:傅里叶变换与倍频程技术应用于实际项目,LabVIEW平台噪声与振动检测模块源码解析:基于傅里叶变换与倍频程原理的实用功能模块,已成功应用于实际项目,虚拟产品退换

    Labiew噪音与振动检测模块源码揭秘:傅里叶变换与倍频程技术应用于实际项目,LabVIEW平台噪声与振动检测模块源码解析:基于傅里叶变换与倍频程原理的实用功能模块,已成功应用于实际项目,虚拟产品退换政策严谨执行,Labiew噪音与振动检测模块源码,改功能模块已运用到实际项目,原理是利用傅里叶变和倍频程实现的,产品一旦发概不 。 需要的可以联系哟 ,Labiew源码; 噪音与振动检测模块; 傅里叶变换; 倍频程; 实际项目运用,Labiew傅里叶变换倍频程噪音振动检测模块源码

    基于Comsol多物理场仿真的光伏集热器异形体建模技术研究,探索comsol多物理场仿真技术:光伏集热器异形体建模应用,comsol多物理场仿真,光伏集热器,异形体建模 ,comsol多物理场仿真;

    基于Comsol多物理场仿真的光伏集热器异形体建模技术研究,探索comsol多物理场仿真技术:光伏集热器异形体建模应用,comsol多物理场仿真,光伏集热器,异形体建模 ,comsol多物理场仿真; 光伏集热器仿真; 异形体建模,Comsol多物理场仿真在光伏集热器及异形体建模中的应用

    器官3D分割-基于WinForm框架开发的医学影像系统源码+sln+演示视频(毕设基于c#和python开发).zip

    器官3D分割-基于WinForm框架开发的医学影像系统源码+sln+演示视频(毕设基于c#和python开发).zip 【项目简单介绍】 主要功能 肺炎诊断 器官 3D 分割 该系统具备肺炎诊断和器官 3D 分割的功能,并模仿了罗万科技的系统界面风格。 python和c#开发实现

    界面GUI设计MATLAB BP的水果识别.zip

    MATLAB可以用于开发水果识别系统。这种系统通常利用机器学习和图像处理技术,对输入的水果图像进行特征提取和分类识别。以下是开发水果识别系统的一般步骤: 1. 数据收集:收集包含各种水果类别的图像数据集。 2. 数据预处理:对图像进行预处理,包括裁剪、缩放、灰度化等操作。 3. 特征提取:从每个水果图像中提取特征,例如颜色直方图、纹理特征、形状特征等。 4. 数据标记:为每个图像标记水果类别,形成训练集和测试集。 5. 模型训练:使用机器学习算法(如支持向量机、卷积神经网络等)对训练集进行训练,建立水果识别模型。 6. 模型测试:使用测试集对模型进行测试和评估,调整模型超参数以提高准确率。 7. 系统集成:将训练好的模型集成到MATLAB应用程序中,实现水果识别功能。 8. 用户界面设计:设计用户友好的界面,以便用户上传水果图像并查看识别结果。 MATLAB提供了丰富的图像处理工具箱和机器学习工具箱,可以帮助开发者快速构建水果识别系统。通过结合这些工具箱,可以实现水果的快速、准确识别。

    COMSOL声子晶体仿真研究:一维至三维能带与带隙分析及色散曲线弹性波声波分析,声子晶体仿真:COMSOL代做能带图、带隙图及弹性波、声波分析与优化设计,COMSOL代做 声子晶体仿真,一维,二维,三

    COMSOL声子晶体仿真研究:一维至三维能带与带隙分析及色散曲线弹性波声波分析,声子晶体仿真:COMSOL代做能带图、带隙图及弹性波、声波分析与优化设计,COMSOL代做 声子晶体仿真,一维,二维,三维能带图,带隙图,色散曲线,弹性波,声波。 ,COMSOL代做;声子晶体仿真;一维/二维/三维能带图;带隙图;色散曲线;弹性波仿真;声波分析,COMSOL声子晶体仿真专家:一至三维声波模拟及能带图绘制

    Matlab Simulink仿真探究Flyback反激式开关电源性能表现与优化策略,Matlab Simulink仿真探究Flyback反激式开关电源的工作机制,Matlab Simulimk仿真

    Matlab Simulink仿真探究Flyback反激式开关电源性能表现与优化策略,Matlab Simulink仿真探究Flyback反激式开关电源的工作机制,Matlab Simulimk仿真,Flyback反激式开关电源仿真 ,Matlab; Simulink仿真; Flyback反激式; 开关电源仿真,Matlab Simulink在Flyback反激式开关电源仿真中的应用

    陪读租房系统(源码+数据库+论文+ppt)java开发springboot框架javaweb,可做计算机毕业设计或课程设计

    陪读租房系统(源码+数据库+论文+ppt)java开发springboot框架javaweb,可做计算机毕业设计或课程设计 【功能需求】 本系统有三个角色:管理员、租客和房主,要求具备以下功能: (a) 管理员;管理员使用本系统涉到的功能主要有:首页、个人中心、租客管理、房主管理、房源信息管理、房源类型管理、教育书籍管理、文章分类管理、租房信息管理、合同信息管理、在线咨询管理、咨阅回复管理、教育论坛、系统管理等功能。 (b) 租客;进入前台系统可以实现首页、房源信息、教育书籍、教育论坛、公告信息、后台管理等功能进行操作。 (C) 房主;进入系统可以实现首页、个人中心、房源信息管理、租房信息管理、合同信息管理、在线咨询管理、咨询回复管理等功能进行操作。 【环境需要】 1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境:IDEA,Eclipse,Myeclipse都可以。 3.tomcat环境:Tomcat 7.x,8.x,9.x版本均可 4.数据库:MySql 5.7/8.0等版本均可; 【购买须知】 本源码项目经过严格的调试,项目已确保无误,可直接用于课程实训或毕业设计提交。里面都有配套的运行环境软件,讲解视频,部署视频教程,一应俱全,可以自己按照教程导入运行。附有论文参考,使学习者能够快速掌握系统设计和实现的核心技术。

    vue3的一些语法以及知识点

    vue3的一些语法以及知识点

    libicu-doc-50.2-4.el7-7.x64-86.rpm.tar.gz

    1、文件内容:libicu-doc-50.2-4.el7_7.rpm以及相关依赖 2、文件形式:tar.gz压缩包 3、安装指令: #Step1、解压 tar -zxvf /mnt/data/output/libicu-doc-50.2-4.el7_7.tar.gz #Step2、进入解压后的目录,执行安装 sudo rpm -ivh *.rpm 4、更多资源/技术支持:公众号禅静编程坊

    水果销售商城(源码+数据库+论文+ppt)java开发springboot框架javaweb,可做计算机毕业设计或课程设计

    水果销售商城(源码+数据库+论文+ppt)java开发springboot框架javaweb,可做计算机毕业设计或课程设计 【功能需求】 水果购物网站用户可以注册登录,在首页开通会员卡,查看水果,购买水果,查看水果信息,以及个人中心修改个人资料,在自己的后台查看自己的购买记录等。 水果购物网站管理员功能:个人中心管理,用户管理,会员管理,会员卡管理,开通会员记录管理,积分管理,水果管理,购买水果订单管理,积分兑换管理,积分兑换记录管理,加积分记录管理,减积分记录管理。 【环境需要】 1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境:IDEA,Eclipse,Myeclipse都可以。 3.tomcat环境:Tomcat 7.x,8.x,9.x版本均可 4.数据库:MySql 5.7/8.0等版本均可; 【购买须知】 本源码项目经过严格的调试,项目已确保无误,可直接用于课程实训或毕业设计提交。里面都有配套的运行环境软件,讲解视频,部署视频教程,一应俱全,可以自己按照教程导入运行。附有论文参考,使学习者能够快速掌握系统设计和实现的核心技术。

    基于Matlab的双输入深度学习模型构建指南:处理序列与图像数据的创新性应用,Matlab双输入深度学习模型搭建指南:如何处理两种输入数据并实现创新与优势,Matlab搭建双输入深度学习模型,双输入网

    基于Matlab的双输入深度学习模型构建指南:处理序列与图像数据的创新性应用,Matlab双输入深度学习模型搭建指南:如何处理两种输入数据并实现创新与优势,Matlab搭建双输入深度学习模型,双输入网络。 相比普通的单输入网络,双输入网络能处理两种输入数据,在科研上也更具有优势和创新性。 如何用Matlab搭建双输入网络也是困扰本人很长时间的一个问题,现已弄明白。 注意,需要Matlab 2022b及以上版本,以下版本估计是都不行。 本程序是两个输入全为一维序列的情况(第二个输入序列是第一个输入序列的特征值,或者变后的序列)。 也可改为两边输入都是图像,或者一边输入图像,一边输入图像的一维特征序列。 本程序工作如下: 1、加载数据,两种输入数据一一对应,第二个数据是第一个数据做FFT之后的序列,属于一个类别。 两种数据样本数相等,序列长度不相等。 2、搭建双输入网络,此网络一边是CNN-LSTM,一边是CNN。 3、训练。 4、测试,输出准确率。 注:程序可直接运行,包教会和调通。 可以有偿修改为两边输入都是图像,或一边输入图像一边输入序列的模型。 可有偿替数据,调通程序。 程序注释详

    十大管理的49个过程组强化记忆

    包含十大管理49个过程组的输入与输出和解释,还有EVA铮值管理的公式汇总和解释

Global site tag (gtag.js) - Google Analytics