database.sarang.net
UserID
Passwd
Database
DBMS
MySQL
ㆍPostgreSQL
Firebird
Oracle
Informix
Sybase
MS-SQL
DB2
Cache
CUBRID
LDAP
ALTIBASE
Tibero
DB 문서들
스터디
Community
공지사항
자유게시판
구인|구직
DSN 갤러리
도움주신분들
Admin
운영게시판
최근게시물
PostgreSQL Tutorials 4300 게시물 읽기
 News | Q&A | Columns | Tutorials | Devel | Files | Links
No. 4300
The PostgreSQL JDBC Primer
작성자
정재익(advance)
작성일
2002-08-15 02:22
조회수
6,574

The PostgreSQL JDBC Primer

 

원본출처 : http://www.j-elite.com/pgprimer/index.jsp

 

1.0 Introduction and Prerequisites

 

Welcome to the PostgreSQL JDBC primer. This online resource contains all you need to successfully implement a JDBC-based solution using PostgreSQL as the database back-end, and Java or JDBC as the interface to the data. Some portions of this document may refer to other Java technology such as Java Server Pages, Servlets, JAXP and others. For further information about Java and Sun Microsystem's, please see: http://www.javasoft.com and http://www.sun.com.

 

This document was also designed to be followed in a sequential manner, so for those people new to the whole area, you can simply step through the sections and they should follow a relatively meaningful progression.

 

Any requests, feedback or otherwise can be emailed to Joe Shevland.

 

This document relies on certain versions of the PostgreSQL software and other required components. As of this revision, the following versions and software components are required; more recent versions of the software should also work given that backwards compatibility is maintained in the areas documented:

 

. PostgreSQL Server v7.2

. PostgreSQL JDBC Driver v7.2 (suitable for JDK/JRE being used)

. JRE/JDK (1.1.x, 1.2.x, 1.3.x and/or 1.4)

 

The earlier versions of the Java Runtime Environment (1.1.x) supported the JDBC 1.0 API, whereas later versions of the JRE/JDK support the JDBC 2.0 API, and onwards to the JDBC 3.0 API in JRE/JDK 1.4. Sections requiring a particular version of the JDBC specification will be marked as such.

 

2.0 Table of Contents

 

1.0 Introduction and Prerequisites

2.0 Table of Contents

3.0 Getting Started

4.0 Configuring PostgreSQL

4.1 Enabling TCP/IP Sockets for PostgreSQL

4.2 Checking for TCP/IP Sockets for PostgreSQL

4.3 Allowing Access to PostgreSQL from the Network

5.0 Connecting to PostgreSQL

A.0 PostgreSQL JDBC Examples

A.1 Basic (JDK 1.1.8, local server, application)

B.0 PostgreSQL BLOB support

B.1 Binary Data Types

 

 

3.0 Getting Started

 

Before getting started, you should make sure you have the following items available. Experienced users will be able to get by with little or no preparation for most parts of this tutorial, however new users should follow these steps to ensure a consistent experience.

 

As a starting point, the following items are necessary:

 

PostgreSQL Server v7.2 (earlier versions may work)

PostgreSQL JDBC Driver v7.2 (suitable for JDK/JRE being used)

JRE/JDK (1.1.x, 1.2.x, 1.3.x and/or 1.4.x)

Optional, but highly recommended tools and items to have available are:

 

Administrative or user account on the PostgreSQL server

Access to the 'psql' CLI tool on the PostgreSQL server

Access to a shell (tcsh) on the PostgreSQL server (FreeBSD, naturally;)

Alternatively, an administration tool can replace the preceding two items; there are various applications available for different platforms, including an X-Windows tool and a Windows-based tool for administering PostgreSQL.

 

 

4.0 Configuring PostgreSQL

 

This section describes any additional configuration steps you need to perform in order to communicate with your PostgreSQL server via JDBC. Some or all of these steps may not be necessary, depending on whether you are working with a new installation of PostgreSQL, or an existing, configured version.

 

4.1 Enabling TCP/IP Sockets for PostgreSQL

 

The JDBC driver uses the TCP/IP protocol to communicate with a PostgreSQL server. As an aside, this means the driver is a Type IV JDBC driver, a pure network driver (see http://www.javasoft.com/products/jdbc for more information about JDBC drivers and the different levels). Because it uses Java and pure TCP/IP communications, the driver itself is highly portable across platforms and environments.

 

The server does not use TCP/IP sockets by default; it requires you (or the administrator) to use a command-line flag to the server, or a configuration item in the configuration file, to tell it to open a listening TCP socket (by default on port 5432).

 

To enable TCP/IP sockets on the PostgreSQL server, you can add the -i command line option to the postmaster when it starts up e.g.

 

postmaster -i <other options>

 

Alternatively, you can add following line to the 'postgresql.conf' configuration file stored in the root directory of your PostgreSQL server's database directory (or edit the existing line if it is set to 'false'):

 

tcpip_socket = true

 

4.2 Checking for TCP/IP Sockets for PostgreSQL

 

To check whether PostgreSQL is configured for TCP/IP sockets, open up a shell on the PostgreSQL server (this assumes you have access to a shell account on the PostgreSQL server; if you don't, perhaps try telneting to the server on port 5432 and see if you obtain a connection).

 

To check for an instance of PostgreSQL listening on TCP/IP port 5432, type:

 

netstat -an | grep 5432 | grep LISTEN

 

The following results are taken from a FreeBSD 4.5-STABLE system. These results indicate that there is a server instance listening on port 5432 (the default PostgreSQL TCP/IP port to use; if you have configured a different port you will need to replace the number above).

%netstat -an | grep -e 5432 | grep LISTEN
tcp4       0      0  *.5432                 *.*                    LISTEN

 

4.3 Allowing Access to PostgreSQL from the Network

 

By default, PostgreSQL does not enable TCP/IP sockets or access to the server from non-local sources (for very good security reasons). Once you have enabled TCP/IP sockets, you will also need to add entries to the pg_hba.conf file to allow access from your application's IP address and username. There are other authentication mechanisms available, but this document only discusses plain password authentication over TCP/IP.

 

Because the default permissions only allow access to the PostgreSQL server from local sources, you will need to add an entry into the pg_hba.conf file to grant your application or applet access from its IP address (even if it is using localhost or 127.0.0.1, it is still considered as TCP/IP and not a local connection).

 

Your pg_hba.conf file is located in the root database folder of your PostgreSQL server. It is worth reading this file to ensure you are familiar with the various options available. Some brief example entries follow, however please consult the PostgreSQL Administration manual for more information.

 

Example pg_hba.conf entries

 

The following entry allows access to all users from the TCP loopback socket, often referred to as localhost or 127.0.0.1, representing connections from the local machine. Use this if your application or applet resides on the same machine as the PostgreSQL server.

host         all         127.0.0.1     255.255.255.255     password

The following entry allows access to user 'fred' from the IP address 192.168.0.50. This setup would be suitable for a client machine needing to connect to the server on a Class C subnet.

host         fred        192.168.0.50  255.255.255.0       password

 

5.0 Connecting to PostgreSQL

 

Connecting to a PostgreSQL database via JDBC is very straightforward, once you have the initial environment ready as described in the previous sections. This section assumes you have followed the steps in the previous sections successfully, and have all the required components ready to test the Java source code (JDK, JDBC driver, development environment or folder).

 

5.1 The PostgreSQL JDBC Driver

 

Two JDBC drivers are provided for PostgreSQL, one for JRE/JDK 1.1.x environments (or earlier), and one provided for JRE/JDK 1.2.x (or later). The former supports the JDBC 1.0 specification, whereas the latter is compiled against the JDBC 2.0 specification. Work is in progress to include support for the JDBC 3.0 specification.

 

You can download the latest PostgreSQL JDBC drivers from the following URL: http://jdbc.postgresql.org/download.html.

 

If you follow any of the examples that include references to a CLASSPATH setting in this document, be sure to replace the name of the PostgreSQL JDBC driver file we have used with the one you obtain (generally 'pgjdbc2.jar' will be used, unless an example calls for a specific environment).

 

5.2 JDBC 1.0 Approach: DriverManager

 

The JDBC 1.0 approach used to connect to a database is to register a specific driver with the DriverManager class, and subsequently make calls to the DriverManager class to hand out connections for a particular JDBC URL (based on the sub-protocol element in the URL e.g. jdbc:postgresql:dbname.

 

So when you register a particular JDBC driver, it informs the DriverManager of the types of JDBC URLs it can handle. When your application code then requests a URL, the DriverManager asks each of its registered drivers whether they can handle the URL.

 

The following code demonstrates registering the PostgreSQL JDBC driver, and subsequently obtaining a connection to a database called 'dontpanic'.

 

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DriverManagerConnectionTest {

    public static void main( String args[] ) {

        try {

            Class.forName("org.postgresql.Driver");
            
        } catch ( ClassNotFoundException cnfe ) {
            
            // Uh-oh, we couldn't find the PostgreSQL driver on
            // our CLASSPATH.
            System.err.println("PostgreSQL driver not in CLASSPATH or cannot be found: "+cnfe);
            System.exit(1);
        }

        // If we reach here, the driver was successfully found and
        // registered. Where you say? Simply by calling the forName()
        // method above, the JVM loads the PostgreSQL Driver class, which
        // at that time registers itself with the DriverManager.
        
        // Now we can obtain a connection
        Connection con = null;
        try {
            
            // Try and connect to the database 'dontpanic' as user 'ford'
            // and password 'prefect'
            con = DriverManager.getConnection(
                "jdbc:postgresql:dontpanic",
                "ford",
                "prefect");
                
            // Use the connection to make updates or retrieve records...
            
        } catch ( SQLException se ) {
            
            // Something happened to cause the connection to fail. The
            // exception message will give you a good indication.
            System.err.println("Problem connecting to database: "+se);
            System.exit(1);
            
        } finally {
         
            // And finally close the connection, freeing important resources.
            // We do this in the finally clause so it will be called even
            // if a problem occurs above (check if its been created though).
            if ( con != null )
                con.close();
        }
    }
}

 

5.3 JDBC 2.0 Approach: DataSource

 

Connecting to a PostgreSQL database using the DataSource approach is new to the Java 2 platform, and has benefits over and above the DriverManager approach.

 

The following code snippet demonstrates how to create and bind a DataSource to a JNDI context. This allows the application to make simple JNDI calls to obtain connections to the database, potentially with connection pooling being implemented behind the scenes.

 

public class DataSourceTest {

    public static void main( String args[] ) {

        org.postgresql.PostgresqlDataSource ds =
            new org.postgresql.PostgresqlDataSource();

        ds.setServerName("host");
        ds.setDatabaseName("database");

        Context ctx = null;
        System.out.println("Creating a new Context...");

        try {
            ctx = new InitialContext();
        } catch ( Exception e ) {
            System.out.println("Failed to create a new Context.");
            System.out.println("Exception: " + e);
            return;
        }

        System.out.println("Binding...");

        try {
            ctx.bind("jdbc/database", ds);
        } catch ( Exception e ) {
            System.out.println("Bind failed.");
            System.out.println("Exception: " + e);
            return;
        }
    }
}

 

Once created and bound to a context, obtaining a connection from the JNDI context is easy.

 

The following code example demonstrates obtaining a connection from the context, performing a simple SELECT query and then releasing the connection.

 

public class DataSourceConnectionTest {

    public static void main( String args[] ) {

        Context() ctx = new InitialContext();

        DataSource ds = (DataSource)ctx.lookup(
            "jdbc/database");

        Connection con = ds.getConnection();

        // perform operations on the connection object

        con.close();
    }
}

 

The next section shows some specific examples of the previous sections, with scenarios for JDBC 1.0 and 2.0 environments and some different uses of the driver.

 

A.0 PostgreSQL JDBC Examples

 

The following sections provide examples that summarise the notes in the previous sections, and provide specific examples based on different scenarios such as deploying an applet or using the driver in a JSP web application.

 

A.1 Basic (JDK 1.1.8, local server, application)

 

This example shows a simple connectivity test to see if a JDK 1.1.8 application will connect to a PostgreSQL server on the local machine. The first step is to ensure the environment is sound.

 

Typing 'java -version' from the command line will display a version string for the Java interpreter you are using. In this example we are looking for a 1.1.8 compliant JVM. A quick look at the download site reveals the driver suited for JDK 1.1.x environments. We'll assume a directory structure similar to the following (the wget command can be replaced with an equivalent HTTP download tool):

 

%mkdir ~/work/simpletest

%mkdir ~/work/simpletest/lib

%cd ~/work/simpletest/lib

%wget http://jdbc.postgresql.org/download.html/pgjdbc1.jar

 

We'll assume the path to a JDK installation is '/usr/local/jdk1.1.8' and that the 'java' and 'javac' executables are in the path. The following Java source code shows the bare minimum necessary to test a connection to a Postgres server (you will need to replace 'testdb', 'guest' and 'guestpw' with your own database name, username and password respectively): import java.sql.*;

 

public class PGTest {
    
    public static void main( String args[] ) {
        
        Connection con = null;
        String database = "testdb";
        String username = "guest";
        String password = "guestpw";
        String message = null;
        
        try {
            
            // Load and register the PostgreSQL driver
            Class.forName("org.postgresql.Driver");
            
            // Try and connect to the database with the given
            // credentials:
            Connection con = DriverManager.getConnection(
                "jdbc:postgresql:"+database,username,password);
            con.close();
        
            message = "Connection to '"+database+"' as user "+username+" successful!";
            System.out.println(message);
            
        } catch ( Exception e ) {
            
            // Report the error and exit:
            message = "Connection to '"+database+"' as user "+username+" failed: "+e;
            System.err.println(message);
            System.exit(1);
        }
    }
}

 

To compile and execute the source code above, use the following commands. We'll assume the source code has been saved into the following file: ~/work/simpletest/PGTest.java. If you want to use this example a few times, you can replace the database, username and password parameters with references to command line arguments instead.

 

%cd ~/work/simpletest

%javac PGTest.java

%java -classpath .:lib/pgjdbc1.jar PGTest

 

If everything is set up correctly, the following output should be produced: Connection to 'testdb' as user guest successful!

 

Otherwise you will see an error message produced. The description occurring at the end of the following message is generally enough to determine the cause of the problem. Connection to 'testdb' as user guest failed: <error message>

 

B.0 PostgreSQL BLOB Support

 

The following section explains the level of support PostgreSQL has for Binary Large OBjects (or BLOBs as they are affectionately known).

 

B.1 Binary Data Types

 

A new binary data type, bytea, has been added with the release of PostgreSQL 7.2. PostgreSQL also supports binary large objects using the oid data type; there are advantages and disadvantages to consider when choosing the most appropriate type for your application, discussed below.

 

Topic

Binary Data using bytea type

Binary Data using oid type

 

storage and size limitations

Can hold up to 1G of data, which is stored along with the rest of the table data.

 

The problem with very large BLOBs using this data type is that all of the data is retrieved when you retrieve this field in a result set, potentially exhausting available memory.

Large objects stored in this fashion are stored in a separate table global to all databases under the one PostgreSQL instance, and referenced via their oid value.

 

In release 7.2 and later, they are accessed using a custom internal API by default. This API obtains streams for the large objects, making this a more suitable data type for very large BLOBs.

 

Deleting a row that contains a large object of this type does not remove the binary data; you need to do this as a separate operation.

 

API Calls

The following methods can be used to work with the bytea data type: getBytes(), setBytes(), getBinaryStream(), and setBinaryStream().

 

Prior to version 7.2, these methods acted by default on the oid type of large object.

 

The following JDBC methods can be used to work with the oid data type: setBlob().

 

Prior to version 7.2, the methods described for the bytea data type worked for this type. The JDBC driver can be made to revert to the 7.1 default behaviour by setting the compatible property of the Connection instance to a value of '7.1'.

 

Security

 

Binary data stored using this type is governed by the same security ACLs the rest of the table's columns are governed by.

Binary data stored using this type is potentially a security issue, as any user that can connect to a database has access to view and modify the global large object table.

 

 

The following tables are used throughout this section to demonstrate accessing binary data using both the supported data types.

 

CREATE TABLE blob_bytea (
    blob_name   varchar(50) primary key,
    blob_data   bytea
);

CREATE TABLE blob_lo (
    blob_name   varchar(50) primary key,
    blob_data   oid
);

 

Also used in this example are a few files containing random binary data, ranging from 256 bytes to 65 megabytes. These will be used to test the relative storage and access speeds of both types using a Highly Specialized Method (TM) on a Pentium 166 beast running FreeBSD, with 32M of RAM and a disk from the Dark Ages. Temporary note: I'll add some test results shortly, the recent introduction of bytea as the default BLOB type causes definite issues with large binary data.

 

The following class provides methods to store and retrieve binary data using the bytea and oid data types. When run as an application, it takes three command line arguments: the database name (which can contain a hostname and port specification), the username and the password.

 

import java.io.*;
import java.sql.*;
import org.postgresql.largeobject.*;

public class BLOBTest {
    
    public BLOBTest()
        throws Exception {
            
        Class.forName("org.postgresql.Driver");

        createDummyDataFile("bin0",256);
        createDummyDataFile("bin1",1024);
        createDummyDataFile("bin512",512000);
        createDummyDataFile("bin1024",1024000);
        createDummyDataFile("bin4096",4096000);
        createDummyDataFile("bin16384",16384000);
        createDummyDataFile("bin32768",32768000);
        createDummyDataFile("bin65535",65535000);
    }

    public static void main( String args[] ) {
        if ( args.length != 3 ) {
            System.err.println("usage: java BLOBTest   ");
            System.exit(1);
        }
        
        try {

            BLOBTest test = new BLOBTest();
            long time = 0;

            Connection conn = DriverManager.getConnection(
                "jdbc:postgresql:"+args[0],args[1],args[2]);

            test.testData(conn,"bytea","bin0",256);
            test.testData(conn,"LO/oid","bin0",256);

            test.testData(conn,"bytea","bin1",1024);
            test.testData(conn,"LO/oid","bin1",1024);

            test.testData(conn,"bytea","bin512",512000);
            test.testData(conn,"LO/oid","bin512",512000);

            test.testData(conn,"bytea","bin1024",1024000);
            test.testData(conn,"LO/oid","bin1024",1024000);

            test.testData(conn,"bytea","bin4096",4096000);
            test.testData(conn,"LO/oid","bin4096",4096000);

            test.testData(conn,"bytea","bin16384",16384000);
            test.testData(conn,"LO/oid","bin16384",16384000);

            test.testData(conn,"bytea","bin32768",32768000);
            test.testData(conn,"LO/oid","bin32768",32768000);

            test.testData(conn,"bytea","bin65535",65535000);
            test.testData(conn,"LO/oid","bin65535",65535000);

        } catch ( Exception e ) {
            System.err.println(e);
            e.printStackTrace();
        }
    }

    private void createDummyDataFile( String fileName, int size )
        throws Exception {

        byte data[] = new byte[size];
        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(data);
        fos.close();
    }
        
    private void testData( Connection conn, String method, String fileName, int size )
        throws Exception {

        long time;

        // Garbage collect to clean up any garbage objects
        System.gc();

        if ( method.equals("bytea") ) {
            time = storeBlobAsBytea(conn, fileName);
            System.err.println("["+method+"] storing    "+(size/1024)+"k of data -> "+time+"ms");
            time = retrieveBlobAsBytea(conn, fileName);
            System.err.println("["+method+"] retrieving "+(size/1024)+"k of data -> "+time+"ms");
        } else {
            time = storeBlobAsLO(conn, fileName);
            System.err.println("["+method+"] storing    "+(size/1024)+"k of data -> "+time+"ms");
            time = retrieveBlobAsLO(conn, fileName);
            System.err.println("["+method+"] retrieving "+(size/1024)+"k of data -> "+time+"ms");
        }
    }

    private void cleanData( Connection conn, String fileName )
        throws Exception {

        PreparedStatement ps = conn.prepareStatement("DELETE FROM blob_bytea WHERE blob_name=?");
        ps.setString(1, fileName);
        ps.executeUpdate();
        ps.close();
        ps = conn.prepareStatement("DELETE FROM blob_lo WHERE blob_name=?");
        ps.setString(1, fileName);
        ps.executeUpdate();
        ps.close();
        ps = conn.prepareStatement("VACUUM FULL;");
        ps.executeUpdate();
        ps.close();
    }

    private long storeBlobAsBytea( Connection conn, String fileName )
        throws Exception {
    
        long t1, t2;
    
        t1 = System.currentTimeMillis();
        File file = new File(fileName);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
        PreparedStatement ps = conn.prepareStatement("INSERT INTO blob_bytea VALUES (?, ?)");
        ps.setString(1, file.getName());
        ps.setBinaryStream(2, bis, (int)file.length());
        ps.executeUpdate();
        ps.close();
        bis.close();
        t2 = System.currentTimeMillis();
        return (t2 - t1);
    }
    
    private long retrieveBlobAsBytea( Connection conn, String fileName )
        throws Exception {
        
        long t1, t2;
    
        t1 = System.currentTimeMillis();
        PreparedStatement ps = conn.prepareStatement(
            "SELECT blob_data FROM blob_bytea WHERE blob_name=?");
        ps.setString(1, fileName);
        ResultSet rs = ps.executeQuery();
        if ( rs.next() ) {
            byte[] imgBytes = rs.getBytes(1);
            // use the stream in some way here
        }
        rs.close();
        ps.close();
        t2 = System.currentTimeMillis();
        return (t2 - t1);
    }
    
    private long storeBlobAsLO( Connection conn, String fileName )
        throws Exception {
    
        long t1, t2;

        boolean oldState = conn.getAutoCommit();
        try {

            t1 = System.currentTimeMillis();

            // All LargeObject API calls must be within a transaction
            conn.setAutoCommit(false);

            // Get the Large Object Manager to perform operations with
            LargeObjectManager lobj = ((org.postgresql.Connection)conn).getLargeObjectAPI();

            // create a new large object
            int oid = lobj.create(LargeObjectManager.READ | LargeObjectManager.WRITE);

            // open the large object for write
            LargeObject obj = lobj.open(oid, LargeObjectManager.WRITE);

            // Now open the file
            File file = new File(fileName);
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));

            // copy the data from the file to the large object
            byte buf[] = new byte[2048];
            int s, tl = 0;
            while ((s = bis.read(buf, 0, 2048)) > 0) {
                obj.write(buf, 0, s);
                tl += s;
            }
            // Close the large object
            obj.close();
            // Now insert the row
            PreparedStatement ps = conn.prepareStatement("INSERT INTO blob_lo VALUES (?, ?)");
            ps.setString(1, file.getName());
            ps.setInt(2, oid);
            ps.executeUpdate();
            ps.close();
            bis.close();

            t2 = System.currentTimeMillis();
            return (t2 - t1);

        } finally {
            conn.setAutoCommit(oldState);
        }
    }
    
    private long retrieveBlobAsLO( Connection conn, String fileName )
        throws Exception {
    
        long t1, t2;

        boolean oldState = conn.getAutoCommit();

        try {

            t1 = System.currentTimeMillis();

            // All LargeObject API calls must be within a transaction
            conn.setAutoCommit(false);

            // Get the Large Object Manager to perform operations with
            LargeObjectManager lobj = ((org.postgresql.Connection)conn).getLargeObjectAPI();
            PreparedStatement ps = conn.prepareStatement(
                "SELECT blob_data FROM blob_lo WHERE blob_name=?");
            ps.setString(1, fileName);
            ResultSet rs = ps.executeQuery();
        
            while(rs.next()) {
                // open the large object for reading
                int oid = rs.getInt(1);
                LargeObject obj = lobj.open(oid, LargeObjectManager.READ);
                // read the data
                byte buf[] = new byte[obj.size()];
                obj.read(buf, 0, obj.size());
                // do something with the data read here
                // Close the object
                obj.close();
            }
            rs.close();
            ps.close();

            t2 = System.currentTimeMillis();
            return (t2 - t1);
        } finally {
            conn.setAutoCommit(oldState);
        }
    }
}
[Top]
No.
제목
작성자
작성일
조회
4324PostgreSQL을 Windows 2000에 설치하기
정재익
2002-09-03
7758
4309Global Transactions - X/Open XA - Resource Managers
정재익
2002-08-23
6819
4301Migrating from MySQL to PostgreSQL
정재익
2002-08-15
6775
4300The PostgreSQL JDBC Primer
정재익
2002-08-15
6574
4285mimic oracle's replace function. versions in pltcl and plpgsql
정재익
2002-08-05
5415
4275LAMPS Tutorial v2.0
정재익
2002-08-01
7472
4274Tutorial - Installation of PostgreSQL, Apache and PHP
정재익
2002-08-01
7101
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2023 DSN, All rights reserved.
작업시간: 0.051초, 이곳 서비스는
	PostgreSQL v16.1로 자료를 관리합니다