JNDI是Java命名与目录接口(Java Naming and Directory Interface),在J2EE规范中是重要的规范之一。是一个应用程序设计的API,为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口,类似JDBC都是构建在抽象层上。
JNDI可访问的现有的目录及服务有:
DNSXNamNovell目录服务LDAP(Lightweight Directory Access Protocol轻型目录访问协议)CORBA对象服务文件系统、Windows XP/2000/NT/Me/9x的注册表RMI、DSML v1&v2、NIS 等例如,它定义了Context接口,这是命名服务执行查询的入口。
Javax.naming.directory:对命名包的扩充,提供了访问目录服务的类和接口。例如,它为属性增加了新的类,提供了表示目录上下文的DirContext接口,定义了检查和更新目录对象的属性的方法。
Javax.naming.event:提供了对访问命名和目录服务时的事件通知的支持。例如,定义了NamingEvent类,这个类用来表示命名/目录服务产生的事件,定义了侦听NamingEvents的NamingListener接口。
Javax.naming.ldap:这个包提供了对LDAP版本3扩充的操作和控制的支持,通用包javax.naming.directory没有包含这些操作和控制。Javax.naming.spi:这个包提供了一个方法,通过javax.naming和有关包动态增加对访问命名和目录服务的支持。这个包是为有兴趣创建服务提供者的开发者提供的。本篇源码地址
关键代码都贴出来了,不看源码也可以。
步骤:
加载数据库驱动程序 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容器来配置和管理,只需要对这些配置和管理进行引用即可。
代码结构
下面使用了spring、springmvc等知识,如果不足的可以通过下面提供的链接,自己补充下。
springmvc与spring整合idea启tomcat跑springmvc项目项目打包指定为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依赖
文件路径: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>文件路径: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.Driveroracle:oracle.jdbc.driver.OracleDriverdb2:com.ibm.db2.jcc.DB2DriverSQLServer:com.microsoft.sqlserver.jdbc.SQLServerDriver url的其他写法示例 mysql: jdbc:mysql://localhost:3306/iworkh_demooracle:jdbc:oracle:thin:@192.168.1.249:1521:XEdb2:jdbc:db2://18.1.99.7:55555/esdb:currentSchema=EMPSAL配置方法有多种,这使用conf/context.xml改配置
注意
需要将context.xml文件复制到tomcat的安装目录的conf下(对context.xml文件修改、覆盖都行)。
其他方式有兴趣可以阅读下面几篇文章
Spring配置JNDItomcat jndi 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_readjdbc/iworkh_demo_read在J2EE中的引用常用的有:
JDBC 数据源引用在java:comp/env/jdbc 子上下文中声明JMS 连接工厂在java:comp/env/jms 子上下文中声明JavaMail 连接工厂在java:comp/env/mail 子上下文中声明URL 连接工厂在 java:comp/env/url 子上下文中声明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; } }示例结果
[ { "empno": 7369, "ename": "SMITH", "job": "CLERK", "mgr": 7902, "hiredate": 345830400000, "sal": 800, "comm": 0, "deptno": 20 }, { ...省略其他... } ]建库
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 ='订单表';文件位置: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>实体类
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(); } }查看order里的add和list方法的hash code是不一样的。即达到了读写分离的效果。
分表内容有点多,直接另写一篇文章。请参照下篇文章 分库分表中间件
能读到文章最后,首先得谢谢您对本文的肯定,你的肯定是对博主最大的鼓励。
你觉本文有帮助,那就点个👍 你有疑问,那就留下您的💬 怕把我弄丢了,那就把我⭐ 电脑不方便看,那就把发到你📲