再说JNDI

说到JNDI,即熟悉又陌生,熟悉在经常使用,如EJB3.0中的@EJB注入,底层实现即是JNDI的方式;喜闻乐见的:

Context ctx=new InitialContext();
Object obj=(Object)ctx.lookup("java:comp/env/XXX");

更是最常用的方式。说它陌生,是因为,对于JNDI,我们仅限于基本的使用,本文就是带领大家进入JNDI分析阶段。

JNDI作用

以数据源为例。

未使用JNDI

如果我们没有使用JNDI,代码如下

Connection conn=null;
try {
  Class.forName("com.mysql.jdbc.Driver",
                true, Thread.currentThread().getContextClassLoader());
  conn=DriverManager.getConnection("jdbc:mysql://MyDBServer?user=qingfeng&password=mingyue");
  /* 使用conn并进行SQL操作 */
  ......
  conn.close();
}
catch(Exception e) {
  e.printStackTrace();
}
finally {
  if(conn!=null) {
    try {
      conn.close();
    } catch(SQLException e) {}
  }
}

这种方法有很大的问题,如果数据库变了怎么办?如果口令变了怎么办?写的太死,灵活性差。

使用JNDI

在来看看使用了JNDI的方式。

配置

<?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local-tx-datasource>
    <jndi-name>MySqlDS</jndi-name>
    <connection-url>jdbc:mysql://localhost:3306/lw</connection-url>
    <driver-class>com.mysql.jdbc.Driver</driver-class>
    <user-name>root</user-name>
    <password>rootpassword</password>
<exception-sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter</exception-sorter-class-name>
    <metadata>
       <type-mapping>mySQL</type-mapping>
    </metadata>
</local-tx-datasource>
</datasources>

使用

Connection conn=null;
try {
  Context ctx=new InitialContext();
  Object datasourceRef=ctx.lookup("java:MySqlDS"); //引用数据源
  DataSource ds=(Datasource)datasourceRef;
  conn=ds.getConnection();
  /* 使用conn进行数据库SQL操作 */
  ......
  c.close();
}
catch(Exception e) {
  e.printStackTrace();
}
finally {
  if(conn!=null) {
    try {
      conn.close();
    } catch(SQLException e) { }
  }
}

小结

使用JNDI,可以将代码和数据库配置分离,降低了耦合,使得程序更灵活,可以看到JNDI的使用方式:

  • 定义内容(名称,内容)
  • 根据名称使用内容

这只是JNDI的用处之一,你可以使用JNDI配置和使用更多内容,知道大概怎么使用,开始介绍JNDI。

JNDI

JNDI(Java Naming and Directory Interface)Java 命名与目录接口,JavaEE规范中重要的规范之一,前面的文章说到过:每一个规范都是对编程某一方面的抽象定义,JNDI定位于:定义、查找。它为编程人员提供了一个统一的方式,不管是目录、文件、注册表、对象、配置等等,我们可以只根据它们的名字就可以以统一的方式操作他们。

规范源码

规范位于命名空间javax.naming下:

上面是部分规范源代码,由上图可以知道,JNDI规范分为5部分:

* javax.naming

* javax.naming.directory

* javax.naming.event

* javax.naming.ldap

* javax.naming.spi

从更宏观的方面来说,JNDI规范分为两部分,API和SPI:

  • API:编程人员可见的接口,可以直接使用
  • SPI:API底层的实现接口,对编程人员不可见

架构图

JNDI宏观架构图如下:

如上,各种厂商提供的产品,他们的命名和目录服务的标准不一致,各个目录服务采用的访问协议也不一样(跟JDBC类同),如果直接访问,需要编写不同的java代码。因此需要JNDI SPI,它能动态的插入这些命名和目录服务,能够将其协议专属的目录产品集成到系统中使得开发人员只需要知道名字,即可获取到操作各种类型的内容。

当前JNDI支持的操作类型为:DNS、XNam 、Novell目录服务、LDAP(轻型目录访问协议)、 CORBA对象服务、文件系统、Windows注册表、RMI、DSML、NIS。

源码分析

JNDI只是规范,所以我们编程还是需要具体实现,当然一般各种容器、服务器都会提供实现,本文我们以Tomcat为例。

首先是下载tomcat源码,可以从《Tomcat 8.0.10源码》,也可以从Tomcat官方SVN下载源码:http://svn.apache.org/repos/asf/tomcat/。在下载的源码中,JNDI的相关实现位于\java\org\apache\naming下,我们此处只分析最核心的一个类,也就是我们经常使用的Context
ctx=new InitialContext();中的“InitailContext”,在Tomcat中是NamingContext.java:

/**
 * Catalina JNDI Context implementation.
 *
 * @author Remy Maucherat
 */
public class NamingContext implements Context {

    // -------------------------------------------------------------- Constants

    /**
     * Name parser for this context.
     */
    protected static final NameParser nameParser = new NameParserImpl();

    private static final org.apache.juli.logging.Log log =
        org.apache.juli.logging.LogFactory.getLog(NamingContext.class);

    // ----------------------------------------------------------- Constructors

    /**
     * Builds a naming context using the given environment.
     */
    public NamingContext(Hashtable<String,Object> env, String name,
            HashMap<String,NamingEntry> bindings) throws NamingException {

        this.env = new Hashtable<>();
        // FIXME ? Could be put in the environment ?
        this.name = name;
        // Populating the environment hashtable
        if (env != null ) {
            Enumeration<String> envEntries = env.keys();
            while (envEntries.hasMoreElements()) {
                String entryName = envEntries.nextElement();
                addToEnvironment(entryName, env.get(entryName));
            }
        }
        this.bindings = bindings;
    }
    // ----------------------------------------------------- Instance Variables

    /**
     * Environment.
     */
    protected final Hashtable<String,Object> env;
    /**
     * The string manager for this package.
     */
    protected static final StringManager sm = StringManager.getManager(Constants.Package);
    /**
     * Bindings in this Context.
     */
    protected final HashMap<String,NamingEntry> bindings;
    /**
     * Name of the associated Catalina Context.
     */
    protected final String name;
    /**
     * Determines if an attempt to write to a read-only context results in an
     * exception or if the request is ignored.
     */
    private boolean exceptionOnFailedWrite = true;

    // -------------------------------------------------------- Context Methods
    @Override
    public void unbind(Name name) throws NamingException {

        if (!checkWritable()) {
            return;
        }

        while ((!name.isEmpty()) && (name.get(0).length() == 0))
            name = name.getSuffix(1);
        if (name.isEmpty())
            throw new NamingException
                (sm.getString("namingContext.invalidName"));

        NamingEntry entry = bindings.get(name.get(0));

        if (entry == null) {
            throw new NameNotFoundException
                (sm.getString("namingContext.nameNotBound", name, name.get(0)));
        }

        if (name.size() > 1) {
            if (entry.type == NamingEntry.CONTEXT) {
                ((Context) entry.value).unbind(name.getSuffix(1));
            } else {
                throw new NamingException
                    (sm.getString("namingContext.contextExpected"));
            }
        } else {
            bindings.remove(name.get(0));
        }

    }
    @Override
    public void rename(Name oldName, Name newName)
        throws NamingException {
        Object value = lookup(oldName);
        bind(newName, value);
        unbind(oldName);
    }

    @Override
    public NamingEnumeration<NameClassPair> list(Name name)
        throws NamingException {
        // Removing empty parts
        while ((!name.isEmpty()) && (name.get(0).length() == 0))
            name = name.getSuffix(1);
        if (name.isEmpty()) {
            return new NamingContextEnumeration(bindings.values().iterator());
        }

        NamingEntry entry = bindings.get(name.get(0));

        if (entry == null) {
            throw new NameNotFoundException
                (sm.getString("namingContext.nameNotBound", name, name.get(0)));
        }

        if (entry.type != NamingEntry.CONTEXT) {
            throw new NamingException
                (sm.getString("namingContext.contextExpected"));
        }
        return ((Context) entry.value).list(name.getSuffix(1));
    }

    /**
     * Enumerates the names bound in the named context, along with the
     * objects bound to them. The contents of any subcontexts are not
     * included.
     * <p>
     * If a binding is added to or removed from this context, its effect on
     * an enumeration previously returned is undefined.
     *
     * @param name the name of the context to list
     * @return an enumeration of the bindings in this context.
     * Each element of the enumeration is of type Binding.
     * @exception NamingException if a naming exception is encountered
     */
    @Override
    public NamingEnumeration<Binding> listBindings(Name name)
        throws NamingException {
        // Removing empty parts
        while ((!name.isEmpty()) && (name.get(0).length() == 0))
            name = name.getSuffix(1);
        if (name.isEmpty()) {
            return new NamingContextBindingsEnumeration(bindings.values().iterator(), this);
        }

        NamingEntry entry = bindings.get(name.get(0));

        if (entry == null) {
            throw new NameNotFoundException
                (sm.getString("namingContext.nameNotBound", name, name.get(0)));
        }

        if (entry.type != NamingEntry.CONTEXT) {
            throw new NamingException
                (sm.getString("namingContext.contextExpected"));
        }
        return ((Context) entry.value).listBindings(name.getSuffix(1));
    }

    /**
     * @param name the name of the context to be destroyed; may not be empty
     * @exception NameNotFoundException if an intermediate context does not
     * exist
     * @exception NotContextException if the name is bound but does not name
     * a context, or does not name a context of the appropriate type
     */
    @Override
    public void destroySubcontext(Name name) throws NamingException {

        if (!checkWritable()) {
            return;
        }

        while ((!name.isEmpty()) && (name.get(0).length() == 0))
            name = name.getSuffix(1);
        if (name.isEmpty())
            throw new NamingException
                (sm.getString("namingContext.invalidName"));

        NamingEntry entry = bindings.get(name.get(0));

        if (entry == null) {
            throw new NameNotFoundException
                (sm.getString("namingContext.nameNotBound", name, name.get(0)));
        }

        if (name.size() > 1) {
            if (entry.type == NamingEntry.CONTEXT) {
                ((Context) entry.value).destroySubcontext(name.getSuffix(1));
            } else {
                throw new NamingException
                    (sm.getString("namingContext.contextExpected"));
            }
        } else {
            if (entry.type == NamingEntry.CONTEXT) {
                ((Context) entry.value).close();
                bindings.remove(name.get(0));
            } else {
                throw new NotContextException
                    (sm.getString("namingContext.contextExpected"));
            }
        }

    }

    /**
     * Creates and binds a new context. Creates a new context with the given
     * name and binds it in the target context (that named by all but
     * terminal atomic component of the name). All intermediate contexts and
     * the target context must already exist.
     *
     * @param name the name of the context to create; may not be empty
     * @return the newly created context
     * @exception NameAlreadyBoundException if name is already bound
     * @exception javax.naming.directory.InvalidAttributesException if creation
     * of the sub-context requires specification of mandatory attributes
     * @exception NamingException if a naming exception is encountered
     */
    @Override
    public Context createSubcontext(Name name) throws NamingException {
        if (!checkWritable()) {
            return null;
        }

        NamingContext newContext = new NamingContext(env, this.name);
        bind(name, newContext);

        newContext.setExceptionOnFailedWrite(getExceptionOnFailedWrite());

        return newContext;
    }

    /**
     * Adds a new environment property to the environment of this context. If
     * the property already exists, its value is overwritten.
     *
     * @param propName the name of the environment property to add; may not
     * be null
     * @param propVal the value of the property to add; may not be null
     * @exception NamingException if a naming exception is encountered
     */
    @Override
    public Object addToEnvironment(String propName, Object propVal)
        throws NamingException {
        return env.put(propName, propVal);
    }

    /**
     * Removes an environment property from the environment of this context.
     *
     * @param propName the name of the environment property to remove;
     * may not be null
     * @exception NamingException if a naming exception is encountered
     */
    @Override
    public Object removeFromEnvironment(String propName)
        throws NamingException {
        return env.remove(propName);
    }

    /**
     * Retrieves the environment in effect for this context. See class
     * description for more details on environment properties.
     * The caller should not make any changes to the object returned: their
     * effect on the context is undefined. The environment of this context
     * may be changed using addToEnvironment() and removeFromEnvironment().
     *
     * @return the environment of this context; never null
     * @exception NamingException if a naming exception is encountered
     */
    @Override
    public Hashtable<?,?> getEnvironment()
        throws NamingException {
        return env;
    }

    /**
     * Closes this context. This method releases this context's resources
     * immediately, instead of waiting for them to be released automatically
     * by the garbage collector.
     * This method is idempotent: invoking it on a context that has already
     * been closed has no effect. Invoking any other method on a closed
     * context is not allowed, and results in undefined behaviour.
     *
     * @exception NamingException if a naming exception is encountered
     */
    @Override
    public void close() throws NamingException {
        if (!checkWritable()) {
            return;
        }
        env.clear();
    }

    /**
     * Retrieves the full name of this context within its own namespace.
     * <p>
     * Many naming services have a notion of a "full name" for objects in
     * their respective namespaces. For example, an LDAP entry has a
     * distinguished name, and a DNS record has a fully qualified name. This
     * method allows the client application to retrieve this name. The string
     * returned by this method is not a JNDI composite name and should not be
     * passed directly to context methods. In naming systems for which the
     * notion of full name does not make sense,
     * OperationNotSupportedException is thrown.
     *
     * @return this context's name in its own namespace; never null
     * @exception OperationNotSupportedException if the naming system does
     * not have the notion of a full name
     * @exception NamingException if a naming exception is encountered
     */
    @Override
    public String getNameInNamespace()
        throws NamingException {
        throw  new OperationNotSupportedException
            (sm.getString("namingContext.noAbsoluteName"));
        //FIXME ?
    }

    // ------------------------------------------------------ Protected Methods

    /**
     * Retrieves the named object.
     *
     * @param name the name of the object to look up
     * @param resolveLinks If true, the links will be resolved
     * @return the object bound to name
     * @exception NamingException if a naming exception is encountered
     */
    protected Object lookup(Name name, boolean resolveLinks)
        throws NamingException {

        // Removing empty parts
        while ((!name.isEmpty()) && (name.get(0).length() == 0))
            name = name.getSuffix(1);
        if (name.isEmpty()) {
            // If name is empty, a newly allocated naming context is returned
            return new NamingContext(env, this.name, bindings);
        }

        NamingEntry entry = bindings.get(name.get(0));

        if (entry == null) {
            throw new NameNotFoundException
                (sm.getString("namingContext.nameNotBound", name, name.get(0)));
        }

        if (name.size() > 1) {
            // If the size of the name is greater that 1, then we go through a
            // number of subcontexts.
            if (entry.type != NamingEntry.CONTEXT) {
                throw new NamingException
                    (sm.getString("namingContext.contextExpected"));
            }
            return ((Context) entry.value).lookup(name.getSuffix(1));
        } else {
            if ((resolveLinks) && (entry.type == NamingEntry.LINK_REF)) {
                String link = ((LinkRef) entry.value).getLinkName();
                if (link.startsWith(".")) {
                    // Link relative to this context
                    return lookup(link.substring(1));
                } else {
                    return (new InitialContext(env)).lookup(link);
                }
            } else if (entry.type == NamingEntry.REFERENCE) {
                try {
                    Object obj = NamingManager.getObjectInstance
                        (entry.value, name, this, env);
                    if(entry.value instanceof ResourceRef) {
                        boolean singleton = Boolean.parseBoolean(
                                    (String) ((ResourceRef) entry.value).get(
                                        "singleton").getContent());
                        if (singleton) {
                            entry.type = NamingEntry.ENTRY;
                            entry.value = obj;
                        }
                    }
                    return obj;
                } catch (NamingException e) {
                    throw e;
                } catch (Exception e) {
                    log.warn(sm.getString
                             ("namingContext.failResolvingReference"), e);
                    throw new NamingException(e.getMessage());
                }
            } else {
                return entry.value;
            }
        }

    }

    /**
     * Binds a name to an object. All intermediate contexts and the target
     * context (that named by all but terminal atomic component of the name)
     * must already exist.
     *
     * @param name the name to bind; may not be empty
     * @param obj the object to bind; possibly null
     * @param rebind if true, then perform a rebind (ie, overwrite)
     * @exception NameAlreadyBoundException if name is already bound
     * @exception javax.naming.directory.InvalidAttributesException if object
     * did not supply all mandatory attributes
     * @exception NamingException if a naming exception is encountered
     */
    protected void bind(Name name, Object obj, boolean rebind)
        throws NamingException {

        if (!checkWritable()) {
            return;
        }

        while ((!name.isEmpty()) && (name.get(0).length() == 0))
            name = name.getSuffix(1);
        if (name.isEmpty())
            throw new NamingException
                (sm.getString("namingContext.invalidName"));

        NamingEntry entry = bindings.get(name.get(0));

        if (name.size() > 1) {
            if (entry == null) {
                throw new NameNotFoundException(sm.getString(
                        "namingContext.nameNotBound", name, name.get(0)));
            }
            if (entry.type == NamingEntry.CONTEXT) {
                if (rebind) {
                    ((Context) entry.value).rebind(name.getSuffix(1), obj);
                } else {
                    ((Context) entry.value).bind(name.getSuffix(1), obj);
                }
            } else {
                throw new NamingException
                    (sm.getString("namingContext.contextExpected"));
            }
        } else {
            if ((!rebind) && (entry != null)) {
                throw new NameAlreadyBoundException
                    (sm.getString("namingContext.alreadyBound", name.get(0)));
            } else {
                // Getting the type of the object and wrapping it within a new
                // NamingEntry
                Object toBind =
                    NamingManager.getStateToBind(obj, name, this, env);
                if (toBind instanceof Context) {
                    entry = new NamingEntry(name.get(0), toBind,
                                            NamingEntry.CONTEXT);
                } else if (toBind instanceof LinkRef) {
                    entry = new NamingEntry(name.get(0), toBind,
                                            NamingEntry.LINK_REF);
                } else if (toBind instanceof Reference) {
                    entry = new NamingEntry(name.get(0), toBind,
                                            NamingEntry.REFERENCE);
                } else if (toBind instanceof Referenceable) {
                    toBind = ((Referenceable) toBind).getReference();
                    entry = new NamingEntry(name.get(0), toBind,
                                            NamingEntry.REFERENCE);
                } else {
                    entry = new NamingEntry(name.get(0), toBind,
                                            NamingEntry.ENTRY);
                }
                bindings.put(name.get(0), entry);
            }
        }

    }
}

如上的代码是我精简后的代码(源码966行),删除了各种Overload的函数和其他辅助函数,精简后的函数可以概括为以下几个:

  • bind/rebind:绑定一项内容到上下文
  • unbind:取消绑定某项内容
  • lookup:根据名字查找一项内容
  • rename:重命名某项内容
  • NamingEnumeration:枚举绑定的内容
  • destroySubcontext:销毁子上下文
  • createSubcontext:创建子上下文
  • addToEnvironment:添加设置参数
  • removeFromEnvironment:移除某项设置参数
  • getEnvironment:得到某项设置参数
  • Close:清空环境变量env的设置

如果再抽象一步,可以看到,这些函数都是对上面源码中:

/**
* Environment.
*/
protected final Hashtable<String,Object> env;
/**
* Bindings in this Context.
*/
protected final HashMap<String,NamingEntry> bindings;
/**
* Name of the associated Catalina Context.
*/

这两个成员变量的各种操作,这两个成员变量为:

  • env:初始化上下文使用的变量集合,是一个Hashtable
  • bindings:我们常操作的name-object对,也是一个Hashtable

我们知道JNDI提供的是一个树状的结构,它的最顶是一个initialContext节点,下面就是绑定的一些对象或是一些subContext,用JNDI树可以查找到树中每一个节点上内容引用,但是Tomcat的Hashtable如何提供树状组织?从createSubcontext的源代码可以看到,它调用了如上代码类的构造函数,重新构造了一个Context对象,然后再放到bindings中,同时存有一个指向父上下文的名称。

另外,JNDI支持分布式,我们知道JNDI在服务器如Jboss中是以服务的形式存在,配置jboss提供的文件,我们可以访问其他jboss上的JNDI服务,从而获取其他Jboss中的内容:

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
java.naming.provider.url=jnp://localhost:1099

总结

JNDI的定位是如何方便、统一的定义和访问资源,其API就效果看来,相当于外观模式,屏蔽了底层注册表、对象等不同访问方式之间的差异,另外@EJB注入底层使用的是JNDI实现,这个在EJB有关的文章中再细说。

再说JNDI,布布扣,bubuko.com

时间: 2024-10-18 00:53:36

再说JNDI的相关文章

JNDI在Tomcat中的配置

使用JNDI获取连接对象 java:comp/env/jdbc/news 这段为固定写法java:comp/env/ 在Tomcat中的配置 配置在:context.xml文件内 F:\apache-tomcat-7.0.68-windows-x64\apache-tomcat-7.0.68\conf\context.xml ---------mySQL<Resource name="jdbc/news"auth="Container" type="

JNDI

JNDI基础 一 简介 1.JNDI:Java Naming and Directory Interface,即Java命名和目录接口.JNDI包含了一些标准API接口,Java程序可以通过这些接口来访问命名目录服务.JNDI不依赖于任何独立的命名目录服务器,不管采用哪种命名目录服务器,应用程序都可以通过统一的JNDI接口来调用.要使用JNDI,必须要安装jdk 1.3以上版本. 2.命名服务:就是将名字和计算机系统内的一个对象建立关联,从而允许应用程序通过该名字访问该对象.简而言之,命名服务就

理解JNDI中 java:comp/env/jdbc/datasource 与 jdbc/datasource 的不同之处(转)

在描述JNDI,例如获得数据源时,JNDI地址有两种写法,例如同是  jdbc/testDS 数据源: A:java:comp/env/jdbc/testDS B:jdbc/testDS   这两种写法,配置的方式也不尽相同,第一种方法应该算是一种利于程序移植或迁移的方法,它的实现与“映射”的概念相同,而B方法,则是一个硬引用. java:comp/env 是环境命名上下文(environment naming context(ENC)),是在EJB规范1.1以后引入的,引入这个是为了解决原来J

JPA, JNDI, OSGi

JPA Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中. JNDI Java Naming and Directory Interface,Java命名和目录接口,是SUN公司提供的一种标准的Java命名系统接口,JNDI提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务

JNDI深入浅出

1.什么是JNDI JNDI(The Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API.命名服务将名称和对象联系起来,使得我们可以用名称访问对象.目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性. 命名或目录服务使你可以集中存储共有信息,这一点在网络应用中是重要的,因为这使得这样的应用更协调.更容易管理.例如,可以将打印机设置存储在目录服务中,以便被与打印机有关的应用使用. 命名服务中的

Springmvc +JNDI 在Tomcat下 配置数据源(转)

一.             简介 jndi(Java Naming and Directory Interface,Java命名和目录接口)是一组在Java应用中访问命名和目录服务的API.命名服务将名称和对象联系起来,使得我们可以用名称访问对象.目录服务是一种命名服务,在这种服务里,对象不但有名称,还有属性. 二.             tomcat配置jndi有三种方式. 第一种:单个应用独享数据源 在Tomcat的server.xml找到工程的Context节点,添加一个私有数据源 <

tomcat配置JNDI获取数据源

各个web工程可以通过工程内的xml文件配置访问数据库的数据源,这样的配置是各个工程私有的.基于JNDI为tomcat配置数据源,则可以做成全局的,各工程只需要通过便签引用数据源即可. 1.需要将数据库的连接驱动mysql-connector-java-5.1.21.jar及数据库连接池的jar包druid-0.2.9.jar放到Tomcat 6.0\lib下. 2.修改tomcat的配置文件,基于JNDI配置数据源,Tomcat 6.0\conf\context.xml改成如下即可,原文件中多

JNDI 使用

J2EE技术规范(一)--JNDI 分类: java 基础2012-12-02 20:05 1539人阅读 评论(14) 收藏 举报 学习Java,我们首先要掌握的就是十三种技术规范,我们接下来就一步一步的来学习.今天主要是讲讲JNDI. 一.理解JNDI的用途: JNDI是用于访问不同的命名和目录服务的统一API接口. 二.那何时使用JNDI呢: (1)JNDI是一种查找服务,用于查找: Web应用环境变量 EJBs和它们的环境变量 通过DataSources的数据库连接池 JMS目标和连接工

Hibernate 异常 : Error parsing JNDI name [xxx]

初学 Hibernate ,用官方给的日志模板配置好了日志,跑起小例子, 控制台中的日志里写了一些异常: 21:16:36,036 DEBUG EntityLoader:146 -  Static select for entity cn.sjll.hibernate435.model.UserModel  [OPTIMISTIC_FORCE_INCREMENT]: select usermodel0_.ID as ID1_0_0_,  usermodel0_.NAME as NAME2_0_0

JNDI数据源的使用

有时候我们数据库的连接会使用jndi的方式 try { InitialContext ic = new InitialContext(); dataSource = (DataSource) ic.lookup("java:/comp/env/jdbc/murach"); } catch(Exception e) { e.printStackTrace(); } 各种不同的J2EE容器,都用不同的配置方式. tomcat可以配置全局JNDI和私有JNDI(注意这里说的Tomcat6):