JPARepository - delete using date comparison with derived query - postgresql

I'm trying to use JPARepository in Spring Boot to delete records that are less than a certain date, for for a given userid
Should be something like this Delete * from [table] where expiration_date < [date] and userid = [userid]
I thought I should be able to use one of the automatically generated methods
int deleteByExpiryDateBeforeAndUser(Date date, User user);
But this is generating a Select and not a Delete. What am I doing wrong?
Update
Entity class
#Getter
#Setter
#ToString
#Entity(name = "refresh_token")
public class RefreshToken {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#OneToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
#Column(nullable = false, unique = true)
private String token;
#Column(nullable = false)
private Date expiryDate;
public RefreshToken() {
}
}
Repository class
#Repository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
Optional<RefreshToken> findByToken(String token);
#Modifying
void deleteByUserIdAndExpiryDateBefore(Long userId, Date expiryDate);
int deleteByUser(User user);
}
Here's how I'm calling it
#Transactional
public void deleteExpiredTokens(User user) {
refreshTokenRepository.deleteByUserIdAndExpiryDateBefore(user.getId(), new Date());
}

You see a select statement because Spring Data first loads entities by condition.
Then once entities became 'managed' Spring Data issues a delete query for each entity that was found.
If you want to avoid redundant SQL query - you have to consider #Query annotation.
Then your code will look like this:
#Repository
public interface RefreshTokenRepository extends JpaRepository<RefreshToken, Long> {
// ...
#Query(value = "DELETE FROM refresh_token WHERE user_id =:userId AND expiry_date < :expiryDate", nativeQuery = true)
#Modifying
void deleteByUserIdAndExpiryDateBefore(Long userId, Date expiryDate);
//...
}

Related

#Transactional in spring JPA

I have a spring boot application where I need to update a migratedCustomer db table based on userId and phoneNumber.
Since I have to use for loop in the service layer for every update, it is creating a
new transaction and performance is hampered.
how could I make sure only one transaction is created and hence to improve the performance. code is like below
#Entity
#Table(name = "MigratedCustomer")
public class MigratedCustomer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String userId;
private String phoneNumber;
#Temporal(TemporalType.TIMESTAMP)
private Date createdTimestamp;
private int batchNumber;
private String comment;
}
public class MigratedCustomerService {
#Autowired
private UserRepository userRepository;
public void updateMsisdn(List<MigratedCustomer> savedCustomers) {
for (MigratedCustomer savedCustomer : savedCustomers) {
userRepository.updateStatus(savedCustomer.getUserId(),
savedCustomer.getPhoneNumber());
}
}
}
public interface MsisdnRepository extends JpaRepository<Msisdn, Long> {
#Modifying
#Query(value = "UPDATE Msisdn SET status=INACTIVE where userId=:userId and phoneNumber=:phoneNumber",
nativeQuery = true)
void updateStatus(#Param("userId") String userId, #Param("phoneNumber") String phoneNumber);
}

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

Spring Data JPA order by value from OneToMany relation

I am trying to sort a result by nested collection element value. I have a very simple model:
#Entity
public class User {
#Id
#NotNull
#Column(name = "userid")
private Long id;
#OneToMany(mappedBy = "user")
private Collection<Setting> settings = new HashSet<>();
// getters and setters
}
#Entity
public class Setting {
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "userid")
private User user;
private String key;
private String value;
// getters and setters
}
public interface UserRepository extends JpaRepository<User, Long>, QuerydslPredicateExecutor<User> {
}
I want to have a result returned sorted by the value of one setting.
Is it possible to order by user.settings.value where settings.name = 'SampleName' using Spring Data JPA with QueryDSL?
I've used JpaSpecificationExecutor. let's see findAll for example.
Page<T> findAll(#Nullable Specification<T> spec, Pageable pageable);
Before call this method you can create your specification dynamically (where condition) and Pageable object with dynamic Sort information.
For example
...
Specification<T> whereSpecifications = Specification.where(yourWhereSpeficiation);
Sort sortByProperty = Sort.by(Sort.Order.asc("property"));
PageRequest orderedPageRequest = PageRequest.of(1, 100, sortByProperty);
userRepository.findAll(whereSpecifications, PageRequest.of(page, limit, orderedPageRequest));

Hibernate Envers - custom RevisionEntity - how to get record

I have written my custom RevisionEntity class to store additional data (for example username), like below:
#Entity
#RevisionEntity(AuditListener.class)
#Table(name = "REVINFO", schema = "history")
#AttributeOverrides({
#AttributeOverride(name = "timestamp", column = #Column(name = "REVTSTMP")),
#AttributeOverride(name = "id", column = #Column(name = "REV")) })
public class AuditEntity extends DefaultRevisionEntity {
private static final long serialVersionUID = -6578236495291540666L;
#Column(name = "USER_ID", nullable = false)
private Long userId;
#Column(name = "USER_NAME")
private String username;
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
I can see that all rows in database are correctly stored, REVINFO table contains also username.
I would like to query database to get detailed information from my custom RevisionEntity, like username.
How can I do it? Is there any supported API to get it?
Lets assume you know the identifier of the entity you're interested in the revision entity metadata for, you can easily query that information using the following approach:
final AuditReader auditReader = AuditReaderFactory.get( session );
List<?> results = auditReader.createQuery()
.forRevisionsOfEntity( YourEntityClass.class, false, false )
.add( AuditEntity.id().eq( yourEntityClassId ) )
.getResultList();
The returned results will contain an Object array, e.g. Object[] where results[1] will hold the revision entity instance which contains the pertinent information your wanting.
For more details, you can see the java documentation comments here
If you only have the revision number, you can access just the revision entity instance directly by:
// I use YourAuditEntity here because AuditEntity is actually an Envers class
YourAuditEntity auditEntity = auditReader
.findRevision( YourAuditEntity.class, revisionId );
For more details on the AuditReader interface, you can see the java documentation here

JPA #MappedSuperclass realisation in Grails 2.4

I found this solution, that doesn't solve the problem. In JPA we can do this:
#MappedSuperclass
public class BasicEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(updatable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date created = new Date();
#Temporal(TemporalType.TIMESTAMP)
private Date modified = new Date();
}
#Entity
public class User extends BasicEntity {
private String username;
private String password;
}
Then, hibernate.hbm2ddl.auto generates one table with all inherited columns and this is exactly what I want:
user (
id,
created,
modified,
username,
password,
)
In Grails I do this
abstract class BasicEntity {
static mapping = {
tablePerSubclass true
}
Date dateCreated
Date lastUpdated
}
class User extends BasicEntity {
String username
String password
}
And it generates me two tables with no inheritance
basic_entity (
id,
version,
date_created,
last_updated,
)
user (
id,
username,
password,
)