JPA 实体未保存在数据库中
Posted
技术标签:
【中文标题】JPA 实体未保存在数据库中【英文标题】:JPA Entities not saved in the database 【发布时间】:2019-11-05 09:01:06 【问题描述】:我有一个使用 JPA 和 mysql 在 WildFly 10.1.0 上运行的 JEE 应用程序。
我有一个无状态服务,它代表一个网络爬虫,它从给定的网站中提取内容,创建结构化的 POJO 实体并将这些实体保存在数据库中。我的问题是,虽然没有抛出异常,但 JPA 实体并没有保存在数据库中。 我的无状态网络爬虫:
@Stateless
public class ImmoweltBot
public static final String URL = "https://www.immowelt.at/";
public static final String queryURL = URL + "/liste/wien/wohnungen/mieten?sort=relevanz";
@Inject
private HomeToDealDBService homeToDealDBService;
@PostConstruct
public void initialize()
System.setProperty("webdriver.chrome.driver", "C:\\Temp\\chromedriver.exe");
public void crawl () throws Exception
String URLPage = StringUtils.EMPTY;
int page = 1;
int totalNumberOfEntities = 6000;
int numberOfEntitiesFound = 0;
List<WebElement> elemnts = new ArrayList<>();
WebDriver webDriver = new ChromeDriver();
outer:
while (numberOfEntitiesFound < totalNumberOfEntities)
webDriver.get(queryURL + URLPage);
final int totalNumberOfKeyDowns = 190;
int keyDownTries = 0;
while ((++keyDownTries < totalNumberOfKeyDowns))
webDriver.findElement(By.tagName("body")).sendKeys(Keys.DOWN);
elemnts = webDriver.findElements(By.xpath("//*[contains(@class, 'clear relative js-listitem')]"));
WebElement elem = webDriver.findElement(By.xpath("//*[contains(@class, 'ellipsis margin_none')]"));
totalNumberOfEntities = Utils.parseNumber(elem.getText()).intValue();
for (int i = 0; i < elemnts.size(); i++)
WebElement divListItemClear = elemnts.get(i);
HomeToDeal homeToRent = new HomeToDeal();
String exposeURL = divListItemClear.findElement(By.tagName("a")).getAttribute("href");
homeToRent.setURL(exposeURL);
try
homeToDealDBService.saveEntity(homeToRent);
Logger.getLogger(ImmoweltBot.class).info("Entity " + homeToRent + " saved");
catch (EntityExistsException eex)
Logger.getLogger(ImmoweltBot.class).warn("Entity already saved " + homeToRent.getURL());
break outer;
URLPage = "&cp="+ (++page);
numberOfEntitiesFound+=elemnts.size();
我的 HomeToDealDBService 类:
@Stateless
public class HomeToDealDBService
@PersistenceContext
private EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void saveEntity(HomeToDeal homeToDeal) throws EntityExistsException
this.em.persist(homeToDeal);
我的 POJO 类:
@Entity
@Table(name = "home")
@Access(AccessType.FIELD)
public class HomeToDeal implements Comparable<HomeToDeal>, Serializable
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@Column(name = "id", updatable=false, nullable=false)
private int id;
private String URL;
private String mainImage;
private String imageFolderPath;
private String description;
private byte noRooms;
private BigDecimal nutzflaeche; // area in sqm
private BigDecimal wohnflaeche;
我的触发爬虫的servlet:
@WebServlet("/test")
public class TestServlet extends HttpServlet
private static final long serialVersionUID = 1L;
@Inject
private ImmoweltBot immoweltBot;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException
try
immoweltBot.crawl();
catch (Exception e)
throw new ServletException(e);
我的persistence.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="IamOK" transaction-type="JTA">
<description>Home to Deal</description>
<provider>org.hibernate.ejb.HibernatePersistence</provider>
<jta-data-source>java:/MySqlDS_HomeToDeal</jta-data-source>
<properties>
<property name="hibernate.dialect" value = "org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.cache.provider_class" value="org.hibernate.cache.NoCacheProvider"/>
<property name="hibernate.show_sql" value="true"></property>
<property name="hibernate.max_fetch_depth" value="3" />
<property name="hibernate.hbm2ddl.auto" value="create"/>
<property name="hibernate.show_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
WildFly Standalone.xml 中的我的数据源:
<datasource jta="true" jndi-name="java:/MySqlDS_HomeToDeal" pool-name="MySqlDS_HomeToDeal" enabled="true" use-java-context="true" use-ccm="true">
<connection-url>jdbc:mysql://localhost:3306/homeToDeal?autoReconnect=true</connection-url>
<driver>mysql</driver>
<transaction-isolation>TRANSACTION_READ_COMMITTED</transaction-isolation>
<pool>
<min-pool-size>10</min-pool-size>
<max-pool-size>100</max-pool-size>
<prefill>true</prefill>
</pool>
<security>
<user-name>homeToDealuser</user-name>
<password>homeToDeal123</password>
</security>
<validation>
<check-valid-connection-sql>SELECT 1</check-valid-connection-sql>
</validation>
<timeout>
<idle-timeout-minutes>0</idle-timeout-minutes>
<query-timeout>600</query-timeout>
</timeout>
<statement>
<prepared-statement-cache-size>100</prepared-statement-cache-size>
<share-prepared-statements>true</share-prepared-statements>
</statement>
</datasource>
数据库中的结果:
select * from home;
我的 pom.xml:
<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>seller</groupId>
<artifactId>home.digest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>home.digest Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://maven.apache.org</url>
<repositories>
<repository>
<id>prime-repo</id>
<name>Prime Repo</name>
<url>http://repository.primefaces.org</url>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.10</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.6</version>
</dependency>
<dependency>
<groupId>org.primefaces</groupId>
<artifactId>primefaces</artifactId>
<version>6.2</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.0.1.GA</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.hibernate.javax.persistence/hibernate-jpa-2.1-api -->
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.2.Final</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.enterprise/cdi-api -->
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<version>2.0.SP1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-web</artifactId>
<version>7.1.1.Final</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jboss.spec.javax.ejb/jboss-ejb-api_3.2_spec -->
<dependency>
<groupId>org.jboss.spec.javax.ejb</groupId>
<artifactId>jboss-ejb-api_3.2_spec</artifactId>
<version>1.0.2.Final</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.cloud/google-cloud-translate -->
<dependency>
<groupId>com.google.cloud</groupId>
<artifactId>google-cloud-translate</artifactId>
<version>1.61.0</version>
</dependency>
<dependency>
<!-- jsoup html parser library @ https://jsoup.org/ -->
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.11.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
<build>
<finalName>home.digest</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven
defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
如果我只像这样更改我的 TestServlet:
@WebServlet("/test")
public class TestServlet extends HttpServlet
@Inject
private HomeToDealDBService homeToDealDBService;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException
try
HomeToDeal home = new HomeToDeal();
home.setURL("alabale");
homeToDealDBService.saveEntity(home);
catch (Exception e)
throw new ServletException(e);
虽然有效。可能是什么问题?
【问题讨论】:
【参考方案1】:这种情况下的问题是注释
@TransactionAttribute(TransactionAttributeType.REQUIRED)
在方法之上
public void saveEntity(HomeToDeal homeToDeal) throws EntityExistsException
this.em.persist(homeToDeal);
在这种情况下,当从方法 ImmoweltBot.crawl() 调用 saveEntity() 时,实际上是从同一事务中调用,直到方法 ImmoweltBot.crawl() 完成。这就是为什么在事务结束之前实体没有保存在数据库中的原因 - 意味着在 ImmoweltBot.crawl() 方法完成之前。使每个实体分别保存意味着将每个实体保存在自己的事务中。如果我将方法 saveEntity() 的注释更改如下:
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void saveEntity(HomeToDeal homeToDeal) throws EntityExistsException
this.em.persist(homeToDeal);
这实际上正是我想要的——如果一个特定实体的保存失败,这不会影响其他实体的保存。
【讨论】:
【参考方案2】:<property name="hibernate.hbm2ddl.auto" value="update"/>
可以解决问题。
如果您输入value="create"
,这意味着您每次运行应用程序时都会一次又一次地创建表。
【讨论】:
Hoq:谢谢,但这并不能解决我的问题,请参阅上述问题末尾的更新。 @K.Nicholas 我创建了另一个爬虫类,那里也有同样的情况以上是关于JPA 实体未保存在数据库中的主要内容,如果未能解决你的问题,请参考以下文章
Spring Data Jpa Repository 未将实体保存到数据库