简体   繁体   中英

Right way to persist new object at runtime in Java EE?

Intro (Skip if you get bored ;)

Hi guys, I'm working on a school project that I "inherited" from a previous year group. It's a Webapp built in Java EE meant for gamification of pentesting training. My task was to include a new vulernability (XXE) in the application.The existing application mixes CDI and EJB annotations, although I've yet to understand the reason for that.

Neither I nor anyone in my group has any experience with Java EE, and while I did my best to get up to speed in the last few weeks, I'm a little lost at the following problem, specifically solving it without a "dirty hack" that would break the MVC pattern.

Problem

I can successfully deserialize a user-uploaded .xml to an instance of "Product" (or let an exploit be triggered when the user uploads malicious data, just as I want).

But persisting the object doesn't work, which seems to have to do with the scope of my UploadController class. So far persistence is only done in the InitBean which is a singleton and is instantiated as soon as the app starts.

It was not the original author's intent to allow for further objects to be added to the database after starting the application.

I tried two routes so far: - Mimicking the usage of the PersistenceContext from InitBean in my UploadController (see code below). This results in javax.persistence.TransactionRequiredException. I did some reading on the error, I guess my controller has the wrong lifecycle, but I don't quite get it.

I also tried wrapping the persisting functionality in InitBean and exposing it as static method (felt super ugly, also didn't work).

Question

So, ultimately, my question: What is the right approach to add an item to the database after the application has started? Should I even do it from my controller class, if so, then how do I get the right scope/Transaction? Or should I go down another path entirely? Explanations are much appreciated.

Model:

@Entity
@XmlRootElement
@Table(name = "product")
public class Product implements Serializable, Comparable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(unique = true, nullable = false)
    private String name;

    @Column(nullable = false)
    private float price;

    @Column(length = 512) // hibernate default of 255 not sufficient for our flags
    private String description;

    @Column(name = "img_path")
    private String imagePath;

    @ManyToOne
    @JoinColumn(name = "category_fk")
    private Category category;

    @OneToMany(mappedBy = "product", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    private Set<Recension> recensions;

    @Column
    private boolean active;


    public Product() { 
    }

    public Product(String name, float price, String description, String imgPath, Category category) {
        this.name = name;
        this.price = price;
        this.description = description;
        this.imagePath = imgPath;
        this.category = category;
        this.active = true;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getImagePath() {
        return imagePath;
    }

    public void setImagePath(String imagePath) {
        this.imagePath = imagePath;
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    public Set<Recension> getRecensions() {
        return recensions;
    }

    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }


    @Override
    public int compareTo(Object o) {
        return Long.compare(((Product) o).getId(), this.id);
    }    


    // Convenience methods

    public int ratingCount() {
        return recensions.size();
    }

    public float averageRating() {
        // avoid division by 0
        if (ratingCount() == 0) {
            return 0;
        }
        int sum = 0;
        for (Recension r : recensions) {
            sum += r.getRating();
        }
        return sum/ratingCount();

    }


}

ModelDAO:

package at.technikum.mic16.prj.dao;

import at.technikum.mic16.prj.entity.Category;
import at.technikum.mic16.prj.entity.Product;

import java.io.Serializable;
import java.util.List;
import javax.enterprise.inject.Model;
import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.apache.commons.lang3.StringEscapeUtils;

/**
 *
 * @author leandros
 */
@Model
public class ProductDAO implements Serializable {

    @PersistenceContext(unitName = "daisy-persunit")
    private EntityManager em;

    public Product findByID(Long id) {
        return em.find(Product.class, id);
    }

    /**
     * Find all products, optionally paginated
     * @param offset Offset to result set
     * @param count Number of rows to return, a value <= 0 will result in pagination being disabled
     * @return 
     */
    public List<Product> findAll(int offset, int count) {
        Query q = em.createQuery("FROM Product p WHERE active is true", Product.class);
        if (count > 0) {
            q.setFirstResult(offset);
            q.setMaxResults(count);
        }
        return q.getResultList();
    }

     /**
     * Find all products matching substring in name or description
     * @param substring Substring to match
     * @param offset Offset to result set
     * @param count Number of rows to return, a value <= 0 will result in pagination being disabled
     * @return 
     */
    public List<Product> findByNameOrDescription(String substring, int offset, int count) {
        Query q = em.createQuery("FROM Product p WHERE (name like :substring or description like :substring) AND active is true", Product.class);
        q.setParameter("substring", "%" + substring + "%");
        if (count > 0) {
            q.setFirstResult(offset);
            q.setMaxResults(count);
        }

        return q.getResultList();
    }

    /**
     * Find all products - this is vulnerable to SQL injection and also unescapes input by purpose
     * Result is also sorted independently of SQL (no order by) in order to display record with reward first (highest id, see comparator)
     * @param queryString
     * @return 
     */
    public List<Product> findByExactName(String queryString) {
        String unescaped = StringEscapeUtils.unescapeHtml4(queryString);
        Query q = em.createQuery("FROM Product p WHERE active is true AND name = '" + unescaped + "'", Product.class);
        List result = q.getResultList();
        result.sort(null);
        return result;
    }

     /**
     * Find all products matching specific category
     * @param category Category to match
     * @param offset Offset to result set
     * @param count Number of rows to return, a value <= 0 will result in pagination being disabled
     * @return 
     */
    public List<Product> findByCategory(Category category, int offset, int count) {
        Query q = em.createQuery("FROM Product p WHERE category_fk = :category AND active is true", Product.class);
        q.setParameter("category", category);
        if (count > 0) {
            q.setFirstResult(offset);
            q.setMaxResults(count);
        }
        return q.getResultList();
    }

    /**
     * Find inactive products
     * @return List of inactive products
     */
    public List<Product> findInactive() {
        Query q = em.createQuery("FROM Product p WHERE active is false", Product.class);
        return q.getResultList();
    }

    public void persist(Product...products) {
        for (Product product : products) {
            em.persist(product);
        }
    }

    public void merge(Product product) {
        em.merge(product);
    }

    public void delete(Product product) throws EntityNotFoundException {
        // attach and delete it...
        Product attached = em.find(Product.class, product.getId());
        if (attached != null) {
            em.remove(attached);
        } else {
            throw new EntityNotFoundException("Product not found with id: " + product.getId());
        }
    }



}

InitBean

@Singleton
@LocalBean
@Startup
public class InitBean {

    // file path of JS to by executed by phantom JS
    private static final String XSS_FILE_PATH = "/home/daisy/.config";
    // this file is intended for holding a token receivable by exploiting the command execution in admin interface
    public static final String HIDDEN_FILE_PATH_CE = "/tmp/TOKEN_REWARD.TXT";
    // this file is intended for holding a token receivable by exploiting the xxe vulnerability in the upload section
    public static final String HIDDEN_FILE_PATH_XXE = "/tmp/hidden/TOKEN_REWARD2.TXT";
    // this user bears a special description -> reward token
    private static final String USER_WITH_TOKEN = "user2@foo.at";
    //Admin User Testing
    private static final String ADMIN_USER = "admin@foo.at";

    @Inject
    private WebshopService webshopService;

    @Inject
    private CategoryDAO categoryDAO;
    @Inject
    private ProductDAO productDAO;
    @Inject
    private OrderItemDAO orderItemDAO;
    @Inject
    private PlacedOrderDAO placedOrderDAO;
    @Inject
    private RecensionDAO recensionDAO;
    @Inject
    private UserDAO userDAO;
    @Inject
    private UserRoleDAO userRoleDAO;

    private String installationToken;
    private Map<Vulnerability, String> rewardTokens;


    public void setInstallationToken(String installationToken) {
        this.installationToken = installationToken;
    }

    public Map<Vulnerability, String> getRewardTokens() {
        return rewardTokens;
    }

    public void setRewardTokens(Map<Vulnerability, String> rewardTokens) {
        this.rewardTokens = rewardTokens;
    }



    @PostConstruct
    public void init() {

        try {
            insertSampleData();
            installationToken = webshopService.retrieveInstallationToken();
            /* if there is no token, retrieving it would fail with FileNotFoundException
            so just go on inserting vulnerability data...
             */
            generateRewardTokens();
            insertVulnerabilityData();
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
            Logger.getLogger(InitBean.class.getName()).log(Level.SEVERE, null, ex);
        } catch (FileNotFoundException ignore) {
            // retrieving installation token failed
        } catch (IOException ex) {
            Logger.getLogger(InitBean.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    /**
     * Inserts sample data into the database
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException 
     */
    private void insertSampleData() throws NoSuchAlgorithmException, UnsupportedEncodingException {

        UserRole user1Role = new UserRole("han", UserRole.Role.CUSTOMER);
        UserRole user2Role = new UserRole(USER_WITH_TOKEN, UserRole.Role.CUSTOMER);
        UserRole user3Role = new UserRole(ADMIN_USER, UserRole.Role.ADMIN);
        userRoleDAO.persist(user1Role, user2Role, user3Role);

        User user1 = new User("user1@foo.at", JBossPasswordUtil.getPasswordHash("user1"), "User", "1");
        User user2 = new User(USER_WITH_TOKEN, JBossPasswordUtil.getPasswordHash(RandomStringUtils.randomAlphanumeric(12)), "User", "2");
        User user3 = new User(ADMIN_USER, JBossPasswordUtil.getPasswordHash("admin"), "Iam", "God");
        userDAO.persist(user1, user2, user3);

        Category clothes = new Category("Clothes");
        categoryDAO.persist(clothes);

        Category men = new Category("Men");
        men.setParent(clothes);
        categoryDAO.persist(men);

        Category trousersMen = new Category("Trousers");
        trousersMen.setParent(men);
        categoryDAO.persist(trousersMen);

        Category electro = new Category("Electro");
        categoryDAO.persist(electro);

        Category telly = new Category("Television");
        telly.setParent(electro);
        categoryDAO.persist(telly);

        Category hoover = new Category("Hoover");
        hoover.setParent(electro);
        categoryDAO.persist(hoover);

        Category smartphone = new Category("Smartphone");
        smartphone.setParent(electro);
        categoryDAO.persist(smartphone);

        Product phillips1 = new Product("Philips 55PUK4900", 679.99f, "This new Phillips is superb...", "images/products/Phillips_55PUK4900.jpg", telly);
        Product phillips2 = new Product("Phillips 55PUS6031", 998.99f, "The brand new Phillips...", "images/products/Phillips_55PUS6031.jpg", telly);
        Product phillips3 = new Product("Phillips 50PFK4109", 328.99f, "This new Phillips is not as good...", "images/products/Phillips_50PFK4109.jpg", telly);
        Product samsung1 = new Product("Samsung UE55JU6470", 850.00f, "This new Samsung is superb...", "images/products/Samsung_UE55JU6470.jpg", telly);
        Product samsung2 = new Product("Samsung UE55K5660", 1100.00f, "This new Samsung is not as good...", "images/products/Samsung_UE55K5650.jpg", telly);
        Product samsung3 = new Product("Samsung UE65JU6070", 1200.99f, "This new Samsung is not as good...", "images/products/Samsung_UE65JU6070.jpg", telly);
        Product panasonic1 = new Product("Panasonic TX-49DXW654", 679.99f, "This new Phillips is superb...", "images/products/Panasonic_TX49DXW654.jpg", telly);
        Product panasonic2 = new Product("Panasonic TX65AXW904", 998.99f, "This new Phillips is not as good...", "images/products/Panasonic_TX65AXW904.jpg", telly);
        Product panasonic3 = new Product("Panasonic TX55CXW684", 328.99f, "This new Phillips is not as good...", "images/products/Panasonic_TX55CXW684.jpg", telly);
        Product hoover1 = new Product("iRobot Roomba 980", 750.90f, "Brand new and strong...", "images/products/Irobot_Roomba980.jpg", hoover);
        Product hoover2 = new Product("iRobot Roomba 886", 930.90f, "Brand new and strong...", "images/products/Irobot_Roomba886.jpg", hoover);
        Product hoover3 = new Product("iRobot Roomba 875", 487.90f, "Brand new and strong...", "images/products/IrobotRoomba875.jpg", hoover);
        Product hoover4 = new Product("Dyson Big Ball Parquet", 640.90f, "Brand new and strong...", "images/products/Dyson_Bigball1.jpg", hoover);
        Product hoover5 = new Product("Dyson DC37c Parquet", 321.90f, "Brand new and strong...", "images/products/Dyson_Dc37.jpg", hoover);
        Product hoover6 = new Product("Dyson DC37 Musclehead", 219.90f, "Brand new and strong...", "images/products/Dyson_Dc37misclehead.jpg", hoover);
        Product smartphone1 = new Product("Apple Iphone 7", 860.90f, "Brand new and strong...", "images/products/Iphone7.jpg", smartphone);
        Product smartphone2 = new Product("Apple Iphone SE", 450.90f, "Brand new and strong...", "images/products/Iphone_SE.jpg", smartphone);
        Product smartphone3 = new Product("Samsung Galaxy S8", 750.90f, "Brand new and strong...", "images/products/Samsung_S8.jpg", smartphone);
        Product smartphone4 = new Product("Samsung Galaxy S6", 650.90f, "Brand new and strong...", "images/products/Samsung_S6.jpg", smartphone);
        Product smartphone5 = new Product("Google Pixel", 800.90f, "Brand new and strong...", "images/products/Google_Pixel.jpg", smartphone);
        Product smartphone6 = new Product("Huawei P10", 700.90f, "Brand new and strong...", "images/products/Huawei_P10.jpg", smartphone);
        Product jeans1 = new Product("Lewis", 110.90f, "Brand new and strong...", "images/products/jeans1.jpg", trousersMen);
        Product jeans2 = new Product("G-Star P10", 120.90f, "Brand new and strong...", "images/products/jeans2.jpg", trousersMen);
        Product jeans3 = new Product("Review P10", 60.90f, "Brand new and strong...", "images/products/jeans3.jpg", trousersMen);
        Product jeans4 = new Product("Replay", 75.90f, "Brand new and strong...", "images/products/jeans4.jpg", trousersMen);
        Product jeans5 = new Product("Diesel", 160.90f, "Brand new and strong...", "images/products/jeans5.jpg", trousersMen);
        Product jeans6 = new Product("Mustang", 55.90f, "Brand new and strong...", "images/products/jeans6.jpg", trousersMen);

        productDAO.persist(phillips1, phillips2, phillips3, samsung1, samsung2, samsung3, panasonic1, panasonic2, panasonic3, hoover1, hoover2, hoover3, hoover4, hoover5, hoover6,
                smartphone1, smartphone2, smartphone3, smartphone4, smartphone5, smartphone6, jeans1, jeans2, jeans3, jeans4, jeans5, jeans6);

        Recension recension1 = new Recension();
        recension1.setCreationDate(LocalDateTime.now().minusDays(14));
        recension1.setProduct(phillips1);
        recension1.setRating(4);
        recension1.setUser(user1);
        recension1.setText("I like it");

        Recension recension2 = new Recension();
        recension2.setCreationDate(LocalDateTime.now().minusDays(3).minusSeconds((int) (Math.random()*1337)));
        recension2.setProduct(phillips1);
        recension2.setRating(3);
        recension2.setUser(user2);
        recension2.setText("It's ok, don't expect too much.");

        recensionDAO.persist(recension1, recension2);

    }

    /**
     * Generates all the reward tokens in rewardTokens
     */
    private void generateRewardTokens() {
        rewardTokens = new HashMap<>();
        for (Vulnerability v : Vulnerability.values()) {
            try {
                rewardTokens.put(v, DaisyPointsCrypter.encryptMessage(installationToken, "Vulnerability|" + v.name()));
                //Logger.getLogger(InitBean.class.getName()).log(Level.INFO, "Generated token: ".concat(rewardTokens.get(v)));
            } catch (DaisyPointsEncryptionException ex) {
                rewardTokens = null;
                Logger.getLogger(InitBean.class.getName()).log(Level.SEVERE, "Error generating reward tokens", ex);
            }

        }
    }

    /**
     * Puts reward tokens to their respective places
     * @throws IOException 
     */
    public void insertVulnerabilityData() throws IOException {

        /*
        this should only happen upon invocation via TokenController and not
        on subsequent restarts, when token is already known
        */
        if (rewardTokens == null) {
            generateRewardTokens();
        }

        // hidden product - find via SQL injection
        Category hoover = categoryDAO.findByName("Hoover");
        Product prod1 = new Product("SQL Injection exploited!", 666, "Congratulations, here is your token for the points system:\n".concat(rewardTokens.get(Vulnerability.SQLI_PRODUCTS)), "images/thumbs_up.png", hoover);
        prod1.setActive(false);
        productDAO.persist(prod1);

        // hidden user - find via indirect object reference
        User user2 = userDAO.findById(USER_WITH_TOKEN);
        user2.setDescription(rewardTokens.get(Vulnerability.INSECURE_DIRECT_OBJECT_REFERENCE));

        // hidden file - find via hidden directory and CommandService
        File f = new File(HIDDEN_FILE_PATH_CE);
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(f));
            bw.write("Command execution exploited, here is your token for the points system:");
            bw.newLine();
            bw.write(rewardTokens.get(Vulnerability.HIDDEN_FILE));
            bw.newLine();
            bw.flush();
        } finally {
            FileUtil.safeClose(bw);
        }

        // hidden file - find via hidden directory and CommandService
        writeFile(HIDDEN_FILE_PATH_CE, Vulnerability.HIDDEN_FILE);

        // hidden file - find via hidden directory and CommandService
        writeFile(HIDDEN_FILE_PATH_XXE, Vulnerability.XXE_UPLOAD);


        // prepare phantom JS script
        f = new File(XSS_FILE_PATH);
        bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(f));
            bw.write(preparePhantomJSScript());
            bw.flush();
        } finally {
            FileUtil.safeClose(bw);
        }

    }

    private void writeFile(String pathname, Vulnerability vulnerability) throws IOException {
        File f;
        BufferedWriter bw;
        f = new File(pathname);
        bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(f));
            bw.write("Command execution exploited, here is your token for the points system:");
            bw.newLine();
            bw.write(rewardTokens.get(vulnerability));
            bw.newLine();
            bw.flush();
        } finally {
            FileUtil.safeClose(bw);
        }
    }

    /**
     * Delete any structures (DB reows, files) containing reward tokens
     */
    public void deleteVulnerabilityData() {
        for (Product p : productDAO.findInactive()) {
            productDAO.delete(p);
        }

        User user2 = userDAO.findById(USER_WITH_TOKEN);
        user2.setDescription("");

        File f = new File(HIDDEN_FILE_PATH_CE);
        f.delete();

        f = new File(XSS_FILE_PATH);
        f.delete();

    }

    /**
     Writes token in script invoked by phantom JS
     * @param token
     * @return
     * @throws UnsupportedEncodingException
     * @throws DaisyPointsEncryptionException 
     */
    private String preparePhantomJSScript() throws UnsupportedEncodingException {
        // this holds the phantom JS script to be executed in portable fashion
        String base64 = "dmFyIHBhZ2UgPSByZXF1aXJlKCd3ZWJwYWdlJykuY3JlYXRlKCk7CgpwYWdlLnNldHRpbmdzLnVzZXJBZ2VudCA9ICdUT0tFTic7CnBhZ2Uudmlld3BvcnRTaXplID0geyB3aWR0aDogMTkyMCwgaGVpZ2h0OiAxMDgwIH07CgpwYWdlLm9wZW4oJ2h0dHA6Ly8xMjcuMC4wLjE6ODA4MC9kYWlzeS13ZWIvJywgZnVuY3Rpb24oKSB7CgogICAgICAgIHBhZ2UuZXZhbHVhdGUoZnVuY3Rpb24oKSB7CiAgICAgICAgICAgICAgICBQcmltZUZhY2VzLmFiKHtzOmRvY3VtZW50LnF1ZXJ5U2VsZWN0b3IoJ1thbHQ9InByb2R1Y3QtMSJdJykuZ2V0QXR0cmlidXRlKCJpZCIpfSk7CiAgICAgICAgfSk7CgogICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7CiAgICAgICAgICAgICAgICBwYWdlLmV2YWx1YXRlKGZ1bmN0aW9uKCkge30pOwogICAgICAgIH0sIDIwMDApOwoKICAgICAgICBjb25zb2xlLmxvZygiZmluaXNoIik7CgogICAgICAgIHNldFRpbWVvdXQoZnVuY3Rpb24oKSB7CiAgICAgICAgICAgICAgICAvL3BhZ2UucmVuZGVyKCd0ZXN0LnBuZycpOwogICAgICAgICAgICAgICAgcGhhbnRvbS5leGl0KCk7CiAgICAgICAgfSwgMjAwMCk7Cn0pOwo=";
        String script = new String(Base64.decodeBase64(base64), "UTF-8");

        // modify user agent to use appropriate reward token string
        return script.replace("TOKEN", rewardTokens.get(Vulnerability.XSS_REMOTE_SCRIPT));
    }

    //Awful Hack:
//    public void persistProduct(Product product){
//        productDAO.persist(product);
//    }

}

UploadController

package at.technikum.mic16.prj.controller;

/**
 * Created by Dave on 11/05/2018.
 */
@ManagedBean(name = "uploadController")
@ApplicationScoped
@Startup
@Singleton
//@Stateful
public class UploadController implements Serializable {
    private Part file;

    private String xmlString;

    @Inject
    private ProductDAO productDAO;

    public void doOutput(Product product) {

        if (product == null) return;

        JAXBContext jc;

        {
            try {

                PrintStream ps = new PrintStream(new StringOutputStream(), true);

//                PrintStream ps = new PrintStream(new File("product.xml"));
                System.setOut(ps);

                jc = JAXBContext.newInstance(Product.class);

                Marshaller marshaller = jc.createMarshaller();
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                marshaller.marshal(product, System.out);


            } catch (JAXBException e) {
                e.printStackTrace();
            }
        }
    }

    public void upload() {
        try {
            setXmlString(new Scanner(file.getInputStream())
                    .useDelimiter("\\A").next());

            Product product = deserializeProduct(getXmlString());

            productDAO.persist(product);

            doOutput(product);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Part getFile() {
        return file;
    }

    public void setFile(Part file) {
        this.file = file;
    }


    public String getXmlString() {
        return xmlString;
    }

    public void setXmlString(String pXmlString) {
        xmlString = pXmlString;
    }

    //Parsing xml back to object (+vuln) -> should eventally go into another class


    public Product deserializeProduct(String xmlString) {
        try {
            JAXBContext jc = JAXBContext.newInstance(Product.class);

            XMLInputFactory xmlInputFactory = XMLInputFactory.newFactory();
            xmlInputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, true);
            xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, true);
            XMLStreamReader xmlStreamReader = xmlInputFactory.createXMLStreamReader(new StringReader(xmlString));

            Unmarshaller unmarshaller = jc.createUnmarshaller();
            return (Product) unmarshaller.unmarshal(xmlStreamReader);

        } catch (JAXBException pE) {
            pE.printStackTrace();
            return null;
        } catch (XMLStreamException pE) {
            pE.printStackTrace();
            return null;
        }
    }


}

Thanks for any help in advance!

Previously I used @Singleton + @Startup like something you do that .
but this method has some problems .
now I use this method :

<persistence 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"
             version="2.1">
  <persistence-unit name="db-connection" transaction-type="JTA">
    <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
    <jta-data-source>JTA-Connection</jta-data-source>
    <non-jta-data-source>None-JTA-Connection</non-jta-data-source>
    <properties>
        <property name="tomee.jpa.cdi" value="false"/>
        <property name="hibernate.dialect" value="org.hibernate.dialect.Oracle12cDialect"/>
        <!--<property name="hibernate.current_session_context_class" value="thread"/>-->
        <property name="hibernate.event.merge.entity_copy_observer" value="allow"/>
        <property name="hibernate.enable_lazy_load_no_trans" value="true"/>
        <!--<property name="hibernate.show_sql" value="true"/>-->
        <!--<property name="hibernate.format_sql" value="true"/>-->
        <!--<property name="hibernate.hbm2ddl.auto" value="create-drop"/>-->
        <!--<property name="hibernate.hbm2ddl.auto" value="create"/>-->
        <!--<property name="hibernate.hbm2ddl.auto" value="update"/>-->

        <property name="hibernate.hbm2ddl.import_files_sql_extractor"
                  value="org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor"/>
        <property name="hibernate.hbm2ddl.import_files" value="initialize-database.sql"/>
    </properties>
  </persistence-unit>
</persistence>

note : hibernate.hbm2ddl.import_files in persistence.xml .
actually too more handy .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM