命令行 nashorn 脚本 (jjs) 无法创建实体管理器。为啥?

Posted

技术标签:

【中文标题】命令行 nashorn 脚本 (jjs) 无法创建实体管理器。为啥?【英文标题】:CommandLine nashorn script (jjs) unable to create entity manager. Why?命令行 nashorn 脚本 (jjs) 无法创建实体管理器。为什么? 【发布时间】:2016-05-10 18:29:19 【问题描述】:

命令行 nashorn 脚本 (jjs) 无法创建实体管理器。

为什么这不起作用?

如何让它工作(如果有的话)?

即,

运行脚本如下所示...

    $ jjs -cp ".;myjpaclasses-1.jar;" myNashornScript.js

即“myNashornScript.js”包含...

    /* global Java, java */
    print("begin test...");
    var EntityManagerFactory = Java.type('javax.persistence.EntityManagerFactory');
    var EntityManager = Java.type('javax.persistence.EntityManager');
    var Persistence = Java.type('javax.persistence.Persistence');
    var Employees = Java.type('aaa.bbb.ccc.jpa.Employees');
    var employees = new Employees();
    var javaImports = new JavaImporter(java.io, java.lang, java.util);

    try
    
        with (javaImports) 

            var emf = Persistence.createEntityManagerFactory("hr_pu"); <== issue here(?)...
            var em = emf.createEntityManager();

            var query = em.createQuery(
                    "SELECT e FROM Employees e WHERE e.employeeId > ?1")
                    .setParameter(1, 100)
                    .setFirstResult(0);

            var rows = query.getResultList();

            //...print info on 2nd row object of returned list...
            //...print returned list size...
            print("rows.get(2).getFirstName()="+ rows.get(2).getFirstName()
                + "...returned row count=" + rows.size());
        
     catch (e) 
        print(e.message);
    
    print("end test...");

从命令行运行脚本始终会产生以下结果...

    begin test...
    No Persistence provider for EntityManager named hr_pu
    end test...

注意:fwiw,当从 java 应用程序调用时,此脚本似乎工作正常......即,

    package aaa.bbb.ccc.jar;

    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;

    public class RunScript 
        public static void main (String[] args) throws ScriptException, FileNotFoundException
        
            ScriptEngineManager engineManager = new ScriptEngineManager();
            ScriptEngine engine = engineManager.getEngineByName("nashorn");
            engine.eval(new FileReader("src/main/resources/myNashornScript.js"));        
        
    

...即,产生...

    begin test...
    [EL Warning]: transaction: 2016-05-12 14:18:00.773--ServerSession(1829217853)--PersistenceUnitInfo hr_pu has transactionType RESOURCE_LOCAL and therefore jtaDataSource will be ignored
    [EL Info]: 2016-05-12 14:18:00.78--ServerSession(1829217853)--EclipseLink, version: Eclipse Persistence Services - 2.6.3.v20160428-59c81c5
    [EL Info]: connection: 2016-05-12 14:18:01.183--ServerSession(1829217853)--/file:/C:/tools/netbeansWS/myjpaclasses/target/classes/_hr_pu login successful
    rows.get(2).getFirstName()=Alexander...returned row count=106
    end test...

感谢您的帮助/指导!

附言

如果有什么不同,persistence.xml(位于“myjpaclasses.jar”的 src/main/resources/META-INF 中)如下所示:

    <?xml version="1.0" encoding="UTF-8"?>
    <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
        <persistence-unit name="hr_pu" transaction-type="RESOURCE_LOCAL">      
            <jta-data-source>jdbc/HR</jta-data-source>
            <class>aaa.bbb.ccc.jpa.Regions</class>
            <class>aaa.bbb.ccc.jpa.Employees</class>
            <class>aaa.bbb.ccc.jpa.Departments</class>
            <class>aaa.bbb.ccc.jpa.Locations</class>
            <class>aaa.bbb.ccc.jpa.Jobs</class>
            <class>aaa.bbb.ccc.jpa.Countries</class>
            <exclude-unlisted-classes>false</exclude-unlisted-classes>
            <properties>
                <property name="javax.persistence.jdbc.driver" value="oracle.jdbc.OracleDriver"/>
                <property name="javax.persistence.jdbc.url" value="jdbc:oracle:thin:@localhost:1521:XE"/>
                <property name="javax.persistence.jdbc.user" value="HR"/>
                <property name="javax.persistence.jdbc.password" value="HR"/>
            </properties>
        </persistence-unit>
    </persistence>

JPA“雇员”类如下所示:

    package aaa.bbb.ccc.jpa;

    import java.io.Serializable;
    import java.math.BigDecimal;
    import java.util.Date;
    import java.util.List;
    import javax.persistence.Basic;
    import javax.persistence.Column;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    import javax.persistence.JoinColumn;
    import javax.persistence.ManyToOne;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    import javax.persistence.Temporal;
    import javax.persistence.TemporalType;
    import javax.persistence.UniqueConstraint;
    import javax.xml.bind.annotation.*;

    @XmlAccessorType(XmlAccessType.FIELD)
    @Entity
    @Table(name = "EMPLOYEES", uniqueConstraints = @UniqueConstraint(columnNames = "EMAIL"))

    @XmlRootElement
    public class Employees implements Serializable 

        @Column(name = "LAST_NAME", table = "EMPLOYEES", nullable = false, length = 25)
        @Basic
        private String lastName;

        @Column(name = "HIRE_DATE", table = "EMPLOYEES", nullable = false)
        @Temporal(TemporalType.TIMESTAMP)
        @Basic
        private Date hireDate;

        @ManyToOne(targetEntity = Departments.class)
        @JoinColumn(name = "DEPARTMENT_ID", referencedColumnName = "DEPARTMENT_ID")
        private Departments departmentId;

        @Column(name = "EMPLOYEE_ID", table = "EMPLOYEES", nullable = false)
        @Id
        private Integer employeeId;

        @ManyToOne(targetEntity = Employees.class)
        @JoinColumn(name = "MANAGER_ID", referencedColumnName = "EMPLOYEE_ID")
        private Employees managerId;

        @Column(name = "SALARY", table = "EMPLOYEES", scale = 2, precision = 8)
        @Basic
        private BigDecimal salary;

        @Column(name = "COMMISSION_PCT", table = "EMPLOYEES", scale = 2, precision = 2)
        @Basic
        private BigDecimal commissionPct;

        @XmlTransient
        @OneToMany(targetEntity = Employees.class, mappedBy = "managerId")
        private List<Employees> employeesCollection;

        @Column(name = "FIRST_NAME", table = "EMPLOYEES", length = 20)
        @Basic
        private String firstName;

        @ManyToOne(optional = false, targetEntity = Jobs.class)
        @JoinColumn(name = "JOB_ID", referencedColumnName = "JOB_ID")
        private Jobs jobId;

        @Column(name = "PHONE_NUMBER", table = "EMPLOYEES", length = 20)
        @Basic
        private String phoneNumber;

        @XmlTransient
        @OneToMany(targetEntity = Departments.class, mappedBy = "managerId")
        private List<Departments> departmentsCollection;

        @Column(name = "EMAIL", table = "EMPLOYEES", nullable = false, length = 25)
        @Basic
        private String email;

        public Employees() 

        

        public String getLastName() 
            return this.lastName;
        

        public void setLastName(String lastName) 
            this.lastName = lastName;
        

        public Date getHireDate() 
            return this.hireDate;
        

        public void setHireDate(Date hireDate) 
            this.hireDate = hireDate;
        

        public Departments getDepartmentId() 
            return this.departmentId;
        

        public void setDepartmentId(Departments departmentId) 
            this.departmentId = departmentId;
        

        public Integer getEmployeeId() 
            return this.employeeId;
        

        public void setEmployeeId(Integer employeeId) 
            this.employeeId = employeeId;
        

        public Employees getManagerId() 
            return this.managerId;
        

        public void setManagerId(Employees managerId) 
            this.managerId = managerId;
        

        public BigDecimal getSalary() 
            return this.salary;
        

        public void setSalary(BigDecimal salary) 
            this.salary = salary;
        

        public BigDecimal getCommissionPct() 
            return this.commissionPct;
        

        public void setCommissionPct(BigDecimal commissionPct) 
            this.commissionPct = commissionPct;
        

        @XmlTransient
        public List<Employees> getEmployeesCollection() 
            return this.employeesCollection;
        

        public void setEmployeesCollection(List<Employees> employeesCollection) 
            this.employeesCollection = employeesCollection;
        

        public String getFirstName() 
            return this.firstName;
        

        public void setFirstName(String firstName) 
            this.firstName = firstName;
        

        public Jobs getJobId() 
            return this.jobId;
        

        public void setJobId(Jobs jobId) 
            this.jobId = jobId;
        

        public String getPhoneNumber() 
            return this.phoneNumber;
        

        public void setPhoneNumber(String phoneNumber) 
            this.phoneNumber = phoneNumber;
        

        @XmlTransient
        public List<Departments> getDepartmentsCollection() 
            return this.departmentsCollection;
        

        public void setDepartmentsCollection(List<Departments> departmentsCollection) 
            this.departmentsCollection = departmentsCollection;
        

        public String getEmail() 
            return this.email;
        

        public void setEmail(String email) 
            this.email = email;
        
    

用于创建“myjpaclasses-1.jar”的 pom.xml...

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>aaa.bbb.ccc.jar</groupId>
        <artifactId>myjpaclasses</artifactId>
        <version>1</version>
        <packaging>jar</packaging>
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <maven.test.skip>false</maven.test.skip>
            <maven.compiler.source>1.8</maven.compiler.source>
            <maven.compiler.target>1.8</maven.compiler.target>
            <timestamp>$maven.build.timestamp</timestamp>
            <maven.build.timestamp.format>yyyyMMdd.HHmmss</maven.build.timestamp.format>
        </properties>

        <dependencies>

            <dependency>
                <groupId>org.eclipse.persistence</groupId>
                <artifactId>eclipselink</artifactId>
                <version>2.6.3</version>
            </dependency>

            <dependency>
                <groupId>javax</groupId>
                <artifactId>javaee-web-api</artifactId>
                <version>7.0</version>
                <scope>provided</scope>
            </dependency>

            <dependency>
                <groupId>com.oracle</groupId>
                <artifactId>ojdbc7</artifactId>
                <version>12.1.0.2</version>
            </dependency>

        </dependencies>

        <build>

            <!-- use for snapshot versioning <finalName>$project.artifactId-$project.version-$maven.build.timestamp</finalName> -->
            <finalName>$project.artifactId-$project.version</finalName>

            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <showDeprecation>true</showDeprecation>
                    </configuration>
                </plugin>

                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-shade-plugin</artifactId>
                    <version>2.4.3</version>
                    <executions>
                        <execution>
                            <phase>package</phase>
                            <goals>
                                <goal>shade</goal>
                            </goals>
                            <configuration>
                                <artifactSet>
                                    <excludes>
                                        <exclude>junit:junit</exclude>
                                    </excludes>
                                </artifactSet>                          
                            </configuration>
                        </execution>
                    </executions>
                </plugin> 

            </plugins>
        </build>
        <name>myjpaclasses</name>
    </project>

【问题讨论】:

【参考方案1】:

可能EntityManager / JPA 库要求持久性库类加载器是线程上下文类加载器。使用 jjs -cp 选项,创建一个新的类加载器,它不是线程上下文类加载器。使用“java -cp”,应用程序类加载器在初始化期间被设置为线程上下文加载器。

您可能想在 .js 脚本中尝试以下操作:

var EntityManagerFactory = Java.type('javax.persistence.EntityManagerFactory');
// set the thread context class to be the loader of EntityManagerFactory class
var cls = EntityManagerFactory.class;
java.lang.Thread.currentThread().contextClassLoader = cls.classLoader;
//... rest of your script..

如果可行,请告诉我。

【讨论】:

宾果游戏! (即,有效)... - 非常感谢 A. Sundararajan,感谢您的慷慨帮助/专业知识!

以上是关于命令行 nashorn 脚本 (jjs) 无法创建实体管理器。为啥?的主要内容,如果未能解决你的问题,请参考以下文章

有没有哪个Java 8妙用Nashorn引擎的好例子

访问/拦截 Nashorn 的全局对象变量

Java 8 的 Nashorn 脚本引擎教程

Java_脚本引擎_03_nashorn支持es6

安全的 Nashorn JS 执行

Nashorn——在JDK 8中融合Java与JavaScript之力--转