Reputation: 1459
I have a JEE application running on WildFly 10.1.0 using JPA and MySQL.
I have a stateless service that represents a web crawler that extracts content from a given web-site, creates structured POJO entities and saves these entities in the database. My problem is that although no exceptions are thrown, the JPA entities are not being saved in the database. My stateless web crawler:
@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();
}
}
}
My HomeToDealDBService class:
@Stateless
public class HomeToDealDBService {
@PersistenceContext
private EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void saveEntity(HomeToDeal homeToDeal) throws EntityExistsException{
this.em.persist(homeToDeal);
}
}
My POJO class:
@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;
My servlet that triggers the crawler:
@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);
}
}
}
My 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>
my datasource in the 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>
The result in the database:
select * from home;
My 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>
If I only change my TestServlet like this:
@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);
}
}
}
It works though. What might be the problem?
Upvotes: 3
Views: 515
Reputation: 1459
The problem in this case was the annotation
@TransactionAttribute(TransactionAttributeType.REQUIRED)
above the method
public void saveEntity(HomeToDeal homeToDeal) throws EntityExistsException{
this.em.persist(homeToDeal);
}
In that case, when being called from the method ImmoweltBot.crawl() saveEntity() is actually called from within the same transaction, until the method ImmoweltBot.crawl() completes. This is why the entities are not beind saved in the database before the end of the transaction - means before the method ImmoweltBot.crawl() completes. To make each entity being saved separately means to save each entity in its own transaction. This could happen if I change the annotation of the method saveEntity() as follows:
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void saveEntity(HomeToDeal homeToDeal) throws EntityExistsException{
this.em.persist(homeToDeal);
}
this is actually EXACTLY what I wanted - that if a save of a particular entity fails, this shall have no impact of the save of the rest of the entities.
Upvotes: 2
Reputation: 629
<property name="hibernate.hbm2ddl.auto" value="update"/>
may solve the problem.
If you put value="create"
that means every time you run the application are creating the table again and again.
Upvotes: 0