DataIntegrityViolationException: same identifier using CRUDRepository - java

I am currently developing an application with Spring (Boot) and JPA. I have the following two entities:
#Entity
#AccessType(AccessType.Type.PROPERTY)
#Table(name = "T_ORDER")
public class Order {
#Id
private String id;
#Version
private Integer version = 0;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "order", cascade = CascadeType.ALL)
private List<Item> items = new ArrayList<>();
private String text;
private String status;
#Deprecated
public Order() {}
public static Order newOrder() {
Order order = new Order();
order.id = UUID.randomUUID().toString();
return order;
}
[... getters and setters ...]
[... equals and hashCode ...]
}
and
#Entity
#Table(name = "T_ITEM")
#IdClass(Item.ItemId.class)
public class Item {
public static class ItemId implements Serializable {
public String order;
public String id;
[... equals and hashcode ...]
}
#Id
#JsonIgnore
#ManyToOne(cascade = CascadeType.PERSIST)
private Order order;
#Id
private String id;
#Version
private int version = 0;
#Transient
private Product product;
private Integer productId;
private BigDecimal quantity;
private String quantityUnit;
private BigDecimal price;
[... getters and setters ...]
[... equals and hashcode ...]
}
I am using a CRUDRepository
#Repository
public interface OrderRepository extends CrudRepository<Order, String> {
#Transactional
List<Order> removeByStatus(String status);
}
to insert data on application startup
#Component
public class OrderLoader implements ApplicationListener<ContextRefreshedEvent> {
private final Logger logger = LoggerFactory.getLogger(getClass());
private OrderRepository orderRepository;
#Autowired
public void setOrderRepository(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
#Override
#Transactional(readOnly = false)
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
try {
Order order = Order.newOrder();
for (int itemId = 1; itemId < 3; itemId++) {
Item item = order.addItem();
item.setProductId(1);
}
order.setText("this is an order");
order.setStatus("delivered");
orderRepository.save(order);
} catch (Exception e) {
logger.error("This shit...", e);
}
}
}
Using the H2 console, I can see that the tables are being generated and empty. However, I get a DataIntegrityViolationException when calling orderRepository.save(order).
This seems very strange to me as :
The tables are empty,
I am using UUIDs as IDs and
save should merge two entities with the same key.
The complete stacktrace: http://pastebin.com/gYk2ZvP4

Try to use orderRepository.saveAndFlush(order); instead of orderRepository.save(order);, with saveAndFlush changes will be flushed to DB immediately,while using just save changes won't apply to database until flush or commit are issued.

Related

Jpa Repository in Spring boot app findBy issue

I'm trying to create findBy JpaRepo it's about returning only the data where isDeleted attribute is false.
this is my Service :
public List<Customer> getAllCustomers() {
List<Customer> customers = cutomerRepository.findByIsDeletedFalse();
return customers;
}
and this is my Controller :
#GetMapping("/viewList")
#CrossOrigin("http://localhost:4200/")
public ResponseEntity<List<Customer>> getAllCustomers() {
List<Customer> customers = new ArrayList<>();
customers = customerService.getAllCustomers();
if (customers.isEmpty()) {
LOGGER.error("no content ");
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
LOGGER.info("calling list of customers");
return new ResponseEntity<>(customers, HttpStatus.OK);
}
and this is customer model :
public class Customer {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
#Column(name = "serial_number")
private long serialNumber;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#Column(name = "mobile_number")
private String mobileNumber;
#Column(name = "is_deleted")
private boolean isDeleted;
}
but when I run it in postman it's not working and return an error :
Caused by: org.postgresql.util.PSQLException: ERROR: operator does not
exist: boolean = integer Hint: No operator matches the given name
and argument types. You might need to add explicit type casts.
Position: 315
How could I solve this issue?
Looks like the name for your query isn't created right.
However, in this case, the usage of #Query will be much clearer.
Code snippet:
public interface CustomerRepo extends JpaRepository<Customer, Integer> {
List<Customer> findAllByIsDeletedIsFalse();
#Query("from Customer c where c.isDeleted=false")
List<Customer> getAllCustomers();
}
Iinstead of:
cutomerRepository.findByIsDeletedFalse()
You missed one more Is at the name of the method.
Update your Domain:
public class Customer implements Serializable {
private final static long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Integer id;
#Column(name = "serial_number")
private Long serialNumber;
// ...
#Column(name = "is_deleted")
private Boolean isDeleted;
}
JPA fields should be Objects instead of primitives. And entity class should implement Serializable as well.
If the exception will be the same you could try to update #Query:
#Query("from Customer c where c.isDeleted=0")
If pure SQL works for your DB you could use native query:
#Query(
value = "select * from Customer where is_deleted = false",
nativeQuery = true)
List<Customer> getAllCustomers();
It's not working because it doesn't follow the naming conventions for a boolean field. Usually in Java the primitive booleans are named without is prefix and the getter would be using this is prefix.
So in your case your entity class should look like that:
public class Customer {
// ...
#Column(name = "is_deleted")
private boolean deleted;
public boolean isDeleted() {
return deleted;
}
public void setDeleted(boolean deleted) {
this.deleted = deleted;
}
}
Also the naming of the spring repository method should be:
List<Customer> findAllByDeletedIsFalse();
In case you want to use a Boolean reference type you can name your field isDeleted, but then the class would look like that:
public class Customer {
// ...
#Column(name = "is_deleted")
private Boolean isDeleted;
public Boolean getIsDeleted() {
return isDeleted;
}
public void setIsDeleted(Boolean isDeleted) {
this.isDeleted = isDeleted;
}
}
and the repository method:
List<Customer> findAllByIsDeletedIsFalse();
Boolean Java maps a bit datatype column. You are probably using int as datatype in your database.

how to find the data by an attribute (name_techno) in jpql

I am developing an application that allows managing candidates in a company, for that I use spring-boot, in order to select the employees who master such a technology (Techno) I used a request JPQL.
So, How can I find a candidate by techno?
In my project I used this code:
1 - the class candidat.java
#Entity
public class Candidat {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name = "candidat_id")
private int id;
private String nom;
private String prenom;
private String ville;
private int numTel;
private String mail;
private String pseudo;
private String roleCible;
private String typeContrat;
private String villeRecherchee;
#OneToMany(mappedBy="candidat")
private List<Techno> techno;
#Temporal(TemporalType.DATE)
private Date date;
#OneToMany
private List<SecteurActivites> secteurActivites;
public Candidat() {
// TODO Auto-generated constructor stub
}
2- the class Techno.java
#Entity
public class Techno {
#Id
#GeneratedValue
#Column(name = "techno_id")
private int id ;
private String nomTechno;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "candidat_id", nullable = false)
#OnDelete(action = OnDeleteAction.CASCADE)
#JsonIgnore
private Candidat candidat;
public Techno() {
// TODO Auto-generated constructor stub
}
/**
* #param nomTechno
* #param candidat
*/
public Techno(String nomTechno, Candidat candidat) {
super();
this.nomTechno = nomTechno;
this.candidat = candidat;
}
3- My CandidatController
#GetMapping(value = "/GetAllCandidats/{nomTechno}")
public List<Candidat> afficherCandidat(#PathVariable ("nomTechno") String nomTechno){
return candidatdao.findByTechno(nomTechno);
}
4- the repository:
#Repository
public interface CandidatDao extends JpaRepository <Candidat, String>{
List<Candidat> findByDate(Date date);
#Query("SELECT DISTINCT e FROM Candidat e INNER JOIN e.Techno t")
List<Candidat> findByTechno(String nomTechno);
}
5- app.properties
server.port= 9090
spring.jpa.show-sql = true
spring.datasource.url= jdbc:mysql://localhost:3306/database
spring.datasource.username=??
spring.datasource.password=??
spring.jpa.hibernate.ddl-auto=update
The result in console is:
"Validation failed for query for method public abstract java.util.List com.avatar.dao.CandidatDao.findByTechno(java.lang.String)!"
You can declare the following method into your JpaRepository (also remove the #Query, it is not needed).
List<Candidat> findDistinctByTechnoNomTechno(String nomTechno);
Also in Techno.java you should add the #Column annotation and map it with the DB schema.
I am not sure if you have pasted incomplete code of your entities on purpose. If not your entities are not correct. You should create setters/getters as the following
private String nomTechno;
#Column(name = "NOM_TECHNO")
public String getNomTechno() {
return nomTechno;
}
public void setNomTechno(String nomTechno){
this.nomTechno = nomTechno;
}
Do the above for all variables in your entities.
You do not need to add explicit #Query for this, Spring data can formulate a query if you have right method names
Instead of
#Query("SELECT DISTINCT e FROM Candidat e INNER JOIN e.Techno t")
List<Candidat> findByTechno(String nomTechno);
Try this
List<Candidat> findDistinctByTechno_NomTechno(String nomTechno);

Google AppEngine Key exception

I have two classes, CarRentalCompany
#Entity
public class CarRentalCompany {
private static Logger logger = Logger.getLogger(CarRentalCompany.class.getName());
#Id
private String name;
#OneToMany(cascade = CascadeType.ALL)
private List<Car> cars;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private List<CarType> carTypes = new ArrayList<>(); //TODO: kan betere datastructuur zijn
/***************
* CONSTRUCTOR *
***************/
public CarRentalCompany()
{
// DEFAULT CONSTRUCTOR
}
public CarRentalCompany(String name, List<Car> cars) {
logger.log(Level.INFO, "<{0}> Car Rental Company {0} starting up...", name);
setName(name);
this.cars = cars;
for(Car car:cars)
carTypes.add(car.getType());
}
...
}
and CarType
#Entity
public class CarType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Key key;
private String name;
private int nbOfSeats;
private boolean smokingAllowed;
private double rentalPricePerDay;
//trunk space in liters
private float trunkSpace;
/***************
* CONSTRUCTOR *
***************/
public CarType()
{
}
public CarType(String name, int nbOfSeats, float trunkSpace, double rentalPricePerDay, boolean smokingAllowed) {
this.name = name;
this.nbOfSeats = nbOfSeats;
this.trunkSpace = trunkSpace;
this.rentalPricePerDay = rentalPricePerDay;
this.smokingAllowed = smokingAllowed;
}
When running the application, it throws the following exception:
NucleusFatalUserException: Attempt to assign child with key
"CarType(6614661952700416)" to parent with key
"CarRentalCompany("Hertz")". Parent keys are immutable
What do I need to do to get the key of the CarType right?
You are not persisting your CarRentalCompany entity before adding CarType entity. See a
similar thread here. The persist operation must be used only for new entities. An entity is new when it has never been associated with a database row, meaning that there is no table record in the database to match the entity in question.

hibernate update one to many items set

Sales Order Entity.
#Entity
#Table(name = "sales_orders")
#IdClass(ReceiptPK.class)
public class SalesOrders implements Serializable {
public SalesOrders() {
}
#Id
protected Integer receiptID;
#Id
protected Integer dateKey;
public SalesOrders(Integer receiptID, Integer dateKey) {
this.receiptID = receiptID;
this.dateKey = dateKey;
}
//order contains many details
#OneToMany(fetch = FetchType.LAZY, mappedBy = "salesOrders")
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
private Set<SalesOrderDetails> orderDetails = new HashSet<SalesOrderDetails>();
public Set<SalesOrderDetails> getOrderDetails() {
return orderDetails;
}
public void setOrderDetails(Set<SalesOrderDetails> orderDetails) {
this.orderDetails = orderDetails;
}
// other property ..
Order Details Entity.
#Entity
#Table(name = "sales_order_details")
public class SalesOrderDetails implements Serializable {
public SalesOrderDetails() {
}
private int id;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
// Order holder
private SalesOrders salesOrders;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "order_num", referencedColumnName = "receiptID"),
#JoinColumn(name = "date_key", referencedColumnName = "dateKey")})
public SalesOrders getSalesOrders() {
return salesOrders;
}
public void setSalesOrders(SalesOrders salesOrders) {
this.salesOrders = salesOrders;
}
// other property ...
My Question : When i try to remove Order item that doesn't affect on sales_order_details .
SalesOrders saleOrder = (SalesOrders) getSession().get(SalesOrders.class ,new ReceiptPK(receiptID,dateKey));
saleOrder.getOrderDetails().remove(someDetails);
getSession().beginTransaction();
getSession().saveOrUpdate(saleOrder);
getSession().getTransaction().commit();
But someDetails doesn't removed.
-- Any help will be appreciated ...
I think that you have to commit the same transaction.
getSession().getTransaction().begin();
getSession().saveOrUpdate(saleOrder);
getSession().getTransaction().commit();
For bi-directional associations you should(must) always ensure that the associations are set correctly.
Do not allow direct access to your collections.
Provide add and remove methods for modification.
public Set<SalesOrderDetails> getOrderDetails() {
//force to use add/remove to ensure consistent object model
return Collections.unmodifiableSet(orderDetails);
}
public void addOrderDetails(SalesOrderDetails salesOrderDetails){
orderDetails.add(salesOrderDetails);
salesOrderDetails.setSalesOrders(this); //important
}
public void removeOrderDetails(SalesOrderDetails salesOrderDetails){
orderDetails.remove(salesOrderDetails);
salesOrderDetails.setSalesOrder(null); //important
}
Additionally, have you implemented equals() and hashCode() on your Entitites i.e. when you call salesOrder.getOrderDetails().remove(someDetails) is anything actually being removed from the collection?
Probably not if you have not implemented equals() and hashCode() on SalesOrderDetails.
Fianlly, you shout set the orphanRemoval flag on the OneToMany mapping to true:
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Orphan_Removal_.28JPA_2.0.29

How do I setup annotations for JOINED inheritance with composite PK in hibernate?

I am new to hibernate and having a tough time trying to wrap my head around setting up Joined inheritance with composite Primary Key. With my current setup, I get a:
JDBCException: could not insert: LandHolidayPackage
I am essentially looking for two things:
Are the inheritance annotations in place ?
Is the composite PK setup properly ?
DB Design:
Reference
Here are my classes and the annotations involved:
#Entity
#Table(name = "HOLIDAYPACKAGE")
public final class HolidayPackage {
private Integer idPackage;
private String name;
private Set<HolidayPackageVariant> holidayPackageVariants = new HashSet<HolidayPackageVariant>(0);
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "IDHOLIDAYPACKAGE", nullable = false)
public Integer getIdPackage() {
return idPackage;
}
#OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.ALL}, mappedBy = "holidayPackage")
public Set<HolidayPackageVariant> getHolidayPackageVariants() {
return holidayPackageVariants;
}
// ommitted other part of the code
}
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
#Table(name="HOLIDAYPACKAGEVARIANT")
public abstract class HolidayPackageVariant {
private Integer idHolidayPackageVariant;
private HolidayPackage holidayPackage;
private String typeHolidayPackage;
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="IDHOLIDAYPACKAGEVARIANT", nullable=false)
public Integer getIdHolidayPackageVariant() {
return idHolidayPackageVariant;
}
#ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
#JoinColumn(name="IDHOLIDAYPACKAGE", nullable=false)
public HolidayPackage getHolidayPackage() {
return holidayPackage;
}
#Column(name="TYPEHOLIDAYPACKAGE", nullable=true)
public String getTypeHolidayPackage() {
return typeHolidayPackage;
}
// ommitted setters, equals hashCode
}
#Entity
#Table(name="LANDHOLIDAYPACKAGEVARIANT")
public final class LandHolidayPackageVariant extends HolidayPackageVariant{
private static final String LAND = "LAND";
protected LandHolidayPackageVariant() {}
public LandHolidayPackageVariant(HolidayPackage holidayPackage) {
super(holidayPackage, LAND);
}
}
#Entity
#Table(name="FLIGHTHOLIDAYPACKAGEVARIANT")
public final class FlightHolidayPackageVariant extends HolidayPackageVariant{
private static final String FLIGHT = "FLIGHT";
private Destination originCity;
protected FlightHolidayPackageVariant(){}
public FlightHolidayPackageVariant(HolidayPackage holidayPackage,
Destination originCity) {
super(holidayPackage, FLIGHT);
setOriginCity(originCity);
}
#ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
#JoinColumn(name="IDDESTINATION", nullable=false)
public Destination getOriginCity() {
return originCity;
}
// ommited other setters etc functions
}
You annotated the properties in stead of the fields. JPA by default tries to access the fields. If you want JPA to use the fields you have to annotate the class with #AccessType(AccessType.Field).

Categories

Resources