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.扩展链接
- 个人博客
- JNDI在J2EE中的角色
- JNDI在Spring和tomcat下的使用
- springmvc与spring整合
- idea启tomcat跑springmvc项目
- Spring配置JNDI
- tomcat jndi spring配置
能读到文章最后,首先得谢谢您对本文的肯定,你的肯定是对博主最大的鼓励。
你觉本文有帮助,那就点个👍
你有疑问,那就留下您的💬
怕把我弄丢了,那就把我⭐
电脑不方便看,那就把发到你📲