JNDI+springmvc使用

2 篇文章 0 订阅

1.JNDI简介

1-1是什么

JNDI是Java命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一。是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层上。

JNDI可访问的现有的目录及服务有:

  • DNS
  • XNam
  • Novell目录服务
  • LDAP(Lightweight Directory Access Protocol轻型目录访问协议)
  • CORBA对象服务
  • 文件系统、Windows XP/2000/NT/Me/9x的注册表
  • RMI、DSML v1&v2、NIS 等

1-2.组件

  • Javax.naming:包含了访问命名服务的类和接口。

例如,它定义了Context接口,这是命名服务执行查询的入口。

  • Javax.naming.directory:对命名包的扩充,提供了访问目录服务的类和接口。

例如,它为属性增加了新的类,提供了表示目录上下文的DirContext接口,定义了检查和更新目录对象的属性的方法。

  • Javax.naming.event:提供了对访问命名和目录服务时的事件通知的支持。

例如,定义了NamingEvent类,这个类用来表示命名/目录服务产生的事件,定义了侦听NamingEvents的NamingListener接口。

  • Javax.naming.ldap:这个包提供了对LDAP版本3扩充的操作和控制的支持,通用包javax.naming.directory没有包含这些操作和控制。
  • Javax.naming.spi:这个包提供了一个方法,通过javax.naming和有关包动态增加对访问命名和目录服务的支持。这个包是为有兴趣创建服务提供者的开发者提供的。

1-3.源码

本篇源码地址

关键代码都贴出来了,不看源码也可以。

2.JDBC驱动

步骤:

  • 加载数据库驱动程序 Class.forName("数据库驱动类");
  • 连接数据库 Connection con = DriverManager.getConnection();
  • 操作数据库 PreparedStatement stat = con.prepareStatement(sql);stat.executeQuery();
  • 关闭数据库,释放连接 con.close();

本篇文件用到的数据库表和数据,请参照 mysql常用sql 里,进行建表和插入数据

依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.15</version>
</dependency>

实体类

public class Emp {

    private long empno;
    private String ename;
    private String job;
    private long mgr;
    private Date hiredate;
    private double sal;
    private double comm;
    private long deptno;

    ...省略setter、getter、toString...
}

MySQLDemo

public class MySQLDemo {

    private static final String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
    // private static final String DRIVER_NAME = "com.mysql.jdbc.Driver";
    private static final String JDBC_URL = "jdbc:mysql://localhost:3306";
    private static final String USER = "iworkh";
    private static final String PASSWORD = "iworkh123";


    public static void main(String[] args) {
        Connection conn = null;

        try {
            Class.forName(DRIVER_NAME);
            conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);

            String sql= "select * from iworkh_demo.emp where ename like ?";
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1,"%M%");

            ResultSet resultSet = ps.executeQuery();
            while (resultSet.next()){
                Emp emp = new Emp();
                emp.setEmpno(resultSet.getLong("empno"));
                emp.setEname(resultSet.getString("ename"));
                emp.setJob(resultSet.getString("job"));
                emp.setMgr(resultSet.getLong("mgr"));
                emp.setHiredate( resultSet.getDate("hiredate"));
                emp.setSal(resultSet.getDouble("sal"));
                emp.setComm(resultSet.getDouble("comm"));
                emp.setDeptno(resultSet.getLong("deptno"));
                System.out.println(emp);
            }
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

这种做法存在的问题:

  • 数据库服务器名称MyDBServer 、用户名和口令都可能需要改变,由此引发JDBC URL需要修改;
  • 数据库可能改用别的产品,如改用DB2或者Oracle,引发JDBC驱动程序包和类名需要修改;
  • 随着实际使用终端的增加,原配置的连接池参数可能需要调整;

代码应该不需要关心

  • 具体的数据库后台是什么?
  • JDBC驱动程序是什么?
  • JDBC URL格式是什么?
  • 访问数据库的用户名和口令是什么?

程序应该没有对 JDBC 驱动程序的引用,没有服务器名称,没有用户名称或口令
甚至没有数据库池或连接管理。而是把这些问题交给J2EE容器来配置和管理,只需要对这些配置和管理进行引用即可。

3.springmvc+JDNI形式

代码结构


下面使用了spring、springmvc等知识,如果不足的可以通过下面提供的链接,自己补充下。

3-1.依赖

<packaging>war</packaging>

项目打包指定为war

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.11.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.11.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.11.0</version>
    </dependency>
</dependencies>

引入springmvc依赖、mysql驱动、jackson依赖

3-2.web.xml

文件路径:src/main/webapp/WEB-INF/web.xml

<web-app version="2.4"
         xmlns="http://java.sun.com/xml/ns/j2ee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
         http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

    <display-name>Archetype Created Web Application</display-name>

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath*:spring-bean.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <filter>
        <filter-name>encodingFilter</filter-name>
        <filter-class>
            org.springframework.web.filter.CharacterEncodingFilter
        </filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>

    <filter-mapping>
        <filter-name>encodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath*:spring-mvc.xml</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

3-3.context.xml

文件路径:src/main/webapp/WEB-INF/context.xml

<?xml version="1.0" encoding="UTF-8"?>

<Context>
    <Resource auth="Container" driverClassName="com.mysql.cj.jdbc.Driver"
              maxActive="20" maxIdel="10" maxWait="1000" type="javax.sql.DataSource"
              name="jdbc/iworkh_demo_read" username="iworkh" password="iworkh123"
              url="jdbc:mysql://localhost:3306/iworkh_demo"/>

</Context>
  • factory:该Resource 配置使用的是哪个数据源配置类,这里使用的是tomcat自带的标准数据源Resource配置类,这个类也可以自己写,实现javax.naming.spi.ObjectFactory 接口即可。

如果使用其他的数据池,如阿里巴巴的druid,要满足两个条件:

  • 其实现了javax.naming.spi.ObjectFactory,druid的com.alibaba.druid.pool.DruidDataSourceFactory就实现了
  • 需把jar及其依赖的jar包 ,都放在tomcat的lib下,光放在工程的WEB-INF/lib下是不够的。阿里巴巴的druid依赖log4j,所以后者的jar也要复制进去
  • driverClassName
  • msyql: com.mysql.cj.jdbc.Driver
  • oracle:oracle.jdbc.driver.OracleDriver
  • db2:com.ibm.db2.jcc.DB2Driver
  • SQLServer:com.microsoft.sqlserver.jdbc.SQLServerDriver
  • url的其他写法示例
  • mysql: jdbc:mysql://localhost:3306/iworkh_demo
  • oracle:jdbc:oracle:thin:@192.168.1.249:1521:XE
  • db2:jdbc:db2://18.1.99.7:55555/esdb:currentSchema=EMPSAL

配置方法有多种,这使用conf/context.xml改配置

注意

需要将context.xml文件复制到tomcat的安装目录的conf下(对context.xml文件修改、覆盖都行)。

其他方式有兴趣可以阅读下面几篇文章

3-4.spring配置文件

文件路径:src/main/resources/spring-mvc.xml

<?xml version="1.0" encoding="utf-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <mvc:annotation-driven/>

    <context:component-scan base-package="com.iworkh.dao.jndi" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

</beans>

文件路径:src/main/resources/spring-bean.xml

方式一

<?xml version="1.0" encoding="utf-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee.xsd">

    <context:component-scan base-package="com.iworkh.dao.jndi" use-default-filters="true">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation"
                                expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

    <bean id="iworkhDemoRead" class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiName" value="java:comp/env/jdbc/iworkh_demo_read"/>
    </bean>

</beans>

方式二

<?xml version="1.0" encoding="utf-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee.xsd">

    <context:component-scan base-package="com.iworkh.dao.jndi" use-default-filters="true">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation"
                                expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

    <jee:jndi-lookup id="iworkhDemoRead" jndi-name="jdbc/iworkh_demo_read"/>
    <!-- <jee:jndi-lookup id="iworkhDemoRead" jndi-name="java:comp/env/jdbc/iworkh_demo_read"/>-->

</beans>

JNDI地址有两种写法

  • java:comp/env/jdbc/iworkh_demo_read
  • jdbc/iworkh_demo_read

在J2EE中的引用常用的有:

  • JDBC 数据源引用在java:comp/env/jdbc 子上下文中声明
  • JMS 连接工厂在java:comp/env/jms 子上下文中声明
  • JavaMail 连接工厂在java:comp/env/mail 子上下文中声明
  • URL 连接工厂在 java:comp/env/url 子上下文中声明

3-5.代码

Emp的实体类

public class Emp {

    private long empno;
    private String ename;
    private String job;
    private long mgr;
    private Date hiredate;
    private double sal;
    private double comm;
    private long deptno;
    ...省略setter、getter、toString方法...
}

Emp的dao

@Repository
public class JndiEmpDao {

    @Autowired
    private DataSource iworkhDemoRead;

    public List<Emp> list() {
        Connection conn = null;
        try {
            conn = iworkhDemoRead.getConnection();

            String sql = "select * from iworkh_demo.emp where ename like ?";

            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, "%M%");
            ResultSet resultSet = ps.executeQuery();

            List<Emp> empList = new LinkedList<>();
            while (resultSet.next()) {
                Emp emp = new Emp();
                emp.setEmpno(resultSet.getLong("empno"));
                emp.setEname(resultSet.getString("ename"));
                emp.setJob(resultSet.getString("job"));
                emp.setMgr(resultSet.getLong("mgr"));
                emp.setHiredate(resultSet.getDate("hiredate"));
                emp.setSal(resultSet.getDouble("sal"));
                emp.setComm(resultSet.getDouble("comm"));
                emp.setDeptno(resultSet.getLong("deptno"));
                empList.add(emp);
            }

            return empList;

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return null;
    }
}

Emp的service

@Service
public class JndiEmpService {

    @Autowired
    private JndiEmpDao jndiEmpDao;

    public List<Emp> list() {
        return jndiEmpDao.list();
    }
}

Emp的controller

@RestController
@RequestMapping("/emp")
public class JndiEmpController {

    @Autowired
    private JndiEmpService jndiDemoService;

    @GetMapping("/list")
    public List<Emp>  list() {
        return jndiDemoService.list();
    }
}

测试controller

@RestController
@RequestMapping("/")
public class IndexController {
    @GetMapping("/")
    public String index() {
        return "hello,world";
    }

    @GetMapping("/data")
    public List<String> data(){
        List<String> data = new ArrayList<>();
        data.add("111");
        data.add("222");
        return data;
    }
}

3-6.测试

  • http://localhost:8080/
  • http://localhost:8080/data
  • http://localhost:8080/jndi-web/emp/list

示例结果

[
    {
    "empno": 7369,
    "ename": "SMITH",
    "job": "CLERK",
    "mgr": 7902,
    "hiredate": 345830400000,
    "sal": 800,
    "comm": 0,
    "deptno": 20
    },
    {
      ...省略其他...
    }
]

4.读写分离库

4-1.sql

建库

CREATE DATABASE IF NOT EXISTS iworkh_demo
    DEFAULT CHARACTER SET utf8mb4
    DEFAULT COLLATE utf8mb4_general_ci;

建表

create table t_order
(
    id int unsigned auto_increment primary key COMMENT 'id',
    product_name  varchar(100) COMMENT '产品名',
    count    int(50) COMMENT '数量', 
		price    decimal(7, 2) COMMENT '单价'
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4 COMMENT ='订单表';

4-2.配置

文件位置:src/main/webapp/WEB-INF/context.xml

<?xml version='1.0' encoding='utf-8'?>
<Context>

    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>

    <Resource auth="Container" driverClassName="com.mysql.cj.jdbc.Driver"
              maxActive="20" maxIdel="10" maxWait="1000" type="javax.sql.DataSource"
              name="jdbc/iworkh_demo_read" username="iworkh" password="iworkh123"
              url="jdbc:mysql://localhost:3306/iworkh_demo"/>

    <Resource auth="Container" driverClassName="com.mysql.cj.jdbc.Driver"
              maxActive="20" maxIdel="10" maxWait="1000" type="javax.sql.DataSource"
              name="jdbc/iworkh_order_read" username="iworkh" password="iworkh123"
              url="jdbc:mysql://localhost:3306/iworkh_order"/>

    <Resource auth="Container" driverClassName="com.mysql.cj.jdbc.Driver"
              maxActive="20" maxIdel="10" maxWait="1000" type="javax.sql.DataSource"
              name="jdbc/iworkh_order_write" username="iworkh" password="iworkh123"
              url="jdbc:mysql://localhost:3306/iworkh_order"/>
</Context>

为了简单起见,写和读的库都使用一样。这配置完全可以使用不一样的。一个库为写库,一个库为读库。

文件位置:src/main/webapp/WEB-INF/context.xml

<?xml version="1.0" encoding="utf-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee.xsd">

    <context:component-scan base-package="com.iworkh.dao.jndi" use-default-filters="true">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
        <context:exclude-filter type="annotation"
                                expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
    </context:component-scan>

    <jee:jndi-lookup id="iworkhDemoRead" jndi-name="jdbc/iworkh_demo_read"/>
    <jee:jndi-lookup id="iworkhOrderRead" jndi-name="jdbc/iworkh_order_read"/>
    <jee:jndi-lookup id="iworkhOrderWrite" jndi-name="jdbc/iworkh_order_write"/>
</beans>

4-3.代码

实体类

public class Order {
    private int id;
    private String productName;
    private int count;
    private double price;
    ...省略setter、getter、toString方法...
}

dao

@Repository
public class OrderDao extends BaseDao {
    public void add(Order order) {
        Connection conn = null;
        try {
            System.out.println("add---" + iworkhOrderWrite.hashCode());
            conn = iworkhOrderWrite.getConnection();
            String sql = "insert into t_order (`product_name`, `count`, `price`) values (?, ?, ?)";
            PreparedStatement ps = conn.prepareStatement(sql);
            ps.setString(1, order.getProductName());
            ps.setInt(2, order.getCount());
            ps.setDouble(3, order.getPrice());
            ps.executeUpdate();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }

    public List<Order> list() {
        Connection conn = null;
        try {
            System.out.println("list---" + iworkhOrderRead.hashCode());
            conn = iworkhOrderRead.getConnection();
            String sql = "select * from t_order";
            PreparedStatement ps = conn.prepareStatement(sql);
            ResultSet resultSet = ps.executeQuery();
            List<Order> orderList = new LinkedList<>();
            while (resultSet.next()) {
                Order order = new Order();
                order.setId(resultSet.getInt("id"));
                order.setProductName(resultSet.getString("product_name"));
                order.setCount(resultSet.getInt("count"));
                order.setPrice(resultSet.getDouble("price"));
                orderList.add(order);
            }
            return orderList;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return null;
    }
}

service

@Service
public class OrderService {

    @Autowired
    private OrderDao orderDao;

    public void add(Order order) {
        orderDao.add(order);
    }

    public List<Order> list() {
        return orderDao.list();
    }
}

controller

@RestController
@RequestMapping("/order")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @GetMapping("/add")
    public void add() {
        Order order = new Order();
        order.setProductName("dell显示器");
        order.setCount(1);
        order.setPrice(2100.00);
        orderService.add(order);
    }

    @GetMapping("/list")
    public List<Order> list() {
        return orderService.list();
    }
}

4-4.测试

  • http://localhost:8080/
  • http://localhost:8080/data
  • http://localhost:8080/jndi-web/emp/list
  • http://localhost:8080/jndi-web/order/add
  • http://localhost:8080/jndi-web/order/list

查看order里的add和list方法的hash code是不一样的。即达到了读写分离的效果。

5.分库分表中间件

分表内容有点多,直接另写一篇文章。请参照下篇文章 分库分表中间件

6.扩展链接

能读到文章最后,首先得谢谢您对本文的肯定,你的肯定是对博主最大的鼓励。

你觉本文有帮助,那就点个👍
你有疑问,那就留下您的💬
怕把我弄丢了,那就把我⭐
电脑不方便看,那就把发到你📲

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值