JPA transaction/rollback behaviour with objects persisted via cascade - jpa

I have two objects Antrag (application) and Anlage (facility). An application can be made for multiple facilities. The application is persisted directly in the DAO. The facilities are persisted via cascade.
#Entity
#Table(name = "EEG_ANTRAG")
public class Antrag implements Serializable {
private static final long serialVersionUID = -2440344011443487714L;
#Id
#Column(name = "ANT_ID", nullable = false)
#SequenceGenerator(name = "sequenceGeneratorAntrag", sequenceName = "EEG_ANTRAG_SEQ", allocationSize = 1)
#GeneratedValue(generator = "sequenceGeneratorAntrag")
#Getter #Setter private Long id;
#OneToMany(mappedBy = "antrag", cascade = { CascadeType.ALL }, orphanRemoval = true)
#OrderBy("id ASC")
#Getter private List<Anlage> anlageList = new ArrayList<Anlage>();
public Anlage addAnlage(Anlage anlage)
anlageList.add(anlage);
anlage.setApplication(this);
return anlage;
}
/* some more simple attributes; just Strings, boolean, .. */
}
#Entity
#Table(name = "EEG_ANLAGE")
public class Anlage implements Serializable {
private static final long serialVersionUID = -3940344011443487741L;
#Id
#Column(name = "ANL_ID")
#SequenceGenerator(name = "sequenceGeneratorAnlage", sequenceName = "EEG_ANLAGE_SEQ", allocationSize = 1)
#GeneratedValue(generator = "sequenceGeneratorAnlage")
#Getter #Setter private Long id;
#ManyToOne
#JoinColumn(name = "ANL_ANT_ID")
#Getter #Setter private Antrag antrag;
/* some more simple attributes; just Strings, boolean, .. */
}
#Stateless
public class AntragDaoBean implements AntragDaoLocal {
#PersistenceContext(unitName = "ejb-model")
private EntityManager em;
#Override
public void persistAntrag(Antrag antrag) {
em.persist(antrag);
}
}
When an error occurs on inserting the facilities, e.g. some column name is misspelled in the entity, an exception is thrown. The stacktrace indicates, that a rollback was performed. The problem is, that the application is still persisted. Shouldn't the insertion of the application be rolled back as well?
We are using EclipseLink 2.4.1. The EclipseLink debug output states, that all inserts are performed in one single transaction. The database is Oracle 11g.
Is my ecpectation of the transactional behaviour wrong? How do I get the behaviour I want?
/* shortened exemplary stacktrace for rollback */
EvaluationException:
javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:101)
EJBTransactionRolledbackException:
org.jboss.as.ejb3.tx.CMTTxInterceptor.handleEndTransactionException(CMTTxInterceptor.java:115)
RollbackException:
com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1177)
DatabaseException:
org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:324)
SQLSyntaxErrorException:
oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:445)

Your expectation is correct: everything should be made in a single transaction, and the insertion of Antrag should be rolled back as well.
I think your persistence-unit is simply not JTA: test in the persistence.xml file that you have something like:
<persistence-unit name="ejb-model" transaction-type="JTA">
<jta-data-source>java:/someNameDB</jta-data-source>

Related

Why JPA update entity causes stackoverflow?

I have an entity Task with id. Tasks belongs to Config. I need to update Task with it's Config doesn't change. Here is my code:
Task:
#Entity
#Getter
#Setter
public class Task{
#ManyToOne
#JoinColumn(name = "config_id", referencedColumnName = "id")
private Config config;
#OneToMany(orphanRemoval = true,cascade = CascadeType.ALL)
#JoinColumn(name="task_id")
private Set<ActivityItemTask> activityItemTasks = new HashSet<>();
}
#Entity
public class ActivityItemTask {
private Double score;
#EmbeddedId
private ActivityItemTaskId activityItemTaskId;
#Getter
#Setter
#Embeddable
#NoArgsConstructor
#AllArgsConstructor
public static class ActivityItemTaskId implements Serializable {
#ManyToOne
#JoinColumn(name = "activity_item_id")
private ActivityItem activityItem;
#ManyToOne
#JoinColumn(name = "task_id")
private Task task;
#ManyToOne
#JoinColumn(name = "config_id")
private TaskConfig config;
}
}
Config:
#Entity
#Getter
#Setter
public class Config{
#OneToMany(mappedBy = "config")
private Set<Task> tasks = new HashSet<>();
}
TaskService:
#Service
public class TaskService{
#Resource
TaskRepository taskRepository;
#Transactional
public Long save(Taskdto dto){
Config config = new Config();
config.setId(task.getConfigId());
s.setTaskConfig(config);
return taskRepository.save(s).getId();
}
}
TaskDto:
#Data
public class TaskDto {
private Long id;
#NotNull
private Long configId;
private String name;
private Date beginDate;
private Date endDate;
private String note;
}
when TaskService#save was called , it throw StackOverflowException:
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.StackOverflowError
the log shows that my application querys task record and querys task's config and config's tasks and so on.
I am wondering what's wrong with my association annation. Any advice are appreciated.
I'm sorry.I have written another 2 calss so that I can find out the truth. It turns out my third calss ActivityItemTask may be the root cause. I think Task. activityItemTasks may should be annnotation with mappedBy=? But which field should be writtern here?
I did config wrong association.
Task should be :
#OneToMany(mappedBy = "activityItemTaskId.task")
// #JoinColumn(name = "task_id")
private Set<ActivityItemTask> activityItemTasks = new HashSet<>();
This is how to use mappedBy annotation with embedable class.
Thank all you guys commented or answered. You did help me find it out.

Spring batch JdbcCursorItemReader : reading from tables having FK relation

Here's my Reader :
private static final String SELECT_ALL_BOOKS = "SELECT * FROM BOOKS WHERE COLOR = 'yellow'";
#Bean
public JdbcCursorItemReader<BookEntity> itemReader(final DataSource dataSource) {
return new JdbcCursorItemReaderBuilder<BookEntity>()
.name("book_reader")
.sql(SELECT_ALL_BOOKS)
.dataSource(dataSource)
.rowMapper(new BeanPropertyRowMapper<>(BookEntity.class))
.build();
}
And my entity :
#Entity
#Getter
#Setter
#Table(name = "book")
#AllArgsConstructor
#NoArgsConstructor
public class BookEntity implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_book")
private Integer idBook;
#Column(name = "color")
private String color;
#Column(name = "page_number")
private Integer pageNumber;
#Column(name = "read")
private Boolean read;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JoinColumn(name = "id_author")
private Author author;
...
...
}
Problem is : with my job executing my step, I'm having the books but with an author = null. So the foreign key is not mapped correctly, all the other String/Integer/Boolean fields are retrieved correctly.
I'm new with Spring batch so maybe I'm using a wrong kind of reader.
Any one has an idea about the problem ? Thanks
Please refer this- You are using JDBC Item reader that is mapped to native columns and properties are binded by BeanPropertyRowMapper
https://docs.spring.io/spring-batch/docs/current/reference/html/readersAndWriters.html#JdbcCursorItemReader
Change the query join with AUTHOR tables as a native query or use JPA readers to support ORM
Below reference will give repository reader
https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/item/data/RepositoryItemReader.html
Below example give some idea to implement and its SO references
https://github.com/gpassini/Spring-Batch-RepositoryItemReader-NativeQuery-Example/tree/master/src/main

Named Query with List of object as input using JPA named query

Getting below QueryExecutionRequestException when I try to excecute update Named query using JPA repository.
org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.company.farmer.entity.FarmProducts p set p.isDeleted=:isDeleted where p.productId IN (:productIdsList_0, :productIdsList_1)]; nested exception is java.lang.IllegalStateException: org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.company.farmer.entity.FarmProducts p set p.isDeleted=:isDeleted where p.productId IN (:productIdsList_0, :productIdsList_1)]
Code:
#Repository
public interface FarmProductRepository extends JpaRepository<FarmProducts, Long> {
void deleteProduct(#Param("isDeleted") String isDeleted, #Param("productIdsList") List<Long> productIdsList);
}
#Override
public String deleteProductAndCategory(long categoryId, FarmProductIdsDTO farmProductIds) {
farmProductRepository.deleteProduct(FarmerProductCategoryConstants.DELETE_YES_FLAG, farmProductIds.getFarmProductIds());
return FarmerProductCategoryConstants.SUCCESS;
}
#Entity
#Table(name="farm_products")
#Getter
#Setter
#ToString(exclude= "productCategory")
#NoArgsConstructor
#AllArgsConstructor
#NamedQueries({#NamedQuery(name="FarmProducts.deleteProduct", query="update FarmProducts p set p.isDeleted=:isDeleted where p.productId IN (:productIdsList)") })
public class FarmProducts extends BaseModel {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "FARM_PRODUCT_GENERATOR")
#SequenceGenerator(name = "FARM_PRODUCT_GENERATOR", sequenceName = "FARM_PRODUCT_GENERATOR_SEQ", allocationSize = 1)
#Column(name = "farm_product_id")
public Long productId;
#ManyToOne
#JoinColumn(name = "farm_product_category_id")
#JsonIgnoreProperties("products")
public ProductCategory productCategory;
#Column(name = "product_name")
public String product;
#Column(name = "is_deleted")
public String isDeleted;
}
I am trying to pass a list of productId to make the isDeleted as "N". But update functionality fails because of the QueryExecutionRequestException.
By default Spring Data treats all queries as SELECT statements. If you have an UPDATE (or DELETE) statement you have you have to apply de #Modifying annotation on the query.
See https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.modifying-queries

Hibernate Postgres Slow Insert

I try to insert data into Postgres using JPA/Hibernate/Postgres.
The data is parsed from a CSV File and then should be saved into a postgres database. The code that persists the data looks as follows:
#Autowired
KundeRepository repo;
#Transactional
public void safe(Kunde kd) {
repo.save(kd);
}
public void safeAll(Iterable<Kunde> kt) {
repo.save(kt);
repo.flush();
}
The entity look as follows
public class account implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
#ManyToOne
Kunde kunde;
#OneToMany
List<TransaktionsGruppe> gruppen;
#Entity
#Table(name = "kunde")
#NoArgsConstructor
public class Kunde implements Serializable {
public static final String kundennummerKey = "KUNDENNUMMER";
private static final long serialVersionUID = 1L;
#Id
#Getter
#Setter
private String id;
#OneToMany
List<Account> accounts;
#Entity
#Table(name = "transaktionsgruppe")
public class Transaktionsgruppe implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
Account acc;
private String bezeichnung;
When I now pass a Collection to the safeAll method the inserts are really slow. Especially it seems to call the hibernate sequence for every insert. Is there a way to speed things up?
My configuration looks as follows:
datasource:
type: com.zaxxer.hikari.HikariDataSource
url: dburl
username: user
password: pw
hikari:
validation-timeout: 10000
health-check-properties: {"connectivityCheckTimeoutMs","1000"}
jpa:
show-sql: true
properties:
hibernate.cache.use_second_level_cache: false
hibernate.cache.use_query_cache: false
hibernate.generate_statistics: false
hibernate.jdbc.batch.size: 100
hibernate.order_inserts: true+
Currently show-sql is enabled. It is about 60000 Entities overall and it takes more than 20 Minutes. the entitties are rather small in size
Your safeAll() method is not annotated with #Transactional so Spring opens and closes a transaction for each item of your list. By annotating it, Spring will open and close one single transaction for the whole list.

Why am I getting "Duplicate entry" errors for related objects upon merge in eclipselink?

I have an entity class that contains a map of key-value pairs which live in a different table and there may be no such pairs for a given entity. The relevant code for the entity classes is below.
Now, when I insert such an entity with persist(), then add key-value pairs, and then save it with merge(), I get duplicate entry errors for the related table that stores the key-value pairs. I tried to hold back insertion until the keys were added, to have one call to persist() only. This led to duplicate entry errors containing an empty (zero) id in the foreign key column (ixSource).
I followed the process in the debugger, and found that eclipselink seems to be confused about the cascading. While it is updating the entity, it executes calls that update the related table. Nonetheless, it also adds those operations to a queue that is processed afterwards, which is when the duplicate entry errors occur. I have tried CascadeType.ALL and MERGE, with no difference.
I'm using static weaving, if it matters.
Here's the entities`code, shortened for brevity:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "sType")
#Table(name = "BaseEntity")
public abstract class BaseEntity extends AbstractModel
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ix")
private long _ix;
}
#Entity
#Table(name = "Source")
public class Source extends BaseEntity
{
#OneToMany(cascade = CascadeType.MERGE)
#JoinTable(name = "SourceProperty", joinColumns = { #JoinColumn(name = "ixSource") })
#MapKey(name = "sKey")
private Map<String, SourceProperty> _mpKeys;
// ... there's more columns that probably don't matter ...
}
#Entity
#Table(name = "SourceProperty")
#IdClass(SourcePropertyKey.class)
public class SourceProperty
{
#Id
#Column(name = "sKey", nullable = false)
public String sKey;
#Id
#Column(name = "ixSource", nullable = false)
public long ixSource;
#Column(name = "sValue", nullable = true)
public String sValue;
}
public class SourcePropertyKey implements Serializable
{
private final static long serialVersionUID = 1L;
public String sKey;
public long ixSource;
#Override
public boolean equals(Object obj)
{
if (obj instanceof SourcePropertyKey) {
return this.sKey.equals(((SourcePropertyKey) obj).sKey)
&& this.ixSource == ((SourcePropertyKey) obj).ixSource;
} else {
return false;
}
}
}
I can't see how those errors would occur. Could you include the SQL and ful exception.
What version of EclipseLink are you using, did you try the latest release?
Why are you calling merge? Are you detaching the objects through serialization, if it is the same object, you do not need to call merge.
It could be an issue with the #MapKey, does it work if you remove this?