简介(如果感到无聊,请跳过;)
你好,伙计们, 我正在做一个我继承的学校项目"来自前一年的小组。它是一个用Java EE构建的Web应用程序,用于对测试培训进行游戏化。我的任务是在应用程序中包含一个新的vulernability(XXE)。现有的应用程序混合了CDI和EJB注释,虽然我还没有理解它的原因。我和我的小组中的任何人都没有任何Java EE经验,虽然我在过去几周内尽力加快速度,但我对以下问题几乎没有丢失,特别是没有肮脏的黑客"这将打破MVC模式。
问题
我可以成功地将用户上传的.xml反序列化为" Product"的实例。 (或者在用户上传恶意数据时触发漏洞利用,就像我想要的那样)。
但持久化该对象并不起作用,这似乎与我的UploadController类的范围有关。到目前为止,持久性只在InitBean中完成,它是一个单例,并在应用程序启动后立即实例化。
原始作者并不打算在启动应用程序后允许将更多对象添加到数据库中。
到目前为止我尝试了两条路线: - 在我的UploadController中模仿来自InitBean的PersistenceContext的使用(参见下面的代码)。这导致javax.persistence.TransactionRequiredException。我做了一些关于错误的阅读,我猜我的控制器生命周期不对,但我不太明白。
我还尝试在InitBean中包装持久化功能并将其作为静态方法公开(感觉超级难看,也没有工作)。
问题
最后,我的问题是:在应用程序启动后将项目添加到数据库的正确方法是什么?我是否应该从我的控制器类中执行此操作,如果是,那么如何获得正确的范围/事务?或者我应该完全走另一条路?非常感谢解释。
型号:
@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;
}
}
}
提前感谢您的帮助!
答案 0 :(得分:0)
以前我使用@Singleton + @Startup就像你做的那样 但这种方法存在一些问题 现在我用这个方法:
<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>
注意:persistence.xml中的hibernate.hbm2ddl.import_files 实际上太方便了。