Spring data JPA only one composite key auto incremented - mysql

I am using MySQL database.
My table is an employee in which there are two primary keys, out of which one is auto incremented.
My code is:
#Embeddable
public class EmployeeId implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
#Column(name = "id", nullable = false)// this i want to increment
private int id;
// I have tried and #GeneratedValue(strategy = GenerationType.IDENTITY),
//#GeneratedValue(strategy = GenerationType.IDENTITY)
//and #GeneratedValue(strategy = GenerationType.TABLE)
//#GeneratedValue(strategy = GenerationType.AUTO, generator = "id") #SequenceGenerator(name = "id", sequenceName = "id")
#Column(name = "gender_key", nullable = false)
private id gender_key;
}
#Entity
#Table(name = "employee")
public class employee {
#EmbeddedId
private EmployeeId employeeId;
private String emp_name;
private String mobile_no;
employee() {
}}
public interface employeeRepository extends
JpaRepository<employee, EmployeeId> {
}
In My Controller I want id after employeeRepository.save(bean); method because i want to save that id in different table .
logger.info("id is --- > "+id);
But I am getting always 0 value of id.
How can I get the incremented value of id which is inserted into MySQL table?
Please Help.
Thanks in advance.

Are you trying to get the value from your original object or from the object return from the save method? Let's assume you have the following code:
employeeRepository.save(bean)
int id = bean.getId();
logger.info("id is --- > "+id);
This will indeed return zero, since your object didn't have this information. If you want to get the value that was generated on the database you have to do the following:
bean = employeeRepository.save(bean)
int id = bean.getId();
logger.info("id is --- > "+id);

Related

Spring boot: How to Save data to specific DTYPE

I have this entity:
public class StatementLinesEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long statementLinesId;
#CreationTimestamp
#Temporal(TemporalType.DATE)
private Date dateOperation;
private String operationNature;
private BigDecimal amount;
private String debitAmount;
And this entity has Inheritance of type SINGLE_TABLE:
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class OperationCreditEntity {
#Id
#Column(nullable = false, updatable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Long operationCreditId;
#CreatedDate
private Date operationDate;
#OneToOne
private StatementLinesEntity statementLine;
And these 3 enteties inherite of it :
#Entity
#DiscriminatorValue("Espece")
public class OperationEspecesEntity extends OperationCreditEntity {
private String cin;
private String nomEmetteur;
private String prenomEmetteur;
=============================
#DiscriminatorValue("Virement")
public class OperationVirementEntity extends OperationCreditEntity {
private String rib;
===========================
#Entity
#DiscriminatorValue("Cheque")
public class OperationChequeEntity extends OperationCreditEntity{
private int numeroCheque;
Let's suppose I have a List<StatementLinesEntity> consist of 2 lines, on line has debitAmount = C and operationNature = Virement and second line has debitAmount = C and operationNature = Espece. My goal is to persist each line in a specific DTYPE. example
first line should be persisted in OperationCreditEntity table DTYPE = Virement and the second should be persisted in OperationCreditEntity table DTYPE = Espece
The model to me should be more like:
#Entity
public class StatementLinesEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long statementLinesId;
#CreationTimestamp
#Temporal(TemporalType.DATE)
private Date dateOperation;
#OneToOne(mappedBy = "statementLine")
private OperationCreditEntity operation;
private BigDecimal amount;
private String debitAmount;
}
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
abstract public class OperationCreditEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long operationCreditId;
#CreatedDate
private Date operationDate;
#OneToOne
private StatementLinesEntity statementLine;
}
Any method then that takes in StatementLinesEntity instances can then take in one that references an OperationCreditEntity instance (which can be any one of its subclasses). There is no need to manage, parse or handle String operationNature strings directly, as the operation type will determine the operation nature.
This might change other signatures, serialization (such as JSON though), so if you can't use this and are 'stuck' with your existing StatementLinesEntity data representation YOU need to handle how to create your OperationCreditEntity instances from that data. There is no tool to automatically do it for you. It is as simple as a utility of the form:
OperationCreditEntity createOperationInstance(StatementLinesEntity statementLine) {
String operationNature = statementLine.getOperationNature();
OperationCreditEntity returnVal = null;
if "Espece".equals(operationNature) {
returnVal = new OperationEspecesEntity();
} else if "Virement".equals(operationNature) {
returnVal = new OperationVirementEntity();
} else if "Cheque".equals(operationNature) {
returnVal = new OperationChequeEntity();
} else {
throw new IllegalStateException();
}
returnVal.setStatementLine(statementLine);
return returnVal;
}
Just call save using your OperationCreditEntity repository when ever you call this method to get it put into the same transactional context you are making changes to. Also note, those OperationCreditEntity subclasses have data you will need to find a way to fill in on your own; I personally think this data will likely be tied to data available when defining/creating a StatementLinesEntity, so should be generated/created then, not after the fact, but that is up to you.
Added just to be complete:
Yes, you can access the column used to store discriminator values directly in a base entity class. Nothing stops or prevents you from mapping the column as you would any other database column. For Hibernate, it uses "DTYPE", so
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class OperationCreditEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long operationCreditId;
#CreatedDate
private Date operationDate;
#Column(name="DTYPE",insertable=false, updatable=false)
private String typeValue;
}
Notice I marked this as insertable/updatable=false though. It is provider specific if it complains about controlling this value in this way; many try to do so with the hope of changing it. Changing an entity 'type' is not supported. A Caterpillar does not become a Butterfly just by changing a string value. Any caches that hold OperationCreditEntity or some specific subclass type aren't magically going to have the object type changed; JPA requires you to delete the entity and create a new instance (of the proper class) for that data, preferably after flushing the delete operation.
Also note, you can query and use Entity Type Expressions (TYPE) without having a column or other mapping for it.
"Select line from OperationCreditEntity operation join operation.statementLine line where TYPE(operation) IN (OperationEspecesEntity, OperationChequeEntity) and line.somethingElse = :someValue"

Hibernate JPA Repeated column in mapping for entity error for bi-directional mapping

I have two entities, sale and sale_details. One sale will have many sale_details, but each sale_detail belongs to only one sale, my current code gives me a Repeated column in mapping for entity error.
Sale:
#Entity
#Table(name="Sales")
public class Sale implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_id", nullable = false)
private int sale_id;
#Column(name = "promotionid_fk")
private int promotionid_fk;
#Column(name = "grand_total", nullable = false)
private double grand_total;
#Column(name = "salespersonid_fk", nullable = false)
private int salespersonid_fk;
#Column(name = "customerid_fk", nullable = false)
private int customerid_fk;
#Column(name = "storeid_fk", nullable = false)
private int storeid_fk;
#Column(name = "expected_date", nullable = false)
private Date expected_date;
#Column(name = "pickup_date")
private Date pickup_date;
#Column(name = "initial_deposit_date", nullable = false)
private LocalDateTime initial_deposit_date;
#Column(name = "initial_deposit_type", nullable = false)
private String initial_deposit_type;
#Column(name = "initial_deposit_amount", nullable = false)
private double initial_deposit_amount;
#Column(name = "final_payment_date")
private LocalDateTime final_payment_date;
#Column(name = "final_payment_type")
private String final_payment_type;
#Column(name = "final_payment_amount")
private double final_payment_amount;
//maps one sale to many sale details relationship
#OneToMany(mappedBy = "sale", fetch = FetchType.LAZY)
private List<SaleDetails> sale_detail_list; //stores list of sale_detail entries where FK saleid_fk field is the ID of this sale
//default constructor, never used
public Sale() {
}
//creates new sale
public Sale(int promotionid_fk, double grand_total, int salespersonid_fk, int customerid_fk, int storeid_fk, Date expected_date, LocalDateTime initial_payment_date, String initial_payment_type, double initial_payment_amount) {
this.promotionid_fk = promotionid_fk;
this.grand_total = grand_total;
this.salespersonid_fk = salespersonid_fk;
this.customerid_fk = customerid_fk;
this.storeid_fk = storeid_fk;
this.expected_date = expected_date;
this.initial_deposit_date = initial_payment_date;
this.initial_deposit_type = initial_payment_type;
this.initial_deposit_amount = initial_payment_amount;
}
Sale_details:
#Entity
#Table(name = "sale_Details")
public class SaleDetails implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_detail_id")
private int saleDetailId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "saleid_fk")
private Sale sale;
#Column(name = "saleid_fk")
private int saleid_fk;
#Column(name = "productid_fk")
private int productid_fk;
#Column(name = "quantity_sold")
private int quantity_sold;
public SaleDetails(){
}
public SaleDetails(int saleid_fk, int productid_fk, int quantity_sold){
this.saleid_fk = saleid_fk;
this.productid_fk = productid_fk;
this.quantity_sold = quantity_sold;
}
Table structures:
Im trying to make the relationship bi-directional, what am I doing wrong here? From my understanding the owning side of the relationship is the many-to-one on the sale_details entity, and the mappedby in the sale entity is just referencing that there already is a mapping on the inverse side?
Full error stack trace:
Unable to build Hibernate SessionFactory
org.hibernate.MappingException: Repeated column in mapping for entity: com.owl.server.entities.Sale_Details column: saleid_fk (should be mapped with insert="false" update="false")
I would suggest you to follow java naming conventions. According to this roles:
Classes: Class names should be nouns, in mixed case with the first letter of each internal word capitalized.
Variables: Except for variables, all instance, class, and class constants are in mixed case with a lowercase first letter. Internal words start with capital letters.
So, I would suggest you to correct your mapping in the following way:
#Entity
#Table(name="Sales")
public class Sale implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_id", nullable = false)
private int saleId;
#Column(name = "promotionid_fk")
private int promotionId;
// ...
//maps one sale to many sale details relationship
#OneToMany(mappedBy = "sale", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<SaleDetails> saleDetails;
// default constructor, should be present
// It is used by hibernate for entity instantiation
public Sale() {
saleDetails = new ArrayList<>();
}
// getters, setters
// The addSaleDetail() and removeSaleDetail() are utility methods that
// synchronize both ends whenever a child element is added or removed.
public void addSaleDetail(SaleDetails saleDetail)
{
saleDetails.add(saleDetail);
saleDetail.setSale(this);
}
public void removeSaleDetail(SaleDetails saleDetail)
{
saleDetails.remove(saleDetail);
saleDetail.setSale(null);
}
}
#Entity
#Table(name = "Sale_Details")
public class SaleDetails implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_detail_id")
private int saleDetailId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "saleid_fk")
private Sale sale;
// This should be removed
// #Column(name = "saleid_fk")
// private int saleid_fk;
#Column(name = "productid_fk")
private int productId;
// ...
public SaleDetails(){
}
// getters, setters
}
The value in the mappedBy referred to the field name of another side of the association.
You can omit to use the referencedColumnName if the foreign key referred to the primary key field of target entity.
And an example of creation and saving a new Sale:
Sale sale = new Sale();
sale.setPromotionId(1);
// ...
SaleDetails saleDetail1 = new SaleDetails();
saleDetail1.setProductId(2);
// set other fields except sale
sale.addSaleDetail(saleDetail1);
SaleDetails saleDetail2 = new SaleDetails();
saleDetail2.setProductId(3);
// set other fields except sale
sale.addSaleDetail(saleDetail2);
entityManager.persist(sale);

How to GET data in the JSON format form the DB Repository

I have this JPA Class, where I have 3 columns id, name and date. The Database is already filled with data, where each entry has an id.
#Data
#Entity
#Table(name = "TEST", schema = "TESTSCHEMA")
public class TestDataJpaRecord implements Serializable {
private static final long serialVersionUID = 1L;
TestDataJpaRecord(){
// default constructor
}
public TestDataJpaRecord(
String name,
Date date,
){
this.name = name;
this.date = date;
}
#Id
#Column(name = "ID", nullable = false)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "TEST_SEQUENCE")
#SequenceGenerator(
sequenceName = "TEST_SEQUENCE", allocationSize = 1,
name = "TEST_SEQUENCEx")
private Long id;
#Column(name = "NAME")
private String name;
#Temporal(TemporalType.TIMESTAMP)
#Column(name = "DATE")
private Date date;
}
I created a JPA repository for all the data.
public interface TestDataJpaRecordRepository extends JpaRepository<TestDataJpaRecord, Long> {
}
I want to get the data from the DB in a JSON format.
Here is my Rest GET Api. Here I return the data as a string just, but I want to return them as JSON.
#GetMapping(value = "data/{id}")
private ResponseEntity<?> getDataFromTheDB(#PathVariable("id") Long id) {
// get one entry form the DB
TestDataJpaRecord testDataJpaRecord =testDataJpaRecordRepository.findOne(id);
// Here I want to return a JSON instead of a String
return new ResponseEntity<>(testDataJpaRecord.toString(), HttpStatus.OK);
}
Any idea on how I could return the data as JSON and not as a string from the DB?
I would very very much appreciate any suggestion.
If you have Jackson on the classpath which you should if you have used the spring-boot-starter-web then simply:
#GetMapping(value = "data/{id}")
private ResponseEntity<TestDataJpaRecord> getDataFromTheDB(#PathVariable("id") Long id) {
TestDataJpaRecord testDataJpaRecord =testDataJpaRecordRepository.findOne(id);
return new ResponseEntity.ok(testDataJpaRecord);
}
This assumes you have annoted your controller with #RestController rather than #Controller. If not then you can either do that or, annotate your controller method with #ResponseBody.
With Spring Data's web support enabled (which it should be by default with Spring Boot) then you can also simplify as below:
#GetMapping(value = "data/{id}")
private ResponseEntity<TestDataJpaRecord>
getDataFromTheDB(#PathVariable("id") TestDataJpaRecord record) {
return new ResponseEntity.ok(record);
}
See:
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web.basic.domain-class-converter

JPA mapping for one-to-many collection of shared data, with user specific values

I have a User model that contains a list of achievements
#Table(name = "user")
#Entity
#NamedEntityGraph(name = "User.achievements",
attributeNodes={
#NamedAttributeNode("achievements")
})
#Data
public class User {
#Id
#NotNull
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#ElementCollection(fetch = FetchType.LAZY, targetClass = Achievement.class)
private List<Achievement> achievements = new ArrayList<>();
}
Here's the achievement model
#Entity
#Data
#Table(name = "achievement")
public class Achievement {
#Id
#GeneratedValue(generator = "system-uuid")
#GenericGenerator(name = "system-uuid", strategy = "uuid")
private String achievementId;
#Column(name = "title")
private String title;
#Column(name = "description")
private String description;
#Column(name = "achieved", columnDefinition="BOOLEAN DEFAULT false", nullable = false)
private boolean achieved = false;
user_achievements table generated from #ElementCollection mapping, which atm only contains user and achievement foreign keys
I am looking to move the boolean achieved value to the user_achievements table, ideally without having to create a separate model User_Achievements
I am fairly new to using Jpa, but i feel like this scenario is too basic so there must be a straight forward way to do that i cant seem to locate it
#Entity
class UserAchievement {
#EmbeddableId
UserAchievementId id;
#ManyToOne(fetch=LAZY)
#JoinColumn(name="user_username", insertable=false, updatable=false)
User user;
#ManyToOne(fetch=LAZY)
#JoinColumn(name="achivement_achivement_id", insertable=false, updatable=false)
Achivement achivement;
// and other fields
}
class User {
// ...
#OneToMany(mappedBy="user")
List<UserAchievement> userAchievements;
}
and you need to define UserAchievementId

How to read specific fields from mapped object in Hibernate

I've got two entity objects in my database: UserEntity and ItemEntity and they're mapped with OneToMany relationship.
Here is my code:
UserEntity:
#Entity
#Table(name = "users")
public class UserEntity {
#Id
#Column(name = "user_id")
#GeneratedValue
public int user_id;
#Column(name = "userlogin")
public String userlogin;
#Column(name = "userpass")
public String userpass;
#Column(name = "name")
public String name;
#Column(name = "email")
public String email;
....
#JsonBackReference
#OneToMany(mappedBy="user", cascade = { CascadeType.MERGE },fetch=FetchType.EAGER)
private List<ItemEntity> items;
ItemEntity:
#Entity
#Table(name = "items")
public class ItemEntity {
#Id
#Column(name = "id")
#GeneratedValue
private int id;
#Column(name = "title")
public String title;
#Column(name = "info")
public String info;
#JsonManagedReference
#ManyToOne
#JoinColumn(name="user_id")
private UserEntity user;
And now I'm trying to read all my Items from my database with specific fields from users that owns current item. I need only UserEntity name and email.
This code:
Query query = this.sessionFactory.getCurrentSession().createQuery("from ItemEntity WHERE title = :title");
returns all fields from UserEntity also, because it's mapped, but I don't want that, because I'm sending that data as JSON, and someone can see all informations about user who own that item (like user login and password) in some dev tools like Chrome.
How to reach that?
I'd suggest you use DTO.
Covert your entities to DTO and then transform the DTO objects to
json string.
In the DTO populate only those field that you want as part of your response.
This would make your design more clean.
In addition to what's jitsonfire is suggesting, you can write a query like this
select name, email from ItemEntity WHERE title = :title
than get your results like
List<Object[]> result = query.list();
The object array will contain your columns, the list element will equal to rows, so you can do something like
for (Object[] tuple : result) {
tuple[0]; //name
tuple[1]; // email
}