??xml version="1.0" encoding="utf-8" standalone="yes"?>
Ms SQL Server 2005 seems to go one step further by announcing future deprecation for ntext, text and image types.
From Sql Server 2005 notes:
“ntext, text, and image data types will be removed in a future version of Microsoft SQL Server. Avoid using these data types in new development work, and plan to modify applications that currently use them. Use nvarchar(max), varchar(max), and varbinary(max) instead.”
When working with Hibernate it seems there is no dialect to handle Unicode integration properly. You have to get down and write a custom dialect that maps to the new data types.
/**
* Unicode support in SQL Server
*
* @author icocan
*/
public class UnicodeSQLServerDialect extends SQLServerDialect {
public UnicodeSQLServerDialect() {
super();
// Use Unicode Characters
registerColumnType(Types.VARCHAR, 255, "nvarchar($l)");
registerColumnType(Types.CHAR, "nchar(1)");
registerColumnType(Types.CLOB, "nvarchar(max)");
// Microsoft SQL Server 2000 supports bigint and bit
registerColumnType(Types.BIGINT, "bigint");
registerColumnType(Types.BIT, "bit");
}
}
publicclassSQLServerNativeDialectextendsSQLServerDialect{
publicSQLServerNativeDialect(){
super();
registerColumnType(Types.VARCHAR,"nvarchar($l)");
registerColumnType(Types.CLOB,"nvarchar(max)");
}
publicString getTypeName(int code,int length,int precision,int scale)throwsHibernateException{
if(code !=2005){
returnsuper.getTypeName(code, length, precision, scale);
}else{
return"ntext";
}
}
}
This class maps Hibernate's types to SQL types, so the class will map the nvarchar(max) SQL Data Type to Hibernate's CLOB data type.
The getTypeName method is used to return "ntext" when Hibernate asks about the data type with code 2005 (which looks like it's the nvarchar(max) data type).
Finally, you need to change your hibernate persistence dialect to this new SQLServerDialect class, which allows hibernate to translate data types into SQL data types.
varchar[(n)]
长度? n 个字节的可变长度且非 Unicode 的字W数据。n
必须是一个介? 1 ? 8,000 之间的数倹{存储大ؓ(f)输入数据的字节的实际长度Q而不? n
个字节。所输入的数据字W长度可以ؓ(f)零。varchar ? SQL-92 中的同义词ؓ(f) char varying ?
character varying?
nvarchar(n)
包含 n
个字W的可变长度 Unicode 字符数据。n 的值必M? 1 ? 4,000
之间。字节的存储大小是所输入字符个数的两倍。所输入的数据字W长度可以ؓ(f)零。nvarchar ? SQL-92 中的同义词ؓ(f)
national char varying ? national character varying?nbsp;
通俗一點就是varchar適合輸入英文和數字,nvarchar一般用做中文或其它語言的入,這樣到別的語pM會出現亂?))
--如果是(f)时表可以?说明,如果用查扑֮表方法来打(f)时表?x)找不?发布区别对代.)
if object_id('tempdb..##temp') is not null
drop table ##temp
--判断存储q程是否存在
if exists(select 1 from sysobjects where id=object_id('所有?存储q程?) and
xtype='P')
print '存在'
else
print '不存?
--判断视图是否存在
--SQL Server 2000
IF EXISTS (SELECT * FROM sysviews WHERE object_id = '[dbo].[视图名]'
--SQL Server 2005
IF EXISTS (SELECT * FROM sys.views WHERE object_id = '[dbo].[视图名]'
/*
sysObjects (
Name sysname, --object 名称
id int, --object id
xtype char(2), -- object cd
type char(2), -- Object cdQ与xtype g一模一P 有点郁闷…Q?
uid smallint, -- object 所有者的ID
... --其他的字D不常用到?nbsp;
)
sysobjects的xtype 代表的对象类型。可以是下列对象cd中的一U:(x)
C = CHECK U束
D = 默认值或 DEFAULT U束
F = FOREIGN KEY U束
L = 日志
FN = 标量函数
IF = 内嵌表函?
P = 存储q程
PK = PRIMARY KEY U束Q类型是 KQ?
RF = 复制{选存储过E?
S = pȝ?
TF = 表函?
TR = 触发?
U = 用户?
UQ = UNIQUE U束Q类型是 KQ?
V = 视图
X = 扩展存储q程
object_id和data_object_id都是表示数据库对象的唯一标志?br />
object_id是数据库对象的逻辑idQdata_object_id是数据库对象的物理id?br />
如果一些object没有物理属性的话那它就不存在data_object_idQ例如procedure,function,package,data
type,db
link,mv定义Qview定义Q(f)时表Q分定义{等q些object都是没有对应着某个segmentQ因此它们的data_object_id
都ؓ(f)I?br />
当一个表建立的时候,他的object_id ?
data_object_id是相{的。当表move和truncate后data_object_id?x)发生变化。修改表l构不会(x)更改?br />
select object_id,data_object_id from user_objects where object_name=’T';
OBJECT_ID DATA_OBJECT_ID
——? ———?#8211;
63053 63464
SELECT HEADER_FILE,HEADER_BLOCK,BLOCKS FROM DBA_SEGMENTS WHERE
SEGMENT_NAME=’T’ AND OWNER=’TEST’;
HEADER_FILE HEADER_BLOCK BLOCKS
——?#8211; ———?——?
4 467 8
*/
SELECT * FROM sysobjects WHERE xtype='U' AND id=OBJECT_ID('Booking')
MySQLdb is an thread-compatible interface to the popular MySQL database server that provides the Python database API.
The README file has complete installation instructions.
If you want to write applications which are portable across databases, use MySQLdb, and avoid using this module directly. _mysql provides an interface which mostly implements the MySQL C API. For more information, see the MySQL documentation. The documentation for this module is intentionally weak because you probably should use the higher-level MySQLdb module. If you really need it, use the standard MySQL docs and transliterate as necessary.
The MySQL C API has been wrapped in an object-oriented way. The only MySQL data structures which are implemented are the MYSQL (database connection handle) and MYSQL_RES (result handle) types. In general, any function which takes MYSQL *mysql as an argument is now a method of the connection object, and any function which takes MYSQL_RES *result as an argument is a method of the result object. Functions requiring none of the MySQL data structures are implemented as functions in the module. Functions requiring one of the other MySQL data structures are generally not implemented. Deprecated functions are not implemented. In all cases, the mysql_ prefix is dropped from the name. Most of the conn methods listed are also available as MySQLdb Connection object methods. Their use is non-portable.
C API | _mysql |
---|---|
mysql_affected_rows() | conn.affected_rows() |
mysql_autocommit() | conn.autocommit() |
mysql_character_set_name() | conn.character_set_name() |
mysql_close() | conn.close() |
mysql_commit() | conn.commit() |
mysql_connect() | _mysql.connect() |
mysql_data_seek() | result.data_seek() |
mysql_debug() | _mysql.debug() |
mysql_dump_debug_info | conn.dump_debug_info() |
mysql_escape_string() | _mysql.escape_string() |
mysql_fetch_row() | result.fetch_row() |
mysql_get_character_set_info() | conn.get_character_set_info() |
mysql_get_client_info() | _mysql.get_client_info() |
mysql_get_host_info() | conn.get_host_info() |
mysql_get_proto_info() | conn.get_proto_info() |
mysql_get_server_info() | conn.get_server_info() |
mysql_info() | conn.info() |
mysql_insert_id() | conn.insert_id() |
mysql_num_fields() | result.num_fields() |
mysql_num_rows() | result.num_rows() |
mysql_options() | various options to _mysql.connect() |
mysql_ping() | conn.ping() |
mysql_query() | conn.query() |
mysql_real_connect() | _mysql.connect() |
mysql_real_query() | conn.query() |
mysql_real_escape_string() | conn.escape_string() |
mysql_rollback() | conn.rollback() |
mysql_row_seek() | result.row_seek() |
mysql_row_tell() | result.row_tell() |
mysql_select_db() | conn.select_db() |
mysql_set_character_set() | conn.set_character_set() |
mysql_ssl_set() | ssl option to _mysql.connect() |
mysql_stat() | conn.stat() |
mysql_store_result() | conn.store_result() |
mysql_thread_id() | conn.thread_id() |
mysql_thread_safe_client() | conn.thread_safe_client() |
mysql_use_result() | conn.use_result() |
mysql_warning_count() | conn.warning_count() |
CLIENT_* | MySQLdb.constants.CLIENT.* |
CR_* | MySQLdb.constants.CR.* |
ER_* | MySQLdb.constants.ER.* |
FIELD_TYPE_* | MySQLdb.constants.FIELD_TYPE.* |
FLAG_* | MySQLdb.constants.FLAG.* |
Okay, so you want to use _mysql anyway. Here are some examples.
The simplest possible database connection is:
import _mysql
db=_mysql.connect()
This creates a connection to the MySQL server running on the local machine using the standard UNIX socket (or named pipe on Windows), your login name (from the USER environment variable), no password, and does not USE a database. Chances are you need to supply more information.:
db=_mysql.connect("localhost","joebob","moonpie","thangs")
This creates a connection to the MySQL server running on the local machine via a UNIX socket (or named pipe), the user name "joebob", the password "moonpie", and selects the initial database "thangs".
We haven't even begun to touch upon all the parameters connect() can take. For this reason, I prefer to use keyword parameters:
db=_mysql.connect(host="localhost",user="joebob",
passwd="moonpie",db="thangs")
This does exactly what the last example did, but is arguably easier to read. But since the default host is "localhost", and if your login name really was "joebob", you could shorten it to this:
db=_mysql.connect(passwd="moonpie",db="thangs")
UNIX sockets and named pipes don't work over a network, so if you specify a host other than localhost, TCP will be used, and you can specify an odd port if you need to (the default port is 3306):
db=_mysql.connect(host="outhouse",port=3307,passwd="moonpie",db="thangs")
If you really had to, you could connect to the local host with TCP by specifying the full host name, or 127.0.0.1.
Generally speaking, putting passwords in your code is not such a good idea:
db=_mysql.connect(host="outhouse",db="thangs",read_default_file="~/.my.cnf")
This does what the previous example does, but gets the username and password and other parameters from ~/.my.cnf (UNIX-like systems). Read about option files for more details.
So now you have an open connection as db and want to do a query. Well, there are no cursors in MySQL, and no parameter substitution, so you have to pass a complete query string to db.query():
db.query("""SELECT spam, eggs, sausage FROM breakfast
WHERE price < 5""")
There's no return value from this, but exceptions can be raised. The exceptions are defined in a separate module, _mysql_exceptions, but _mysql exports them. Read DB API specification PEP-249 to find out what they are, or you can use the catch-all MySQLError.
At this point your query has been executed and you need to get the results. You have two options:
r=db.store_result()
# ...or...
r=db.use_result()
Both methods return a result object. What's the difference? store_result() returns the entire result set to the client immediately. If your result set is really large, this could be a problem. One way around this is to add a LIMIT clause to your query, to limit the number of rows returned. The other is to use use_result(), which keeps the result set in the server and sends it row-by-row when you fetch. This does, however, tie up server resources, and it ties up the connection: You cannot do any more queries until you have fetched all the rows. Generally I recommend using store_result() unless your result set is really huge and you can't use LIMIT for some reason.
Now, for actually getting real results:
>>> r.fetch_row()
(('3','2','0'),)
This might look a little odd. The first thing you should know is, fetch_row() takes some additional parameters. The first one is, how many rows (maxrows) should be returned. By default, it returns one row. It may return fewer rows than you asked for, but never more. If you set maxrows=0, it returns all rows of the result set. If you ever get an empty tuple back, you ran out of rows.
The second parameter (how) tells it how the row should be represented. By default, it is zero which means, return as a tuple. how=1 means, return it as a dictionary, where the keys are the column names, or table.column if there are two columns with the same name (say, from a join). how=2 means the same as how=1 except that the keys are always table.column; this is for compatibility with the old Mysqldb module.
OK, so why did we get a 1-tuple with a tuple inside? Because we implicitly asked for one row, since we didn't specify maxrows.
The other oddity is: Assuming these are numeric columns, why are they returned as strings? Because MySQL returns all data as strings and expects you to convert it yourself. This would be a real pain in the ass, but in fact, _mysql can do this for you. (And MySQLdb does do this for you.) To have automatic type conversion done, you need to create a type converter dictionary, and pass this to connect() as the conv keyword parameter.
The keys of conv should be MySQL column types, which in the C API are FIELD_TYPE_*. You can get these values like this:
from MySQLdb.constants import FIELD_TYPE
By default, any column type that can't be found in conv is returned as a string, which works for a lot of stuff. For our purposes, we probably want this:
my_conv = { FIELD_TYPE.LONG: int }
This means, if it's a FIELD_TYPE_LONG, call the builtin int() function on it. Note that FIELD_TYPE_LONG is an INTEGER column, which corresponds to a C long, which is also the type used for a normal Python integer. But beware: If it's really an UNSIGNED INTEGER column, this could cause overflows. For this reason, MySQLdb actually uses long() to do the conversion. But we'll ignore this potential problem for now.
Then if you use db=_mysql.connect(conv=my_conv...), the results will come back ((3, 2, 0),), which is what you would expect.
MySQLdb is a thin Python wrapper around _mysql which makes it compatible with the Python DB API interface (version 2). In reality, a fair amount of the code which implements the API is in _mysql for the sake of efficiency.
The DB API specification PEP-249 should be your primary guide for using this module. Only deviations from the spec and other database-dependent things will be documented here.
Only a few top-level functions and attributes are defined within MySQLdb.
Constructor for creating a connection to the database. Returns a Connection Object. Parameters are the same as for the MySQL C API. In addition, there are a few additional keywords that correspond to what you would pass mysql_options() before connecting. Note that some parameters must be specified as keyword arguments! The default value for each parameter is NULL or zero, as appropriate. Consult the MySQL documentation for more details. The important parameters are:
If True, CHAR and VARCHAR and TEXT columns are returned as Unicode strings, using the configured character set. It is best to set the default encoding in the server configuration, or client configuration (read with read_default_file). If you change the character set after connecting (MySQL-4.1 and later), you'll need to put the correct character set name in connection.charset.
If False, text-like columns are returned as normal strings, but you can always write Unicode strings.
This must be a keyword parameter.
If present, the connection character set will be changed to this character set, if they are not equal. Support for changing the character set requires MySQL-4.1 and later server; if the server is too old, UnsupportedError will be raised. This option implies use_unicode=True, but you can override this with use_unicode=False, though you probably shouldn't.
If not present, the default character set is used.
This must be a keyword parameter.
If present, the session SQL mode will be set to the given string. For more information on sql_mode, see the MySQL documentation. Only available for 4.1 and newer servers.
If not present, the session SQL mode will be unchanged.
This must be a keyword parameter.
Integer constant stating the level of thread safety the interface supports. This is set to 1, which means: Threads may share the module.
The MySQL protocol can not handle multiple threads using the same connection at once. Some earlier versions of MySQLdb utilized locking to achieve a threadsafety of 2. While this is not terribly hard to accomplish using the standard Cursor class (which uses mysql_store_result()), it is complicated by SSCursor (which uses mysql_use_result(); with the latter you must ensure all the rows have been read before another query can be executed. It is further complicated by the addition of transactions, since transactions start when a cursor execute a query, but end when COMMIT or ROLLBACK is executed by the Connection object. Two threads simply cannot share a connection while a transaction is in progress, in addition to not being able to share it during query execution. This excessively complicated the code to the point where it just isn't worth it.
The general upshot of this is: Don't share connections between threads. It's really not worth your effort or mine, and in the end, will probably hurt performance, since the MySQL server runs a separate thread for each connection. You can certainly do things like cache connections in a pool, and give those connections to one thread at a time. If you let two threads use a connection simultaneously, the MySQL client library will probably upchuck and die. You have been warned.
For threaded applications, try using a connection pool. This can be done using the Pool module.
String constant stating the type of parameter marker formatting expected by the interface. Set to 'format' = ANSI C printf format codes, e.g. '...WHERE name=%s'. If a mapping object is used for conn.execute(), then the interface actually uses 'pyformat' = Python extended format codes, e.g. '...WHERE name=%(name)s'. However, the API does not presently allow the specification of more than one style in paramstyle.
Note that any literal percent signs in the query string passed to execute() must be escaped, i.e. %%.
Parameter placeholders can only be used to insert column values. They can not be used for other parts of SQL, such as table names, statements, etc.
A dictionary or mapping which controls how types are converted from MySQL to Python and vice versa.
If the key is a MySQL type (from FIELD_TYPE.*), then the value can be either:
If the key is a Python type or class, then the value is a callable Python object (usually a function) taking two arguments (value to convert, and the conversion dictionary) which converts values of this type to a SQL literal string value.
This is initialized with reasonable defaults for most types. When creating a Connection object, you can pass your own type converter dictionary as a keyword parameter. Otherwise, it uses a copy of MySQLdb.converters.conversions. Several non-standard types are returned as strings, which is how MySQL returns all columns. For more details, see the built-in module documentation.
Connection objects are returned by the connect() function.
There are many more methods defined on the connection object which are MySQL-specific. For more information on them, consult the internal documentation using pydoc.
Calls stored procedure procname with the sequence of arguments in args. Returns the original arguments. Stored procedure support only works with MySQL-5.0 and newer.
Compatibility note: PEP-249 specifies that if there are OUT or INOUT parameters, the modified values are to be returned. This is not consistently possible with MySQL. Stored procedure arguments must be passed as server variables, and can only be returned with a SELECT statement. Since a stored procedure may return zero or more result sets, it is impossible for MySQLdb to determine if there are result sets to fetch before the modified parmeters are accessible.
The parameters are stored in the server as @_*procname*_*n*, where n is the position of the parameter. I.e., if you cursor.callproc('foo', (a, b, c)), the parameters will be accessible by a SELECT statement as @_foo_0, @_foo_1, and @_foo_2.
Compatibility note: It appears that the mere act of executing the CALL statement produces an empty result set, which appears after any result sets which might be generated by the stored procedure. Thus, you will always need to use nextset() to advance result sets.
Advances the cursor to the next result set, discarding the remaining rows in the current result set. If there are no additional result sets, it returns None; otherwise it returns a true value.
Note that MySQL doesn't support multiple result sets until 4.1.
The connect() method works nearly the same as with _mysql:
import MySQLdb
db=MySQLdb.connect(passwd="moonpie",db="thangs")
To perform a query, you first need a cursor, and then you can execute queries on it:
c=db.cursor()
max_price=5
c.execute("""SELECT spam, eggs, sausage FROM breakfast
WHERE price < %s""", (max_price,))
In this example, max_price=5 Why, then, use %s in the string? Because MySQLdb will convert it to a SQL literal value, which is the string '5'. When it's finished, the query will actually say, "...WHERE price < 5".
Why the tuple? Because the DB API requires you to pass in any parameters as a sequence. Due to the design of the parser, (max_price) is interpreted as using algebraic grouping and simply as max_price and not a tuple. Adding a comma, i.e. (max_price,) forces it to make a tuple.
And now, the results:
>>> c.fetchone()
(3L, 2L, 0L)
Quite unlike the _mysql example, this returns a single tuple, which is the row, and the values are properly converted by default... except... What's with the L's?
As mentioned earlier, while MySQL's INTEGER column translates perfectly into a Python integer, UNSIGNED INTEGER could overflow, so these values are converted to Python long integers instead.
If you wanted more rows, you could use c.fetchmany(n) or c.fetchall(). These do exactly what you think they do. On c.fetchmany(n), the n is optional and defaults to c.arraysize, which is normally 1. Both of these methods return a sequence of rows, or an empty sequence if there are no more rows. If you use a weird cursor class, the rows themselves might not be tuples.
Note that in contrast to the above, c.fetchone() returns None when there are no more rows to fetch.
The only other method you are very likely to use is when you have to do a multi-row insert:
c.executemany(
"""INSERT INTO breakfast (name, spam, eggs, sausage, price)
VALUES (%s, %s, %s, %s, %s)""",
[
("Spam and Sausage Lover's Plate", 5, 1, 8, 7.95 ),
("Not So Much Spam Plate", 3, 2, 0, 3.95 ),
("Don't Wany ANY SPAM! Plate", 0, 4, 3, 5.95 )
] )
Here we are inserting three rows of five values. Notice that there is a mix of types (strings, ints, floats) though we still only use %s. And also note that we only included format strings for one row. MySQLdb picks those out and duplicates them for each row.
In general, it is probably wise to not directly interact with the DB API except for small applicatons. Databases, even SQL databases, vary widely in capabilities and may have non-standard features. The DB API does a good job of providing a reasonably portable interface but some methods are non-portable. Specifically, the parameters accepted by connect() are completely implementation-dependent.
If you believe your application may need to run on several different databases, the author recommends the following approach, based on personal experience: Write a simplified API for your application which implements the specific queries and operations your application needs to perform. Implement this API as a base class which should be have few database dependencies, and then derive a subclass from this which implements the necessary dependencies. In this way, porting your application to a new database should be a relatively simple matter of creating a new subclass, assuming the new database is reasonably standard.
Because MySQLdb's Connection and Cursor objects are written in Python, you can easily derive your own subclasses. There are several Cursor classes in MySQLdb.cursors:
CursorDictRowsMixIn
Causes the cursor to return rows as a dictionary, where the keys are column names and the values are column values. Note that if the column names are not unique, i.e., you are selecting from two tables that share column names, some of them will be rewritten as table.column. This can be avoided by using the SQL AS keyword. (This is yet-another reason not to use * in SQL queries, particularly where JOIN is involved.)
Instead of connecting to a stand-alone server over the network, the embedded server support lets you run a full server right in your Python code or application server.
If you have built MySQLdb with embedded server support, there are two additional functions you will need to make use of:
- server_init(args, groups)
Initialize embedded server. If this client is not linked against the embedded server library, this function does nothing.
- args
- sequence of command-line arguments
- groups
- sequence of groups to use in defaults files
- server_end()
- Shut down embedded server. If not using an embedded server, this does nothing.
See the MySQL documentation for more information on the embedded server.
Title: | MySQLdb: a Python interface for MySQL |
---|---|
Author: | Andy Dustman |
Version: | $Revision: 421 $ |
1、下载MySQL 5.x 发行?Q解压ƈ安装映像中的两个安装包文件?/p>
a. mysql-5.x-osx10.6_x86_64.pkg Qmysql 5.x 标准版安?/p>
b. MySQLStartupItem.pkgQmysql启动目Q可以上你的?sh)脑在启动系l时自动q行mysql服务。它安装?Library /StartupItems/MySQL/Q如果你不想pȝ启动时运行mysql服务Q请不要安装。如果你在安装后又不想用,请删?Library /StartupItems/MySQL/q个目录?/p>
启动mysqlQ?/p>
2、如果你已经安装?jin)MySQLStartupItem.pkgQ重新启动电(sh)脑即可?/p>
3、如果你有安装MySQLStartupItem.pkg或者不惛_动电(sh)脑,q行Q应用程序-实用E序Q终端程序,在终端中输入命o(h)Q?/p>
sudo /Library/StartupItems/MySQL/MySQL start
然后输入你的pȝ理员密码,如果没有讑֮密码q接回车?/p>
关闭mysql服务Q?/p>
l端中输入命令:(x)sudo /Library/StartupItems/MySQL/MySQL stop
然后输入你的pȝ理员密码,如果没有讑֮密码q接回车?/p>
mysql root账户密码Q?/p>
mysql root密码初始值是I。这栯然没有问题。但很不安全。徏议你更改root用户密码。注意:(x)mysql root用户和系l中的root用户是不一L(fng)。是完全两个不同的用戗?/p>
更改mysql root密码请在l端中输入命令:(x)
/usr/local/mysql/bin/mysqladmin -u root password 新密?/p>
同时你也可以随时使用q条命o(h)更改你的密码?/p>
4、下载x版mysql数据库管理工?/p>
q是一个运行在mac os
xpȝ中的mysql数据库管理YӞ支持本地?qing)远E数据库理。ƈ且还是免费的。这个程序的优点是完全CGI界面。ƈ且密码是保存在本Z的。相Ҏ(gu)?
安全?/p>
Spring提供?jin)两个这L(fng)数据源(都位于org.springframework.jdbc.datasourceE序包里Q:(x)
DriverManagerDataSourceQ在每个q接h旉新徏一个连接。与DBCP的BasicDataSource不同QDriverManagerDataSource提供的连接没有进行池理?br />
SingleConnectionDataSourceQ在每个q接h旉q回同一个连接。虽然它不同严格意义上的池管理数据源Q但我们可以把它看作只有一个连接的池?br />
对两个数据源的配|都cM于配|DBCP的BasicDataSource
区别在于׃DriverManagerDataSource和SingleConnectionDataSource都没有提供连接池Q所以在此没有设|池配置属性?br />
虽然q两个数据源都对于小E序来说是很不错的,而且q在不断发展Q但把它们用于生产程序还是需要认真考虑的?/p>
SingleConnectionDataSource只用一个数据库q接Q所以不适合用于多线E程序。? DriverMangerDataSource虽然能够支持多线E,但它?x)在每次q接h旉新徏一个连接,q是以性能ZL(fng)。由于这些限Ӟ我们强烈 应该使用数据源池?/p>
在通过数据源与数据库徏立连接之后,我们p实际讉K数据库了(jin)Q而最基本的方式就是用JDBCQ现在我们就来看一看Spring如何让用简单的JDBC更加ѝ?br /> Spring在第三方依赖包中包含?jin)两个数据源的实现类包,其一是Apache的DBCPQ其二是 C3P0。可以在Spring配置文g中利用这两者中M一个配|数据源?/p>
DBCP数据?
DBCP cd位于
/lib/jakarta-commons/commons-dbcp.jarQDBCP是一个依?Jakarta commons-
pool对象池机制的数据库连接池Q所以在c\径下q必d?lib/jakarta-
commons/commons-pool.jar。下面是?用DBCP配置MySql数据源的配置片断Q?/p>
xml 代码
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3309/sampledb" />
<property name="username" value="root" />
<property name="password" value="1234" />
bean>
BasicDataSource提供?jin)close()Ҏ(gu)关闭数据源,所以必设定destroy-method=”close”属性, 以便Spring容器关闭Ӟ数据源能够正常关闭。除以上必须的数据源属性外Q还有一些常用的属性:(x)
defaultAutoCommitQ设|从数据源中q回的连接是否采用自动提交机Ӟ默认gؓ(f) trueQ?
defaultReadOnlyQ设|数据源是否仅能执行只读操作Q?默认gؓ(f) falseQ?
maxActiveQ最大连接数据库q接敎ͼ讄?Ӟ表示没有限制Q?
maxIdleQ最大等待连接中的数量,讄?Ӟ表示没有限制Q?
maxWaitQ最大等待秒敎ͼ单位为毫U, 过旉?x)报出错误信息?
validationQueryQ用于验证连接是否成功的查询SQL语句QSQL语句必须臛_要返回一行数据, 如你可以单地讄为:(x)“select count(*) from user”Q?
removeAbandonedQ是否自我中断,默认?false Q?
removeAbandonedTimeoutQ几U后数据q接?x)自动断开Q在removeAbandoned为trueQ提供该|
logAbandonedQ是否记录中断事Ӟ 默认?falseQ?
C3P0数据?
C3P0
是一个开放源代码的JDBC数据源实现项目,它在lib目录中与Hibernate一起发布,实现?jin)JDBC3和JDBC2扩展规范说明?
Connection 和Statement 池。C3P0cd位于/lib/c3p0/c3p0-0.9.0.4.jar。下面是使用C3P0配置一
?oracle数据源:(x)
xml 代码
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value=" oracle.jdbc.driver.OracleDriver "/>
<property name="jdbcUrl" value=" jdbc:oracle:thin:@localhost:1521:ora9i "/>
<property name="user" value="admin"/>
<property name="password" value="1234"/>
bean>
ComboPooledDataSource和BasicDataSource一h供了(jin)一个用于关闭数据源的close()Ҏ(gu)Q这h们就可以保证Spring容器关闭时数据源能够成功释放?
C3P0拥有比DBCP更丰富的配置属性,通过q些属性,可以Ҏ(gu)据源q行各种有效的控Ӟ(x)
acquireIncrementQ当q接池中的连接用完时QC3P0一ơ性创建新q接的数目;
acquireRetryAttemptsQ定义在从数据库获取新连接失败后重复试获取的次敎ͼ默认?0Q?
acquireRetryDelayQ两ơ连接中间隔旉Q单位毫U,默认?000Q?
autoCommitOnCloseQ连接关闭时默认所有未提交的操作回滚。默认ؓ(f)falseQ?
automaticTestTableQ?
C3P0徏一张名为Test的空表,q用其自带的查询语句进行测试。如果定义了(jin)q个参数Q那么属性preferredTestQuery被忽略?
?不能在这张Test表上q行M操作Q它?yu)中为C3P0试所用,默认为nullQ?
breakAfterAcquireFailureQ?获取q接p|会(x)引v所有等待获取连接的U程抛出异常。但是数据源仍有效保留,q在下次?nbsp;
用getConnection()的时候l尝试获取连 接。如果设为trueQ那么在试获取q接p|后该数据源将x(chng)已断开q永久关闭。默认ؓ(f)
falseQ?
checkoutTimeoutQ当q接池用完时客户端调用getConnection()后等待获取新q接的时_(d)时后将抛出SQLExceptionQ如设ؓ(f)0则无限期{待。单位毫U,默认?Q?
connectionTesterClassNameQ?
通过实现ConnectionTester或QueryConnectionTester的类来测试连接,cd需讄为全限定名。默认ؓ(f)
com.mchange.v2.C3P0.impl.DefaultConnectionTesterQ?
idleConnectionTestPeriodQ隔多少U检查所有连接池中的I闲q接Q默认ؓ(f)0表示不检查;
initialPoolSizeQ初始化时创建的q接敎ͼ应在minPoolSize与maxPoolSize之间取倹{默认ؓ(f)3Q?
maxIdleTimeQ最大空闲时_(d)过I闲旉的连接将被丢弃。ؓ(f)0或负数则怸丢弃。默认ؓ(f)0Q?
maxPoolSizeQ连接池中保留的最大连接数。默认ؓ(f)15Q?
maxStatementsQ?
JDBC的标准参敎ͼ用以控制数据源内加蝲的PreparedStatement数量。但׃预缓存的Statement?
于单个Connection 而不是整个连接池。所以设|这个参数需要考虑到多斚w的因素,如果maxStatements?
maxStatementsPerConnection 均ؓ(f)0Q则~存被关闭。默认ؓ(f)0Q?
maxStatementsPerConnectionQ连接池内单个连接所拥有的最大缓存Statement数。默认ؓ(f)0Q?
numHelperThreadsQC3P0是异步操作的Q缓慢的JDBC操作通过帮助q程完成。扩展这些操作可以有效的提升性能Q通过多线E实现多个操作同时被执行。默认ؓ(f)3Q?
preferredTestQueryQ定义所有连接测试都执行的测试语句。在使用q接试的情况下q个参数能显著提高测试速度。测试的表必d初始数据源的时候就存在。默认ؓ(f)nullQ?
propertyCycleQ?用户修改pȝ配置参数执行前最多等待的U数。默认ؓ(f)300Q?
testConnectionOnCheckoutQ?
因性能消耗大请只在需要的时候用它。如果设为true那么在每个connection提交的时候都 校验其有效性。徏议?
idleConnectionTestPeriod或automaticTestTable
{方法来提升q接试的性能。默认ؓ(f)falseQ?
testConnectionOnCheckinQ如果设为true那么在取得连接的同时校验连接的有效性。默认ؓ(f)false?
读配|文件的方式引用属性:(x)
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="/WEB-INF/jdbc.properties"/>
bean>
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
bean>
在jdbc.properties属性文件中定义属性|(x)
jdbc.driverClassName= com.mysql.jdbc.Driver
jdbc.url= jdbc:mysql://localhost:3309/sampledb
jdbc.username=root
jdbc.password=1234
提示 l常有开发者在${xxx}的前后不心(j)键入一些空|q些I格字符和变量合ƈ后作为属性的倹{如Q?的属性配|项Q在前后都有I格Q被解析后,username的gؓ(f)“ 1234 ”Q这造成最l的错误Q因此需要特别小?j)?/p>
获取JNDI数据?
如果应用配置在高性能的应用服务器Q如WebLogic或Websphere{)(j)上,我们可能更希望用应用服务器本n提供的数据源。应用服务器的数据源
使用JNDI开放调用者用,Spring为此专门提供引用JNDI资源的JndiObjectFactoryBeancR下面是一个简单的配置Q?/p>
xml 代码
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="java:comp/env/jdbc/bbt"/>
bean>
通过jndiName指定引用的JNDI数据源名U?
Spring 2.0取J2EE资源提供?jin)一个jee命名I间Q通过jee命名I间Q可以有效地化J2EE资源的引用。下面是使用jee命名I间引用JNDI数据源的配置Q?
xml 代码
<beans xmlns=http://www.springframework.org/schema/beans
xmlns:xsi=http://www.w3.org/2001/XMLSchema-instance
xmlns:jee=http://www.springframework.org/schema/jee
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/jee
<jee:jndi-lookup id="dataSource" jndi-name=" java:comp/env/jdbc/bbt"/>
beans>
Spring的数据源实现c?
Spring
本n也提供了(jin)一个简单的数据源实现类DriverManagerDataSource Q它位于
org.springframework.jdbc.datasource包中。这个类实现?jin)javax.sql.DataSource接口Q但
它ƈ?
有提供池化连接的机制Q每ơ调用getConnection()获取新连接时Q只是简单地创徏一个新的连接。因此,q个数据源类比较适合在单元测?
或简 单的独立应用中用,因ؓ(f)它不需要额外的依赖cR?
下面Q我们来看一下DriverManagerDataSource的简单用:(x)当然Q我们也可以通过配置的方式直接用DriverManagerDataSource?/p>
java 代码
DriverManagerDataSource ds = new DriverManagerDataSource ();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3309/sampledb");
ds.setUsername("root");
ds.setPassword("1234");
Connection actualCon = ds.getConnection();
结
不管采用何种持久化技术,都需要定义数据源。Spring附带?jin)两个数据源的实现类包,你可以自行选择q行定义。在实际部vӞ我们可能?x)直接采用应用? 务器本n提供的数据源Q这Ӟ则可以通过JndiObjectFactoryBean或jee命名I间引用JNDI中的数据源?
DBCP与C3PO配置的区别:(x)
C3PO Q?/p>
xml 代码
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass">
<value>oracle.jdbc.driver.OracleDrivervalue>
property>
<property name="jdbcUrl">
<value>jdbc:oracle:thin:@10.10.10.6:1521:DataBaseNamevalue>
property>
<property name="user">
<value>testAdminvalue>
property>
<property name="password">
<value>123456value>
property>
bean>
DBCPQ?/p>
xml 代码
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName">
<value>oracle.jdbc.driver.OracleDrivervalue>
property>
<property name="url">
<value>jdbc:oracle:thin:@10.10.10.6:1521:DataBaseNamevalue>
property>
<property name="username">
<value>testAdminvalue>
property>
<property name="password">
<value>123456value>
property>
bean>
再D个简单的例子
with a as (select * from test)
select * from a;
其实是把一大堆重复用到的SQL语句攑֜with as 里面Q取一个别名,后面的查询就可以用它
q样对于大批量的SQL语句起到一个优化的作用Q而且清楚明了(jin)
The SQL-99 “WITH clause” is very confusing at first because the SQL statement does not begin with the word SELECT. Instead, we use the “WITH clause” to start our SQL query, defining the aggregations, which can then be named in the main query as if they were “real” tables:
WITH
subquery_name
AS
(the aggregation SQL statement)
SELECT
(query naming subquery_name);
@Entity
@Table(name = "customer")
public class CustomerEO implements java.io.Serializable {
……
private CustomerType type;
@Enumerated(EnumType.STRING)
public CustomerType getType() {
return type;
}
public void setType(CustomerType type) {
this.type = type;
}
public enum CustomerType {
COMPETITOR, INVESTOR, PARTNER, VENDER
}
}
在实体中虽然标注成枚丄型,但当实体持久化后Q表中所对应的g旧是基本的数据类型,以上代码创徏表的SQL语句是:(x)
CREATE TABLE customer (
id int(20) NOT NULL auto_increment,
name varchar(255),
type varchar(255),
PRIMARY KEY (id)
)
使用枚Dcd后,在创建实体时便可以直接引用枚丄型,例如以下代码所C?/font>
CustomerEO customer = new CustomerEO();
customer.setName("Janet2");
customer.setType(CustomerType.PARTNER);
entityManager.persist(customer);
在?/span>@Enumerated注释Ӟ需要注意以下几个问题:(x)
l 因ؓ(f)枚Dcd的有名称和g个属性,所以在持久化时可以选择持久化名U或是持久化倹{通过EnumType来定义,它有两个值如下所C?/span>
public enum EnumType {
ORDINAL,
STRING
}
ORDINAL表示持久化的为枚丄型的|STRING表示持久化的为枚丄型的名称。默认ؓ(f)ORDINALQ持久化倹{例如以上示例中标注的ؓ(f)STRINGQ这h久化实体后,数据库中保存的是枚Dcd的名Uͼ如图所C?/span>
若此时改?/span>ORDINALQ代码如下:(x)
@Enumerated(EnumType.ORDINAL)
public CustomerType getType() {
return type;
}
则同h久化的实体后Q数据库保存的结果如?/span>所C?/span>
l 如何选择STRING?/span>ORDINALQ?/span>
如果使用STRING保存Q虽然从数据库中查询数据旉常直观,能够清楚的看?gu)cd代表意义Q但q样也会(x)带来其他的问题。若此时枚Dcd的定义改变,例如上例中的枚Dcd名称改ؓ(f)Q?/span>
public enum CustomerType {
CUST_COMPETITOR, INVESTOR, PARTNER, VENDER
}
则此时数据库中保存的“COMPETITOR”的值将不能转化为枚丄?/span>CustomerType中的“CUST_COMPETITOR”的倹{但若?/span>ORDINAL则不?x)带来这U问题。所以徏议?/span>ORDINALcd来持久化枚Dcd?/span>
l 枚Dcd的定义位|,实体外部VS实体内部?/span>
上例?/span>CustomerType枚Dcd定义?/span>CustomerEO实体内部Q这是因为只?/span>CustomerEOq个实体?x)?/span>CustomerTypecdQ其他的实体不会(x)使用该类型。该cd与这个实体关pȝ密联pR?/span>
但若此时多个实体公用一个枚丄型时Q则可以枚丄型单独定义,定义在实体的外部。有q样一个枚丄?/span>BusinessLineQ它定义在实体外部,代码如下Q?/span>
public enum BusinessLine {
REAL_ESTATE,FINANCE, NON_PROFIT
}
例如CustomerEO实体增加一?/span>BusinessLine的枚丄型,代码如下所C?/span>
@Entity
@Table(name = "customer")
public class CustomerEO implements java.io.Serializable {
……
private BusinessLine businessLine;
@Enumerated(EnumType.STRING)
public BusinessLine getBusinessLine() {
return businessLine;
}
public void setBusinessLine(BusinessLine businessLine) {
this.businessLine = businessLine;
}
}
import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.Date;
/*
* 该类只能创徏一个实例,其它对象能够调用光(rn)态方法(也称为类Ҏ(gu)Q获得该唯一实例的引用?br />
* DBConnectionManagercȝ建构函数是私有的Q这是ؓ(f)?jin)避免其它对象创cȝ实例.
* DBConnectionManagercȝ客户E序可以调用getInstance()Ҏ(gu)获得对该cd一实例的引用?br />
* cȝ唯一实例在getInstance()Ҏ(gu)W一ơ被调用期间创徏Q此后其引用׃直保存在?rn)态变?br />
* instance中。每ơ调用getInstance()都增加一个DBConnectionManager的客L(fng)序计数?br />
* 卻I该计C表引用DBConnectionManager唯一实例的客L(fng)序LQ它?yu)被用于控制q接池的
* 关闭操作?该类实例的初始化工作U有Ҏ(gu)init()完成。其?getResourceAsStream()Ҏ(gu)
* 用于定位q打开外部文g。外部文件的定位Ҏ(gu)依赖于类装蝲器的实现。标准的本地c装载器查找?br />
* 作L开始于cL件所在\径,也能够搜索CLASSPATH中声明的路径。db.properties是一个属?br />
* 文gQ它包含定义q接池的?值对。可供定义的公用属性如下:(x)
* drivers 以空格分隔的JDBC驱动E序cd?"
* logfile 日志文g的绝对\?
* 其它的属性和特定q接池相养I其属性名字前应加上连接池名字Q?
* < poolname>.url 数据库的 JDBC URL
* < poolname>.maxconn 允许建立的最大连接数Q?表示没有限制
* < poolname>.user 用于该连接池的数据库帐号
* < poolname>.password 相应的密?"
* 其中url属性是必需的,而其它属性则是可选的。数据库帐号和密码必d法。用于Windowsq_?br />
* db.properties文gCZ如下Q?br />
* drivers=com.microsoft.jdbc.sqlserver.SQLServerDriver
* logfile=D:""log.txt
* access.maxconn=20
* access.url=jdbc:microsoft:sqlserver://localhost:1433;databasename=web
* access.user=sa
* access.password=sa
* 注意在Windows路径中的反斜杠必输?个,q是׃属性文件中的反斜杠同时也是一个{义字W?br />
* init()Ҏ(gu)在创建属性对象ƈddb.properties文g之后Q就开始检查logfile属性。如果属
* 性文件中没有指定日志文gQ则默认为当前目录下的DBConnectionManager.log文g。如日志?br />
* 件无法用,则向System.err输出日志记录。装载和注册所有在drivers属性中指定的JDBC驱动
* E序loadDrivers()Ҏ(gu)实现。该Ҏ(gu)先用StringTokenizerdrivers属性值分割ؓ(f)对应于驱
* 动程序名U的字符Ԍ然后依次装蝲q些cdƈ创徏其实例,最后在DriverManager中注册该实例q把
* 它加入到一个私有的向量drivers。向量drivers用于关闭服务时从DriverManager取消所?br />
* JDBC 驱动E序的注册。init()Ҏ(gu)的最后一个Q务是调用U有Ҏ(gu)createPools()创徏q接池对
* 象。createPools()Ҏ(gu)先创建所有属性名字的枚D对象Q即Enumeration对象Q该对象可以惌
* Z个元素系列,逐次调用其nextElement()Ҏ(gu)顺序返回各元素Q,然后在其中搜索名字以“.url”
* l尾的属性。对于每一个符合条件的属性,先提取其q接池名字部分,q而读取所有属于该q接池的属性,
* 最后创接池对象q把它保存在实例变量pools中。散列表QHashtablec?Qpools实现q接池名?br />
* 到连接池对象之间的映,此处以连接池名字为键Q连接池对象为倹{?Z于客L(fng)序从指定q接池获
* 得可用连接或连接返回给q接池,cDBConnectionManager提供?jin)方法getConnection()?br />
* freeConnection()。所有这些方法都要求在参C指定q接池名字,具体的连接获取或q回操作则调
* 用对应的q接池对象完成。ؓ(f)实现q接池的安全关闭QDBConnectionManager提供?jin)方法r(sh)elease()?br />
* 在上面我们已l提刎ͼ所有DBConnectionManager的客L(fng)序都应该调用?rn)态方法getInstance()
* 以获得该理器的引用Q此调用增加客L(fng)序计数。客L(fng)序在关闭时调用release()可以递减该计数?br />
* 当最后一个客L(fng)序调用release()Q递减后的引用计数?Q就可以调用各个q接池的release()Ҏ(gu)
* 关闭所有连接了(jin)。管理类release()Ҏ(gu)最后的d是撤销所有JDBC驱动E序的注册?br />
*/
/**
* 理cDBConnectionManager支持对一个或多个由属性文件定义的数据库连?br />
* 池的讉K.客户E序可以调用getInstance()Ҏ(gu)讉K本类的唯一实例.
*/
public class DBConnectionManager {
static private DBConnectionManager instance; // 唯一实例
static private int clients;
private Vector drivers = new Vector();
private PrintWriter log;
private Hashtable pools = new Hashtable();
/**
* q回唯一实例.如果是第一ơ调用此Ҏ(gu),则创建实?br />
* @return DBConnectionManager 唯一实例
*/
static synchronized public DBConnectionManager getInstance() {
if (instance == null) {
instance = new DBConnectionManager();
}
clients++;
return instance;
}
/**
* 建构函数U有以防止其它对象创建本cd?br />
*/
private DBConnectionManager() {
init();
}
/**
* * 连接对象返回给由名字指定的q接?br />
* @param name在属性文件中定义的连接池名字
* @param conq接对象""""r
*/
public void freeConnection(String name, Connection con) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
pool.freeConnection(con);
}
}
/**
* 获得一个可用的(I闲?q接.如果没有可用q接,且已有连接数于最大连接数 053 * 限制,则创建ƈq回新连
* @param name在属性文件中定义的连接池名字 056 *
* @return Connection 可用q接或null 057
*/
public Connection getConnection(String name) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
return pool.getConnection();
}
return null;
}
/**
* 获得一个可用连?若没有可用连?且已有连接数于最大连接数限制,
* 则创建ƈq回新连?否则,在指定的旉内等待其它线E释放连?
* @param name q接池名?071 *
* @param time以毫U计的等待时?a>""""r
* @return Connection 可用q接或null
*/
public Connection getConnection(String name, long time) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
return pool.getConnection(time);
}
return null;
}
/**
* 关闭所有连?撤销驱动E序的注?a>""""r
*/
public synchronized void release() {
// {待直到最后一个客L(fng)序调?br />
if (--clients != 0) {
return;
}
Enumeration allPools = pools.elements();
while (allPools.hasMoreElements()) {
DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();
pool.release();
}
Enumeration allDrivers = drivers.elements();
while (allDrivers.hasMoreElements()) {
Driver driver = (Driver) allDrivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
log("撤销JDBC驱动E序 " + driver.getClass().getName() + "的注?);
} catch (SQLException e) {
log(e, "无法撤销下列JDBC驱动E序的注? " + driver.getClass().getName());
}
}
}
/**
* Ҏ(gu)指定属性创接池实例.
* @param props q接池属?113
*/
private void createPools(Properties props) {
Enumeration propNames = props.propertyNames();
while (propNames.hasMoreElements()) {
String name = (String) propNames.nextElement();
if (name.endsWith(".url")) {
String poolName = name.substring(0, name.lastIndexOf("."));
String url = props.getProperty(poolName + ".url");
if (url == null) {
log("没有接池" + poolName + "指定URL");
continue;
}
String user = props.getProperty(poolName + ".user");
String password = props.getProperty(poolName + ".password");
String maxconn = props.getProperty(poolName + ".maxconn", "0");
int max;
try {
max = Integer.valueOf(maxconn).intValue();
} catch (NumberFormatException e) {
log("错误的最大连接数限制: " + maxconn + " .q接? " + poolName);
max = 0;
}
DBConnectionPool pool = new DBConnectionPool(poolName, url,
user, password, max);
pools.put(poolName, pool);
log("成功创徏q接? + poolName);
}
}
}
/**
* d属性完成初始化
*/
private void init() {
InputStream is = getClass().getResourceAsStream("/db.properties");
Properties dbProps = new Properties();
try {
dbProps.load(is);
} catch (Exception e) {
System.err.println("不能d属性文? "+"L(fng)保db.properties在CLASSPATH指定的\径中");
return;
}
String logFile = dbProps.getProperty("logfile","DBConnectionManager.log");
try {
log = new PrintWriter(new FileWriter(logFile, true), true);
} catch (IOException e) {
System.err.println("无法打开日志文g: " + logFile);
log = new PrintWriter(System.err);
}
loadDrivers(dbProps);
createPools(dbProps);
}
/**
* 装蝲和注册所有JDBC驱动E序
* @param props属?br />
*/
private void loadDrivers(Properties props) {
String driverClasses = props.getProperty("drivers");
StringTokenizer st = new StringTokenizer(driverClasses);
while (st.hasMoreElements()) {
String driverClassName = st.nextToken().trim();
try {
Driver driver = (Driver) Class.forName(driverClassName).newInstance();
DriverManager.registerDriver(driver);
drivers.addElement(driver);
log("成功注册JDBC驱动E序" + driverClassName);
} catch (Exception e) {
e.printStackTrace();
log("无法注册JDBC驱动E序: " + driverClassName + ", 错误: " + e);
}
}
}
/**
* 文本信息写入日志文?br />
*/
private void log(String msg) {
log.println(new Date() + ": " + msg);
}
/**
* 文本信息与异常写入日志文g
*/
private void log(Throwable e, String msg) {
log.println(new Date() + ": " + msg);
e.printStackTrace(log);
}
// ///////////////////////////////////////////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////////////////////////////
/*
* DBConnectionPool实现Q它表示指向某个数据库的q接池。数据库由JDBC URL标识。一个JDBCURL׃部分l成Q协议标识(LjdbcQ,
* 驱动E序标识Q如odbc、idb、oracle{)(j)Q数据库标识Q其格式依赖于驱动程序)(j)。例如,jdbc:odbc:demoQ即是一个指向demo数据
* 库的JDBCURLQ而且讉K该数据库要用JDBC-ODBC驱动E序。每个连接池都有一个供客户E序使用的名字以?qing)可选的用户帐号、密码、最
* 大连接数限制。如果Web应用E序所支持的某些数据库操作可以被所有用h行,而其它一些操作应q别许可的用户执行Q则可以ZcL?br />
* 分别定义q接池,两个q接池用相同的JDBC URLQ但使用不同的帐号和密码。类DBConnectionPool的徏构函数需要上q所有数据作为其
* 参数。客L(fng)序可以用DBConnectionPool
* cL供的两个Ҏ(gu)获取可用q接。两者的共同之处在于Q如q接池中存在可用q接Q则直接q回Q否则创建新的连接ƈq回。如果没有可用连?br />
* 且已有连接L{于最大限制数Q第一个方法将直接q回nullQ而第二个Ҏ(gu)等待直到有可用q接为止。所有的可用q接对象均登记在名ؓ(f)
* freeConnections的向量(VectorQ中。如果向量中有多于一个的q接QgetConnection()L选取W一个。同Ӟ׃新的可用q接?br />
* 是从N加入向量Q从而得数据库q接׃长时间闲|而被关闭的风险减低到最程度?W一个getConnection()在返回可用连接给客户
* E序之前Q调用了(jin)isClosed()Ҏ(gu)验证q接仍旧有效。如果该q接被关闭或触发异常QgetConnection()递归地调用自׃试获取另外?br />
* 可用q接。如果在向量freeConnections中不存在M可用q接QgetConnection()Ҏ(gu)(g)查是否已l指定最大连接数限制。如已经指定Q?br />
* 则检查当前连接数是否已经到达极限。此处maxConn?表示没有限制。如果没有指定最大连接数限制或当前连接数于该|该方法尝试创?br />
* 新的q接。如创徏成功Q则增加已用连接的计数q返回,否则q回I倹{创建新q接由newConnection()Ҏ(gu)实现?br />
* 创徏q程与是否已l指定数据库帐号、密码有兟뀂JDBC的DriverManagercL供多个getConnection()Ҏ(gu)Q这些方法要用到JDBC URL
* 与其它一些参敎ͼ如用户帐号和密码{。DriverManager用指定的JDBC URL定适合于目标数据库的驱动程序及(qing)建立q接?br />
* W二个getConnection()Ҏ(gu)需要一个以毫秒为单位的旉参数Q该参数表示客户E序能够{待的最长时间。徏立连接的具体?br />
* 作仍旧由W一个getConnection()Ҏ(gu)实现。该Ҏ(gu)执行时先startTime初始化ؓ(f)当前旉。在while循环中尝试获得一个连接。如果失
* 败,则以l定的时间gؓ(f)参数调用wait()。wait()的返回可能是׃其它U程调用notify()或notifyAll()Q也可能是由于预定时间已到?br />
* 为找出wait()q回的真正原因,E序用当前时间减开始时_(d)startTimeQ,如差值大于预定时间则q回I|否则再次调用getConnection()?br />
* 把空闲的q接登记到连接池由freeConnection()Ҏ(gu)实现Q它的参Cؓ(f)q回l连接池的连接对象。该对象被加入到freeConnections
* 向量的末,然后减少已用连接计数。调用notifyAll()是ؓ(f)?jin)通知其它正在{待可用q接的线E?许多Servlet引擎为实现安全关闭提?br />
* 多种Ҏ(gu)。数据库q接池需要知道该事g以保证所有连接能够正常关闭。DBConnectionManagerc负协调整个关闭q程Q但关闭q接池中所有连
* 接的d则由DBConnectionPoolc负责。release()Ҏ(gu)供DBConnectionManager调用。该Ҏ(gu)遍历freeConnections向量q关闭所有连接,
* 然后从向量中删除q些q接?br />
*/
/**
* 此内部类定义?jin)一个连接池.它能够根据要求创建新q接,直到预定的最""""r
*/
class DBConnectionPool {
private int checkedOut;
private Vector freeConnections = new Vector();
private int maxConn;
private String name;
private String password;
private String URL;
private String user;
/**
* 创徏新的q接?br />
* @param nameq接池名?br />
* @param URL数据库的JDBC URL
* @param user数据库帐??null
* @param password密码,?null
* @param maxConn此连接池允许建立的最大连接数
*/
public DBConnectionPool(String name, String URL, String user,
String password, int maxConn) {
this.name = name;
this.URL = URL;
this.user = user;
this.password = password;
this.maxConn = maxConn;
}
/**
* 不再用的q接q回l连接池
* @param con客户E序释放的连?br />
*/
public synchronized void freeConnection(Connection con) {
// 指定连接加入到向量末尾
freeConnections.addElement(con);
checkedOut--;
notifyAll();
}
/**
* 从连接池获得一个可用连?如没有空闲的q接且当前连接数于最大连?数限?则创建新q接.
* 如原来登Cؓ(f)可用的连接不再有?则从向量删除?
* 然后递归调用自己以尝试新的可用连?
*/
public synchronized Connection getConnection() {
Connection con = null;
if (freeConnections.size() > 0) {// 获取向量中第一个可用连?br />
con = (Connection) freeConnections.firstElement();
freeConnections.removeElementAt(0);
try {
if (con.isClosed()) {
log("从连接池" + name + "删除一个无效连?);
// 递归调用自己,试再次获取可用q接
con = getConnection();
}
} catch (SQLException e) {
log("从连接池" + name + "删除一个无效连?);
// 递归调用自己,试再次获取可用q接
con = getConnection();
}
} else if (maxConn == 0 || checkedOut < maxConn) {
con = newConnection();
}
if (con != null) {
checkedOut++;
}
return con;
}
/**
* 从连接池获取可用q接.可以指定客户E序能够{待的最长时?参见前一个getConnection()Ҏ(gu).
* @param timeout以毫U计的等待时间限?br />
*/
public synchronized Connection getConnection(long timeout) {
long startTime = new Date().getTime();
Connection con;
while ((con = getConnection()) == null) {
try {
wait(timeout);
} catch (InterruptedException e) {
e.printStackTrace();
}
if ((new Date().getTime() - startTime) >= timeout) {// wait()q回的原因是时
return null;
}
}
return con;
}
/**
* 关闭所有连?br />
*/
public synchronized void release() {
Enumeration allConnections = freeConnections.elements();
while (allConnections.hasMoreElements()) {
Connection con = (Connection) allConnections.nextElement();
try {
con.close();
log("关闭q接? + name + "中的一个连?);
} catch (SQLException e) {
log(e, "无法关闭q接? + name + "中的q接");
e.printStackTrace();
}
}
freeConnections.removeAllElements();
}
/**
* 创徏新的q接
*/
private Connection newConnection() {
Connection con = null;
try {
if (user==null||"".equals(user)) {
con = DriverManager.getConnection(URL);
} else {
con = DriverManager.getConnection(URL, user, password);
}
log("q接? + name + "创徏一个新的连?);
} catch (SQLException e) {
log(e, "无法创徏下列URL的连? " + URL);
return null;
}
return con;
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
}
在sqlserver2000Qtomcat5.5验证通过
Ҏ(gu)调用Q?/p>
package com.xinnuo.jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
1>mysql安装文g(本h使用mysql5.0.51a-linux-i686-glibc23.tar.gz)解压到对应目录,在此?/home/seal/mysql
2>q入源码目录~译安装
CODE:
#cd /home/seal/mysql
#./configure --prefix=/usr/local/mysql --with-charset=gbk |?配置Mysql安装路径q且支持中文
#make |?~译
#make install |?~译安装
3>替换/etc/my.cnf文g,q入源码?执行命o(h)
CODE:
#cd /home/seal/mysql
#cp support-files/my-medium.cnf /etc/my.cnf
4>建立MySQL使用者和组:
CODE:
#groupadd mysql5>完成以上操作以后q行初始化数据库,q入已经安装好的mysql目录
CODE:
#cd /usr/local/mysql
#bin/mysql_install_db --user=mysql |?--user=mysql 初始化表q且规定用mysql用户
6>讄lmysql和root用户讑֮讉K权限 我们先进入mysql目录
CODE:
#cd /usr/local/mysql
#chown -R root /usr/local/mysql |?讑֮root能访?usr/local/mysq
#chown -R mysql /usr/local/mysql/var |?讑֮mysql用户能访?usr/local/mysql/var
#chgrp -R mysql /usr/local/mysql |?讑֮mysqll能够访?usr/local/mysq
7>启动mysql,q入已经安装好的目录
CODE:
#cd /usr/local/mysql
#bin/mysqld_safe --user=mysql &
8>
修改mysql数据库超U用户root的缺省密?
/usr/local/mysql/bin/mysqladmin -u root password 'mysql'
关闭mysql服务?
cd /usr/local/mysql/bin
./mysqladmin -u root -p shutdown
9>讑֮开机就启动mysql,q入源码目录?/p>
# cd /home/seal/mysql
# cp support-files/mysql.server /etc/init.d/mysql
解决Ҏ(gu)Q?/span>
1?/span>改表法。可能是你的帐号不允总q程登陆Q只能在localhost。这个时候只要在localhost的那台电(sh)脑,dmysql后,更改 “mysql” 数据库里?/span> “user” 表里?/span> “host” ,?/span>“localhost”改称“%”
mysql -u root -pvmwaremysql>use mysql; |
2. 授权法。例如,你想myuser使用mypassword从Q何主接到mysql服务器的话?/span>
GRANT ALL PRIVILEGES ON *.* TO ‘myuser’@'%’ IDENTIFIED BY ‘mypassword’ WI |
如果你想允许用户myuser?/span>ip?/span>192.168.1.6的主接到mysql服务器,q?/span>mypassword作ؓ(f)密码
GRANT ALL PRIVILEGES ON *.* TO ‘myuser’@'192.168.1.3′ IDENTIFIED BY |
SQL Server中的日期cd包括datetime和smalldatetimeQ仅能处理可以识别ؓ(f)1753q_(d)9999q间的日期的|没有单独的日期型或时间型?/font>
datetimecd处理?753q??日~9999q?2?1日的日期和时间数据,_度ؓ(f)癑ֈ之三U。即Q对?.000?.001?.009的日期|调整?.000Q对?.002?.004的日期|调整?.003Q对?.005?.008的日期|调整?.007?/font>
例如Q下面的代码在输入时Q其旉_度ؓ(f)癑ֈ之一U,但经数据库保存后再显C出来,其结果就已经做了(jin)处理?/font>
DECLARE @t TABLE(date char(21))
INSERT @t SELECT '1900-1-1 00:00:00.000'
...
INSERT @t SELECT '1900-1-1 00:00:00.009'
SELECT date,转换后的日期=CAST(date as datetime) FROM @t
/*--l果
date 转换后的日期
---------------------------------- ----------------------------
1900-1-1 00:00:00.000 1900-01-01 00:00:00.000
...
1900-1-1 00:00:00.000 1900-01-01 00:00:00.010
datetime的存储长度ؓ(f)8字节Q日期和旉各用4个字节存储,W一?字节存储?900q??日之前或之后的天敎ͼ?900q??日ؓ(f)分界点,?900q??日之前的日期的天数小?Q在1900q??日之后的日期的天数大?Q。另外一?字节存储以午夜(00:00:00.000Q后毫秒数所代表的每天的旉?/font>
例如Q下面的代码演示?jin)datetime变量中,仅包含单U的日期和单U的旉Ӟ日期存储的十六进制存储表C结果?/font>
DECLARE @dt datetime
--单纯的日?/font>
SET @dt='1900-1-2'
SELECT CAST(@dt as binary(8))
--l果: 0x0000000100000000
--单纯的时?/font>
SET @dt='00:00:01'
SELECT CAST(@dt as binary(8))
--l果: 0x000000000000012C
smalldatetimecd处理?900q??日~2079q?? 日的日期和时间数据,_到分钟?9.998U或更低的smalldatetime值向下舍入ؓ(f)最接近的分钟,29.999U或更高的smalldatetime值向上舍入ؓ(f)最接近的分钟?/font>
smalldatetime的存储长度ؓ(f)4字节Q第一?字节存储?900q??日之后的天数。另外一?字节存储午夜Q?0:00:00.000Q后的分钟数?/font>
例如Q下面的代码演示?jin)smalldatetime变量中,仅包含单U的日期和单U的旉Ӟ日期存储的十六进制存储表C结果?/font>
DECLARE @dt smalldatetime
--单纯的日?/font>
SET @dt='1900-1-2'
SELECT CAST(@dt as binary(4))
--l果: 0x00010000
--单纯的时?/font>
SET @dt='00:10'
SELECT CAST(@dt as binary(4))
--l果: 0x0000000A
日期由年、月、日、时{多个部分组成,它的处理相对复杂Q因此,SQL Server提供?jin)大量的日期处理函数Q用以完成各U日期数据的处理。掌握好q些函数Q对完成数据库的各种日期处理非常必要Q本节将介绍几个常用的日期处理函数。期增减函数可以Ҏ(gu)期指定部分的D行增减,q返回处理后的日期|SQL Server提供的日期增减函Cؓ(f)DATEADD?/font>
DATEADD的具体语法如下:(x)DATEADD ( datepart , number, date )
其中包括以下参数?/font>
¡ datepartQ是规定应向日期的哪一部分q回新值的参数。表2-1列出?jin)SQL Server支持的日期部分、羃写及(qing)含义?/font>
DATEADD?font face="?hu)? MS Song">DATEDIFF支持的日期部分、羃写及(qing)含义
日期部分 |
~?font face="?hu)? MS Song"> ?/font> |
?font face="?hu)? MS Song"> ?/font> |
Year |
yy , yyyy |
q䆾 |
Quarter |
qq , q |
季度 |
Month |
mm , m |
月䆾 |
Dayofyear |
dy,y |
?/font> |
Day |
dd , d |
|
Week |
wk , ww |
星期 |
Hour |
Hh |
时 |
Minute |
mi , n |
分钟 |
Second |
ss , s |
U?/font> |
Millisecond |
Ms |
毫秒 |
¡ numberQ是用来增加datepart的倹{正数表C增加,负数表示减少Q如果指定的是非整数|则忽略此值的数部分Q不做四舍五入处理。例如,DATEADDQDay,1.7,dateQ,表示date增加1天?/font>
¡ dateQ是q回datetime或smalldatetime值或日期格式字符串的表达式?/font>
如果date是smalldatetimeQ则q回smalldatetimeQ否则返回datetime。date为smalldatetimeQDatepart为SecondQss,sQ或MillisecondQmsQ时Q返回值将Ҏ(gu)日期增减的结果调整到分钟Qdate为datetimeQDatepart为MillisecondQmsQ时Q返回值将Ҏ(gu)日期增减的结果调整ؓ(f)癑ֈ之三U。调整规则可以参?.1节的相关说明?/font>
date允许直接与numberq行增减计算Q即对于DATEADDQDay,number,dateQ,{同于date+number?/font>
日期信息获取函数用于获取日期指定部分的相关信息,常用的日期信息获取函数如?-2所C?/font>
常用的日期信息获取函?/font>
功能说明 |
?font face="?hu)? MS Song"> ?/font> |
参数?qing)返回值数据类型说?/font> |
q回代表指定日期的指定日期部分的字符?/font> |
DATENAME(datepart,date) |
datepart是指定应q回的日期部分的参数Q其定义如表2-3所C。date是返回datetime或smalldatetime值或日期格式字符串的表达式。DATENAME函数q回nvarcharQDATEPART函数q回int |
q回代表指定日期的指定日期部分的整数 |
DATEPART(datepart,date) |
|
q回表示指定日期中的q䆾的整?/font> |
YEAR(date) |
q回int |
q回表示指定日期中的月䆾的整?/font> |
MONTH(date) |
q回int |
q回表示指定日期中的天的整数 |
DAY(date) |
q回int |
DATENAME?font face="?hu)? MS Song">DATEPART支持的日期部分、羃写及(qing)含义
日期部分 |
~?font face="?hu)? MS Song"> ?/font> |
?font face="?hu)? MS Song"> ?/font> |
Year |
yy , yyyy |
q䆾 |
Quarter |
qq , q |
季度 |
Month |
mm , m |
月䆾 |
Dayofyear |
dy , y |
?/font> |
Day |
dd , d |
|
Week |
wk , ww |
自年初开始的W几个星?/font> |
Weekday |
Dw |
星期几(例如星期一、星期二Q?/font> |
Hour |
Hh |
时 |
Minute |
mi , n |
分钟 |
Second |
ss , s |
U。date为smalldatetimeӞ始终q回0 |
Millisecond |
Ms |
毫秒。date为smalldatetimeӞ始终q回0Qؓ(f)datetimeӞq回百䆾之三U?/font> |
DATEPARTQWeek,dateQ返回的星期计算方式Q是按照星期日ؓ(f)一周的W一天,q点与中国h的日期处理习(fn)惯不同,在用时要注意这一炏VDATENAME函数q回指定日期的指定日期部分的字符Ԍ其返回的具体字符串|与SET DATEFIRST?qing)SET DATELANGUAGE选项的设|有兟뀂用DATEPARTQWeekday,dateQ时Q其q回的gSET DATEFIRST选项的设|有养I具体的将?.3节中说明?/font>
日期差D函数用于计两个给定日期指定部分的边界敎ͼSQL Server提供的日期差D函Cؓ(f)DATEDIFF?/font>
DATEDIFF的具体语法如下:(x)
DATEDIFF ( datepart , startdate , enddate )
其中包括以下参数?/font>
¡ datepartQ规定了(jin)应在日期的哪一部分计算差额Q其定义如表2-1所C?/font>
¡ startdateQ规定了(jin)计算的开始日期?/font>
¡ enddateQ规定了(jin)计算的终止日期?/font>
q回cdQinteger
计算的开始日期和l止日期Q可以是日期或日期格式的字符丌Ӏ计的Ҏ(gu)是从enddate减去startdate。如果startdate比enddate晚,q回负倹{当l果出整数D_(d)DATEDIFF׃生错误。对于毫U,最大数?4?0时31分钟23.647U。对于秒Q最大数?8q?/font>
计算跨分钟、秒和毫U这些边界的Ҏ(gu)Q得DATEDIFFl出的结果在全部数据cd中是一致的。结果是带正负号的整数|其等于跨W一个和W二个日期间的datepart边界数。例如,?005q??日和2005q??1日之间的月䆾数是1?/font>
其他常用的日期处理相兛_数包括以下几个?/font>
GETDATE按照datetimeD回当前系l日期和旉?/font>
GETDATE的语法如下:(x)
GETDATE()
q回cdQdatetime
ISDATE定输入的表辑ּ是否有效日期?/font>
在输入日期表辑ּӞ日期都是以日期格式的字符串提供的Q由于不同的区域有不同的日期格式Q所以ƈ不能保证输入的日期表辑ּ能够被SQL Server识别Q这U情况下Q就需要用ISDATE来判断日期表辑ּ能否正确地被SQL Server识别?jin)?/font>
ISDATE的语法如下:(x)
ISDATE(expression)
q回cdQint
CONVERT某U数据类型的表达式显式{换ؓ(f)另一U数据类型?/font>
严格来说QCONVERT不属于日期处理函敎ͼ只是它被l常用于日期处理中,所以这里把它列入了(jin)其他日期处理函数Q下面是CONVERT的用法描qͼ只重点说明在日期处理中的应用Q?/font>
CONVERT的具体语法如下:(x)
CONVERT ( data_type [ ( length ) ] , expression [ , style ] )
其中包括以下参数?/font>
¡ expressionQ是要{换数据类型的有效SQL Server表达式?/font>
¡ data_typeQ是expression转换后的数据cdQlength是对于有_ֺ定义需要的data_type的精度定义,对于没有_ֺ定义需要的data_typeQ该参数可以省略?/font>
¡ styleQ定义数据类型{换时的格式,对于日期cd的{换,它的定义如表2-4所C?/font>
?font face="?hu)? MS Song">2-4 style在日期{换中的说?/font>
不带世纪C |
带世U数?/font> |
?font face="?hu)? MS Song"> ?/font> |
输入/输出 |
?/font> |
0?00 |
默认?/font> |
mon dd yyyy hh:miAM(?PM) |
1 |
101 |
国 |
mm/dd/yyyy |
2 |
102 |
ANSI |
yy.mm.dd |
3 |
103 |
英国/法国 |
dd/mm/yy |
4 |
104 |
德国 |
dd.mm.yy |
5 |
105 |
意大?/font> |
dd-mm-yy |
6 |
106 |
?/font> |
dd mon yy |
7 |
107 |
?/font> |
mon dd, yy |
8 |
108 |
?/font> |
hh:mm:ss |
?/font> |
9?09 |
默认?毫秒 |
mon dd yyyy hh:mi:ss:mmmAM(或PM) |
10 |
110 |
国 |
mm-dd-yy |
11 |
111 |
日本 |
yy/mm/dd |
12 |
112 |
ISO |
yymmdd |
?/font> |
13?13 |
Ƨ洲默认?毫秒 |
dd mon yyyy hh:mm:ss:mmm(24h) |
14 |
114 |
?/font> |
hh:mi:ss:mmm(24h) |
?/font> |
20?20 |
ODBC规范 |
yyyy-mm-dd hh:mm:ss[.fff] |
?/font> |
21?21 |
ODBC规范Q带毫秒Q?/font> |
yyyy-mm-dd hh:mm:ss[.fff] |
?/font> |
126 |
ISO8601 |
yyyy-mm-ddThh:mm:ss.mmm |
?/font> |
130 |
Hijri |
dd mon yyyy hh:mi:ss:mmmAM |
?/font> |
131 |
Hijri |
dd/mm/yy hh:mi:ss:mmmAM |
说明Q?/font>
?nbsp;输入/输出Q?#8220;输入”表示从字W串转换为日期时字符串的日期格式Q?#8220;输出”指从日期转换为字W串时的日期字符串格式?/font>
?nbsp;HijriQ是h几种变化形式的日历系l,SQL Server使用其中的科威特法?/font>
当从smalldatetime转换为字W数据时Q由于smalldatetimer只保存到分钟的数据,因此Q对于包含秒或毫U的样式Q将在秒或毫U的位置上显C零。当从datetime或smalldatetimeD行{换时Q可以通过使用适当的char或varchar数据cd长度来截断不需要的日期部分?/font>
注意Q?/font>
在SQL Server中,׃直接提供的日期均是以日期格式的字W串提供Q所以在使用CONVERTq行日期格式转换Ӟ要先把日期格式的字符串{换ؓ(f)日期型,然后才能利用CONVERTq行日期格式转换Q否则就变成字符串{换ؓ(f)字符Ԍ此时的style选项是无效的?/font>
q回cdQ由参数data_type定?/font>
下面是利用CONVERTq行日期转换的简单示例:(x)
/*== 字符转换为日期时,Style的?font face="Courier New"> ==*/
--1. Style=101?font face="Courier New">,表示日期字符串ؓ(f):mm/dd/yyyy格式
SELECT CONVERT(datetime,'11/1/2003',101)
--l果:2003-11-01 00:00:00.000
--2. Style=101?font face="Courier New">,表示日期字符串ؓ(f):dd/mm/yyyy格式
SELECT CONVERT(datetime,'11/1/2003',103)
--l果:2003-01-11 00:00:00.000
/*== 日期转换为字W串 ==*/
DECLARE @dt datetime
SET @dt='2003-1-11'
--1. Style=101?font face="Courier New">,表示日期{换ؓ(f):mm/dd/yyyy 格式
SELECT CONVERT(varchar,@dt,101)
--l果:01/11/2003
--2. Style=103?font face="Courier New">,表示日期{换ؓ(f):dd/mm/yyyy 格式
SELECT CONVERT(varchar,@dt,103)
--l果:11/01/2003
/*== q是很多人经常犯的错?font face="Courier New">,寚w日期型{换用日期的style样式 ==*/
SELECT CONVERT(varchar,'2003-1-11',101)
--l果:2003-1-11数据库结构的脚本:
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[TempA]') and OBJECTPROPERTY(id, N'IsUserTable') = 1)
drop table [dbo].[TempA]
GO
CREATE TABLE [dbo].[TempA] (
[id] [int] IDENTITY (1, 1) NOT NULL ,
[PositionName] [varchar] (256) COLLATE Chinese_PRC_CI_AS NULL ,
[EnglishPositionName] [varchar] (256) COLLATE Chinese_PRC_CI_AS NULL
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[TempA] ADD
CONSTRAINT [PK_TempA] PRIMARY KEY CLUSTERED
(
[id]
) ON [PRIMARY]
GO
TempA表中有三个字D?id唯一且ؓ(f)主键,自动增长; PositionName,EnglishPositionName中有重复的记?比如:
id PositionName EnglishPositionName
20 其他 Others
21 质量工程?nbsp; QC Engineer
22 其他 Others
.......
100 质量工程?nbsp; QC Engineer
需要剔除重复的"其他","质量工程?{记录?/p>
采用的SQL语句Q?br />
Delete from TempA where id not in (
select max(t1.id) from TempA t1 group by
t1.PositionName,t1.EnglishPositionName)
说明Q?br />
(1)需要剔除那几个用于判断重复的字D,则将它们攑֜group by语句之后?br />
(2)max(t1.id) 也可以改成:(x)min(t1.id)