#Transactional in spring JPA - spring-data-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);
}

Related

Spring Data JPA #OneToOne mapping is not projected

This question is already phrased as an issue here: https://github.com/spring-projects/spring-data-jpa/issues/2369 but for lack of a reaction there I am copying the contents of that issue here, hoping that somebody might find what's wrong with my code or confirm that this could be a bug:
I've set up an example project here that showcases what seems to be a bug in Spring Data projections: https://github.com/joheb-mohemian/gs-accessing-data-jpa/tree/primary-key-join-column-projection-bug/complete
I have a Customer entity that has a OneToOne mapping to an Address entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
#OneToOne(mappedBy = "customer", cascade = CascadeType.ALL)
#PrimaryKeyJoinColumn
private Address address;
//...
}
#Entity
public class Address {
#Id
#Column(name = "customer_id")
private Long id;
#OneToOne
#MapsId
#JoinColumn(name = "customer_id")
private Customer customer;
private String street;
//...
}
Then there are simple projection interfaces:
public interface CustomerProjection {
String getFirstName();
String getLastName();
AddressProjection getAddress();
}
public interface AddressProjection {
String getStreet();
}
But when I try to fetch a projected entity from a repository method like this one:
public interface CustomerRepository extends CrudRepository<Customer, Long> {
//...
<T> T findById(long id, Class<T> type);
}
, getAddress() on the projection will be null, whereas getAddress() when fetching the entity type is populated correctly. Of these two unit tests, only testEntityWithOneToOne()will be successful:
#BeforeEach
void setUpData() {
customer = new Customer("first", "last");
Address address = new Address(customer, "street");
customer.setAddress(address);
entityManager.persist(address);
entityManager.persist(customer);
}
#Test
void testEntityWithOneToOne() {
Customer customerEntity = customers.findById(customer.getId().longValue());
assertThat(customerEntity.getAddress()).isNotNull();
}
#Test
void testProjectionWithOneToOne() {
CustomerProjection customerProjection = customers.findById(customer.getId(), CustomerProjection.class);
assertThat(customerProjection.getAddress()).isNotNull();
}
What's the problem here?

How to find the specified field in spring-data-jpa

I want to find the specified field with the interface which extends JpaSpecificationExecutor.but i have not idea.
for example,i just need to find id,nickname in the user entity,what shall I do?
#Service
public class UserService {
public Page<User> findAll(User user, Pageable pageable) {
List<Predicate> predicates = new ArrayList<>();
if (user != null) {
//some condition
}
query.where(predicates.toArray(new Predicate[predicates.size()]));
return query.getRestriction();
}
}
#Repository
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}
#Data
#Entity
#Table(name = "sys_user")
public class Note implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(nullable = false, unique = true)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String nickname;
}
Here is answer on your question: https://www.baeldung.com/spring-data-jpa-projections .

Spring Data JPA Specification Manyto One relationship

I have two entities in Spring Data JPA:
User--->* TaxPayment
The goal is to get the all the taxpayments related to user_id:
User.java
public class User extends AbstractAuditingEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
..........
}
TaxPayment.jva
public class TaxPayment implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotNull
#Column(name = "payment_date", nullable = false)
private LocalDate paymentDate;
#NotNull
#Column(name = "amount", precision=10, scale=2, nullable = false)
private BigDecimal amount;
#Column(name = "reference")
private String reference;
#ManyToOne
private User user;
public Long getId() {
return id;
}..
}
I dont wan't to have oneTOMany Annotation from User.java and the column mapped in the Taxpayment as user_id.
Specification class as follows:
final class TaxPaymentSpecification {
private TaxPaymentSpecification(){
}
static Specification<TaxPayment> hasUser(Long userId){
return new Specification<TaxPayment>() {
#Override
public Predicate toPredicate(Root<TaxPayment> arg0, CriteriaQuery<?> arg1,
CriteriaBuilder arg2) {
// TODO Auto-generated method stub
Root<TaxPayment> root = arg0;
Subquery<Long> subqry = arg1.subquery(Long.class);
Root<User> user = subqry.from(User.class);
final Join<User,TaxPayment> taxpays = root.join("user");
subqry.select(taxpays.<Long> get("user_id"));
subqry.where(arg2.equal(user.<Long> get("id"),userId));
return arg2.in(arg0.get("user_id")).value(subqry);
}
};
}
}
Is specification is correct or wrong as per my goal to get all the TaxPayment related to user_id?
There is no need to use subquery if you already know the userId, which will be the value of user_id in TaxPayment table:
#Override
public Predicate toPredicate(Root<TaxPayment> arg0, CriteriaQuery<?> arg1,
CriteriaBuilder arg2) {
return arg2.equal(arg0.get("user_id"), userId);
}

There is no ID defined for this entity hierarchy

I am stuck with this error message, that appears every time I want to add a ManytoOne relationship with another entity class.
The class must use a consistent access type (either field or property). There is no ID defined for this entity hierarchy
This is my entity Transaction
#Entity
#Table(name = "CustomerTransaction")
public class CustomerTransaction implements Serializable {//this is the line with the error message
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne //This generates the problem
#JoinColumns({
#JoinColumn(name = "CUS_ID", referencedColumnName = "IDCUSTOMER") })
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
private long transactionID;
#Temporal(TemporalType.TIMESTAMP)
private Date buyDate;
public Date getBuyDate() {
return buyDate;
}
public void setBuyDate(Date buyDate) {
this.buyDate = buyDate;
}
public long getTransactionID() {
return transactionID;
}
public void setTransactionID(long transactionID) {
this.transactionID = transactionID;
}
public String getCarYear() {
return carYear;
}
public void setCarYear(String carYear) {
this.carYear = carYear;
}
public Date getTransactionDate() {
return transactionDate;
}
public void setTransactionDate(Date transactionDate) {
this.transactionDate = transactionDate;
}
private String carYear;
#Temporal(TemporalType.TIMESTAMP)
private Date transactionDate;
JPA annotation should all be placed either on fields or on accessor methods. You've placed the #Id and #GeneratedValue annotation on a field (private Long id), but #ManyToOne and #JoinColumns on a getter (public Long getId()). Move the latter on a field as well.
i had similar error but in the end, i realized #Id was referencing this package org.springframework.data.annotation.Id instead of javax.persistence.Id. i was using #MappedSuperClass approach so as soon as i corrected this, everything worked fine
You need to import #Id from "import javax.persistence.Id;"

How to correctly do a manytomany join table in JPA?

I need 3 entities: User, Contract (which are a many to many relation) and a middle entity: UserContract (this is needed to store some fields).
What I want to know is the correct way to define the relationships between these entities in JPA/EJB 3.0 so that the operations (persist, delete, etc) are OK.
For example, I want to create a User and its contracts and persist them in a easy way.
Currently what I have is this:
In User.java:
#OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
private List<UserContract> userContract;
In Contract.java:
#OneToMany(mappedBy = "contract", fetch = FetchType.LAZY)
private Collection<UserContract> userContract;
And my UserContract.java:
#Entity
public class UserContract {
#EmbeddedId
private UserContractPK userContractPK;
#ManyToOne(optional = false)
private User user;
#ManyToOne(optional = false)
private Contract contract;
And my UserContractPK:
#Embeddable
public class UserContractPK implements Serializable {
#Column(nullable = false)
private long idContract;
#Column(nullable = false)
private String email;
Is this the best way to achieve my goals?
Everything looks right. My advice is to use #MappedSuperclass on top of #EmbeddedId:
#MappedSuperclass
public abstract class ModelBaseRelationship implements Serializable {
#Embeddable
public static class Id implements Serializable {
public Long entityId1;
public Long entityId2;
#Column(name = "ENTITY1_ID")
public Long getEntityId1() {
return entityId1;
}
#Column(name = "ENTITY2_ID")
public Long getEntityId2() {
return entityId2;
}
public Id() {
}
public Id(Long entityId1, Long entityId2) {
this.entityId1 = entityId1;
this.entityId2 = entityId2;
}
}
protected Id id = new Id();
#EmbeddedId
public Id getId() {
return id;
}
protected void setId(Id theId) {
id = theId;
}
}
I omitted obvious constructors/setters for readability. Then you can define UserContract as
#Entity
#AttributeOverrides( {
#AttributeOverride(name = "entityId1", column = #Column(name = "user_id")),
#AttributeOverride(name = "entityId2", column = #Column(name = "contract_id"))
})
public class UserContract extends ModelBaseRelationship {
That way you can share primary key implementation for other many-to-many join entities like UserContract.