Java的JDBC操作


Java的JDBC操作

1.JDBC入門

1.1.什么是JDBC

JDBC從物理結構上來說就是java語言訪問數據庫的一套接口集合,本質上是java語言根數據庫之間的協議。JDBC提供一組類和接口,通過使用JDBC,開發人員可以使用java代碼發送sql語句,來操作數據庫

1.2.使用JDBC發送SQL的前提

登錄數據庫服務器(連接數據庫服務器)需要有以下幾項:

  • 數據庫的IP地址
  • 端口
  • 數據庫用戶名
  • 密碼 java連接數據庫代碼示例:
    /**
    * 連接數據庫的三種方式
    */
public class dbConnect {
    //連接數據庫的URL,
    // JDBC協議:數據庫子協議://主機:端口/連接的數據庫
    private String url="jdbc:mysql://localhost:3306/db";
    private String user="root";//用戶名
    private String password = "root";//密碼

    /**
     *  1,連接數據庫方法1:
     * @throws SQLException
     */
    @Test
    public void connect1() throws SQLException {
        //1.連接的創建驅動程序類對象
        //com.mysql.jdbc.Driver()是mysql連接java的驅動,由mysql提供
        Driver driver=new com.mysql.jdbc.Driver();//新版本寫發,推薦
//        Driver driver = new org.gjt.mm.mysql.Driver();//舊版本寫法
        //設置用戶名和密碼
        Properties props = new Properties();
        props.setProperty("user",user);
        props.setProperty("password",password);

        //2.連接數據庫,返回連接對象
        Connection conn = driver.connect(url,props);

        System.out.println(conn);
    }

    /**
     *  2.連接數據庫方法2:
     *  使用驅動管理器來連接數據庫
     */
    @Test
    public void connect2() throws SQLException {
        Driver driver = new com.mysql.jdbc.Driver();
        //1.注冊驅動程序(可以注冊多個驅動程序)
        //下面一行的代碼在加載類的時候已經被執行了,不需要再寫了
//        DriverManager.registerDriver(driver);

        //2.連接到數據庫
        Connection conn = DriverManager.getConnection(url,user,password);
        System.out.println(conn);
    }

    /**
     *  3.連接數據庫方法3:
     *  推薦使用此方法來連接數據庫
     */
    @Test
    public void connect3() throws ClassNotFoundException, SQLException {
        //通過字節碼對象的方法加載靜態代碼塊,從而注冊驅動程序
        Class.forName("com.mysql.jdbc.Driver");
        //連接到具體的數據庫
        Connection conn = DriverManager.getConnection(url,user,password);
        //打印判斷是否連接成功
        System.out.println(conn);
    }
}

1.3.JDBC接口的核心API

JDBC的接口主要來源於舊版的java.sql.和新版的javax.sql. **Drive接口:**表示java驅動程序接口。所有具體的數據庫廠商要實現此接口

connect(url,properties):連接數據庫的方法

  • url語法:JDBC協議:數據庫子協議://主機:端口/數據庫
  • user:數據庫的用戶名
  • password:數據庫用戶密碼

DriveManager類:驅動管理器類,用於管理所有注冊的驅動程序

registerDriver(driver):注冊驅動類對象 Connection getConnection(url,user,password);獲取連接對象

**Connection接口:**表示java程序和數據庫的連接對象。

Statement createStatement();創建Statement對象 PreparedStatement prepareStatement(String sql);創建PreparedStatement對象 CallableStatement prepareCall(String sql);創建CallableStatement對象

**Statement接口:**用於執行靜態sql語句

int executeUpdate(String sql);執行靜態的更新sql語句 ResultSet executeQuery(String sql);執行的靜態查詢sql語句

**PreparedStatement接口:**用於執行預編譯sql語句,是Statement的子接口

int executeUpdate();執行預編譯的更新sql語句 ResultSet executeQuery();執行預編譯的查詢sql語句

**CallableStatement接口:**用於執行存儲過程sql語句,是PreparedStatement的子接口

Result executeQuery();調用存儲過程的方法

**ResultSet接口:**用於執行存儲過程的sql語句

Result executeQuery();調用存儲過程的方法

對ResultSet結果獲取數據的基本思想是:先指定行,然后獲取該行上字段的值

ResultSet用於代表sql語句查詢的結果,對結果進行封裝,並使用游標來獲取數據,在初始的時候,游標在第一行之前 ,調用ResultSet.next()方法,可以使游標指向具體的數據行,每調用一次向下調用一行,調用該方法獲取該行的數據。

ResultSet是用於封裝結果的,所以提供的主要是get方法,當使用next()方法指定到一行的時候,可以使用以下兩種方式來獲取指定行的字段值: 方法1: 通過索引來獲取,索引從1開始,注意,並不是從0開始,

  • getObject(int index);Object使用字段的數據類型來替換 方法2: 指定數據類型並帶有字段的參數來獲取,例如:
  • getString("name");

常用的幾個類型: int getInt(); varchar getString() date getdate() bit getBoolean()

ResultSet還提供了對結果集進行定位行的方法:

  • next();移動到下一行
  • previous();移動到前一行
  • absolute(int row);移動到指定行
  • beforeFist();移動ResultSet的最前面
  • afteLast();移動到ResultSet的最最后

2.使用Statement執行SQL語句

當書庫已經連接並准備好sql語句之后,就要將這個語句在數據庫中執行,這個使用使用Statement對象,該對象主要提供兩種方法,一種是

  • executeUpdate(sql);該方法主要用於向數據庫發送插入,修改,刪除的命令,返回一個整數
  • executeQuery()方法用於向數據庫發送查詢語句,用於查詢數據,返回的是結果封裝在ResultSet中,然后再從ResultSet中將數據打印出來

使用executeUpdate插入,修改,刪除代碼示例:

/**
 * 測試Statement接口
 */
public class StatementDemo {
    private String url = "jdbc:mysql://localhost:3306/db";
    private String user = "root";
    private String password = "root";
    /**
     *1.創建表
     */
    @Test
    public void TestCteate(){
        Connection connection=null;
        Statement statement=null;
        try {
            //1.驅動注冊程序
            Class.forName("com.mysql.jdbc.Driver");
            //2.獲取連接對象
            connection = DriverManager.getConnection(url,user,password);
            //3.創建Statement對象
            statement=connection.createStatement();
            //4.准備sql
            String sql = "CREATE TABLE student (id INT PRIMARY KEY AUTO_INCREMENT,NAME VARCHAR(20),gender VARCHAR(2));";
            //5.發送sql語句,執行sql語句,並返回結果被影響的行數
            int count = statement.executeUpdate(sql);
            //6.輸出
            System.out.println("影響了"+count+"行!");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //7.關閉連接(關閉順序:后開發先關閉)
            if (statement!=null) try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            if (connection!=null) try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    /**
     * 插入數據
     * 修改數據
     * 刪除數據
     */
    @Test
    public void TestInsert(){
        Connection connection=null;
        Statement statement=null;
        try {
            //1.驅動注冊程序
            Class.forName("com.mysql.jdbc.Driver");
            //2.獲取連接對象
            connection = DriverManager.getConnection(url,user,password);
            //3.創建Statement對象
            statement=connection.createStatement();
            //4.准備sql
            //其他的修改,刪除,只需要更改這里的sql即可
            //插入
//            String sql = "INSERT INTO student(NAME,gender) VALUES ('張梅','女');";
           //修改
//            String sql = "UPDATE student SET NAME='張三' WHERE id=2";
            //刪除
            String sql = "DELETE FROM student WHERE id=2";
            //5.發送sql語句,執行sql語句,並返回結果被影響的行數
            int count = statement.executeUpdate(sql);
            //6.輸出
            System.out.println("影響了"+count+"行!");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //7.關閉連接(關閉順序:后開發先關閉)
            if (statement!=null) try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            if (connection!=null) try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

使用executeQuery查詢數據代碼示例: 注意其中的ResultSet方法

    /**
     * 查詢數據
     */
    @Test
    public void TestFind(){
        Connection connection=null;
        Statement statement=null;
        try {
            //1.驅動注冊程序
            Class.forName("com.mysql.jdbc.Driver");
            //2.獲取連接對象
            connection = DriverManager.getConnection(url,user,password);
            //3.創建Statement對象
            statement=connection.createStatement();
            //4.准備sql
            String sql = "SELECT * FROM student";
            //5.發送sql語句,執行sql語句,並返回查詢結果
            ResultSet rs = statement.executeQuery(sql);
            //6.打印出數據
            //方法1:移動光標:
        /*
            boolean flag = rs.next();

			if(flag){
				//取出列值
                //方法1.1:以索引方式
				int id = rs.getInt(1);
				String name = rs.getString(2);
				String gender = rs.getString(3);
				System.out.println(id+","+name+","+gender);


                //方法1.2:以列名稱
				int id = rs.getInt("id");
				String name = rs.getString("name");
				String gender = rs.getString("gender");
				System.out.println(id+","+name+","+gender);

			}
        */
			//方法2:使用遍歷方法
            //遍歷結果
            while(rs.next()){
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String gender = rs.getString("gender");
                System.out.println(id+","+name+","+gender);
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            //7.關閉連接(關閉順序:后開發先關閉)
            if (statement!=null) try {
                statement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            if (connection!=null) try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

注意: 1.jdbc運行完以后一定要釋放資源,特別是Connection對象,它是非常西游的資源,使用完要馬上釋放。 2.jdbc中的釋放要后連接的先釋放 3.為確保資源釋放代碼能夠運行,此部分代碼一定要放在finally語句中

##3.使用PreparedStatement執行SQL語句

在Statement中,可以發現在連接數據庫和釋放資源方面有代碼重復的部分,這個時候可以把重復的部分抽取出來,現在把連接部分和關閉部分的代碼抽取放到jdbcUtil這個類中,有連接方法和關閉方法,代碼示例如下:

/**
 * jdbc工具類
 * 數據庫連接和資源釋放
 */
public class jdbcUtil {
    private static String url="jdbc:mysql://localhost:3306/db";
    private static String user="root";
    private static String password="root";
    /**
     * 靜態代碼塊(只加載一次)
     */
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("驅動程序注冊出錯");
        }

    }
    /**
     * 抽取獲取連接對象的方法
     */
    public static Connection getConnect(){
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url,user,password);
            return conn;
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 釋放資源的方法
     */
    public static void close(Connection conn, Statement stmt){
        if (stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        if (conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }


    public static void close(Connection conn, Statement stmt, ResultSet rs){
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        if (stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        if (conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }
}

重復的部分已經抽取了,現在使用抽取的部分來實現PreparedStatement接口的方法 PreparedStatement接口是執行預編譯的sql語句的,最大的特點是,sql語句中的參數可以使用問號?來占用一個位置,然后再根據位置來設置sql里面的參數,最后發送到數據庫執行 PreparedStatement的CURD方法分別使用代碼表示如下: 1.插入

    /**
     * 插入數據
     */
    @Test
    public void TestInsert(){
        Connection conn = null;
        PreparedStatement pstmt = null;

        try {
            //1.獲取連接
            conn = jdbcUtil.getConnect();
            //2.准備預編譯的sql
            String sql = "INSERT INTO student(NAME,gender) VALUES(?,?)";//問號?表示一個參數的占位符
            //3.執行編譯sql語句(語法檢查)
            pstmt = conn.prepareStatement(sql);
            //4.設置參數
            //參數的設置用兩個參數,第一個指定是第幾個問號,第二個是參數的值,參數位置從1開始
            pstmt.setString(1, "李四");
            pstmt.setString(2, "男");
            //5.發送參數,執行sql
            int count = pstmt.executeUpdate();//注意此處括號里不放sql
            System.out.println("影響了"+count+"行!");

        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            jdbcUtil.close(conn,pstmt);
        }
    }

2.修改

    /**
     * 更改數據
     */
    @Test
    public void testUpdate() {
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            //1.獲取連接
            conn = jdbcUtil.getConnect();

            //2.准備預編譯的sql
            String sql = "UPDATE student SET NAME=? WHERE id=?"; //?表示一個參數的占位符

            //3.執行預編譯sql語句(檢查語法)
            stmt = conn.prepareStatement(sql);

            //4.設置參數值
            /**
             * 參數一: 參數位置  從1開始
             */
            stmt.setString(1, "王五");
            stmt.setInt(2, 9);

            //5.發送參數,執行sql
            int count = stmt.executeUpdate();

            System.out.println("影響了"+count+"行");

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            jdbcUtil.close(conn, stmt);
        }
    }

3.刪除

    /**
     * 刪除數據
     */
    @Test
    public void testDelete() {
        Connection conn = null;
        PreparedStatement stmt = null;
        try {
            //1.獲取連接
            conn = jdbcUtil.getConnect();

            //2.准備預編譯的sql
            String sql = "DELETE FROM student WHERE id=?"; //?表示一個參數的占位符

            //3.執行預編譯sql語句(檢查語法)
            stmt = conn.prepareStatement(sql);

            //4.設置參數值
            /**
             * 參數一: 參數位置  從1開始
             */
            stmt.setInt(1, 9);

            //5.發送參數,執行sql
            int count = stmt.executeUpdate();

            System.out.println("影響了"+count+"行");

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            jdbcUtil.close(conn, stmt);
        }
    }

4.查詢

    /**
     * 查詢數據
     */
    @Test
    public void testQuery() {
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rs = null;
        try {
            //1.獲取連接
            conn = jdbcUtil.getConnect();

            //2.准備預編譯的sql
            String sql = "SELECT * FROM student";

            //3.預編譯
            stmt = conn.prepareStatement(sql);

            //4.執行sql
            rs = stmt.executeQuery();

            //5.遍歷rs
            while(rs.next()){
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String gender = rs.getString("gender");
                System.out.println(id+","+name+","+gender);
            }

        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        } finally {
            //關閉資源
            jdbcUtil.close(conn,stmt,rs);
        }
    }

PreparedStatement 和 Statment 區別: 1.語法不同:PreparedStatement可以使用預編譯的sql,而Statement只能使用靜態的sql 2.效率不同:PreparedStatement可以使用sql緩存區,效率比Statement高。Oracle是支持sql緩存區的,mysql是不支持緩存區的 3.安全性不同:PreparedStatement可以有效防止sql注入,而Statement不能防止sql注入

推薦使用PreparedStatement

##4.CallableStatement執行存儲過程 CallableStatement是PreparedStatement的子類,所以可以使用PreparedStatement的方法,即也可以使用編譯sql,同時該類是用來執行存儲過程的,所以兩者之間是可以連在一起用的。 注意沒有executeUpdate方法,所有調用存儲過程都是使用executeQuery方法。 根據存儲過程的不同,將代碼分為兩種:一個是只有出入參數的存儲過程,一個是只有輸出過程的存儲過程 帶有出入參數的存儲過程

# 帶有出入參數的存儲過程
DELIMITER $
CREATE PROCEDURE pro_findById(IN sid INT)
  BEGIN
    SELECT * FROM student WHERE id=sid;
  END $

CALL pro_findById(4);

帶有輸出參數的存儲過程

# 帶有輸出參數的存儲過程
DELIMITER $
CREATE PROCEDURE pro_findById2(IN sid INT,OUT sname VARCHAR(20))
  BEGIN
    SELECT name INTO sname FROM student WHERE id=sid;
  END $

CALL pro_findById2(4,@name);

SELECT @name;

使用jdbc分別調用以上兩種存儲過程

/**
 *使用CallableStatement調用存儲過程
 */
public class CallableStatementDemo {
    /**
     *調用帶有輸入參數的存儲過程
     * CALL pro_findById(4);
     */
    @Test
    public void testIn(){
        Connection conn = null;
        CallableStatement cstmt = null;
        ResultSet rs = null;

        try {
            //1.獲取連接方法
            conn = jdbcUtil.getConnect();
            //2.准備sql
            String sql = "CALL pro_findById(?)";//可以執行預編譯sql
            //3.預編譯
            cstmt=conn.prepareCall(sql);
            //4.設置輸入參數
            cstmt.setInt(1,4);
            //5.發送參數
            rs = cstmt.executeQuery();//注意:所有調用存儲過程的sql語句都是使用executeQuery方法
            //遍歷結果
            while (rs.next()){
                int id = rs.getInt("id");
                String name = rs.getString("name");
                String gender = rs.getString("gender");
                System.out.println(id+","+name+","+gender);
            }
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            jdbcUtil.close(conn,cstmt,rs);
        }

    }


    /**
     * 執行帶有輸出參數的存儲過程
     * CALL pro_findById2(4,@NAME);
     */
    @Test
    public void TestOut(){
        Connection conn =null;
        CallableStatement cstmt = null;
        ResultSet rs = null;

        try {
            //1.獲取連接
            conn = jdbcUtil.getConnect();
            //2.准備sql
            String sql = "CALL pro_findById2(?,?)";//第一個?是輸入參數,第二個?是輸出參數
            //3.預編譯
            cstmt = conn.prepareCall(sql);
            //4.設置輸入參數
            cstmt.setInt(1,4);
            //5.設置輸出參數(注冊輸出參數)
            /**
             * 參數一:參數位置
             * 參數二:存儲過程中的輸出參數的jdbc類型
             */
            cstmt.registerOutParameter(2, Types.VARCHAR);
            //6.發送參數,執行
            cstmt.executeQuery();//結果不是返回到結果集中,而是返回到輸出參數中
            //7.得到輸出參數的值
            /**
             * 索引值:預編譯sql中的輸出參數的位置
             */
            String result = cstmt.getString(2);//getXX方法專門用於獲取存儲過程中的輸出參數

            System.out.println(result);
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }finally {
            jdbcUtil.close(conn,cstmt,rs);
        }

    }
}

##5.類路徑的讀取和jdbcUtil的配置文件

在jdbcUtil類中,我們把數據庫,驅動,用戶名,密碼寫死了,以后想要修改就很麻煩,這種情況,我們會把配置文件再次抽取出來,創建一個db.properties文件來保存一些配置文件。需要輸入信息,在這個文件里面配置就可以了 首先,配置文件是這樣的:

url=jdbc:mysql://localhost:3306/db
user=root
password=123456
driverClass=com.mysql.jdbc.Driver

然后,在jdbcUtil類中調用:

/**
 * jdbc工具類
 * 數據庫連接和資源釋放
 */
public class jdbcUtil {
    private static String url=null;
    private static String user=null;
    private static String password=null;
    private static String driverClass=null;
    /**
     * 靜態代碼塊(只加載一次)
     */
    static {
        try {
            //讀取db.properties
            Properties props = new Properties();

            /**
             * 使用類路徑的讀取方法
             */
            /**
             *  . :代表java命令運行的目錄
             *  在java項目下:點 . java命令的運行目錄從項目的根目錄開始
             *  在web項目下: 點 . java命令的運行目錄從tomcat/bin目錄開始
             *  使用點.來指定目錄會產生在java 項目和javaweb項目中不適用的情況
             */
            /**
             * /:斜杠表示classpath的根目錄
             * 在java項目下,classpath的根目錄從bin目錄開始
             * 在web項目下,classpath的根目錄從WEB-INF/classes目錄開始。
             * 而db.properties就在/目錄下
             * 一般都是使用/來指定按文件名字
             */
            InputStream in = jdbcUtil.class.getResourceAsStream("/db.properties");
            //加載文件
            props.load(in);
            //讀取信息
            url=props.getProperty("url");
            user=props.getProperty("user");
            password=props.getProperty("password");
            driverClass=props.getProperty("driverClass");

            Class.forName(driverClass);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            System.out.println("驅動程序注冊出錯");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    /**
     * 抽取獲取連接對象的方法
     */
    public static Connection getConnect(){
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url,user,password);
            return conn;
        } catch (SQLException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 釋放資源的方法
     */
    public static void close(Connection conn, Statement stmt){
        if (stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        if (conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }


    public static void close(Connection conn, Statement stmt, ResultSet rs){
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        if (stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        if (conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
    }
}

其中比較重要的就是文件路徑的查找,因為在java項目和javaweb項目中的文件路徑是不一樣的,這個時候就可以使用類路徑來查找。java中也通常使用點和斜杠來指定路徑,但是指定的路徑是不一樣的: 點指定路徑: . :代表java命令運行的目錄 在java項目下:點 . java命令的運行目錄從項目的根目錄開始 在web項目下: 點 . java命令的運行目錄從tomcat/bin目錄開始 使用點.來指定目錄會產生在java 項目和javaweb項目中不適用的情況

斜杠指定路徑: /:斜杠表示classpath的根目錄 在java項目下,classpath的根目錄從bin目錄開始 在web項目下,classpath的根目錄從WEB-INF/classes目錄開始。 而db.properties就在/目錄下 一般都是使用/來指定按文件名字


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM