I'm working with spring-boot-starter-data-jpa. Should I use annotation #GeneratedValue on my entity id if my code working without it and generate PRIMARY KEY automatically in mysqldb?
When I run the test in the sqltable appears new row with an ID with the following AUTO_INCREMENT value, while passed every time id 0.
Entity
#Data
#Entity
#RequiredArgsConstructor
#NoArgsConstructor(access = AccessLevel.PRIVATE, force = true)
public class Person {
#Id
// #GeneratedValue(strategy = GenerationType.IDENTITY)// use or not - the same effect
private int id;
#NonNull
private String name;
#NonNull
private String surname;
}
Repository
public interface PersonRepository extends CrudRepository<Person, Integer> {
Person findByNameAndSurname(String name, String surname);
}
Testing
#RunWith(SpringRunner.class)
#SpringBootTest
public class SpringRestInventoryPersistenceTests {
#Autowired
private PersonRepository personRepository;
#Test
public void personPersist() {
Person person = new Person("John", "Smith");
assertTrue(person.getId() == 0);
personRepository.save(person);
assertTrue(person.getId() == 0);
Person person2 = personRepository.findByNameAndSurname("John", "Smith");
assertEquals(person.getName(), person2.getName());
}//test passed
mySql table
CREATE TABLE `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`surname` varchar(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8
I was also facing the same issue , I have added sequence generator in DB say 'idGenerator' ,and then add
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="idGenerator")
#SequenceGenerator(name='dbname' , sequenceName="idGenerator") ,
This will take the values from sequence generator created in DB
I`ve understood the reason for this behavior. Without the #GeneratedValue annotation, ID is not generated automatically and ID with value 0 is always passed to the mysql database. In this case mysql generate ID value from AUTO_INCREMENT. This is default behavior.
To disable this behavior you can set next parameter:
SET SQL_MODE='NO_AUTO_VALUE_ON_ZERO'
Then after second call test method personPersist() we get error. In this case we can`t generate ID in mysql DB without #GeneratedValue annotation.
Related
I'm using spring-boot 2.3.3 and spring-data-jdbc 2.0.3 to model a relationship between to elements guitar and classType. A guitar has a classType.
My schema in H2 (also in MySQL) is this:
CREATE TABLE class_type (
id bigint NOT NULL AUTO_INCREMENT,
description varchar(50) NOT NULL
PRIMARY KEY (id)
);
CREATE TABLE guitars (
id bigint NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
description varchar(1000) NOT NULL,
classType bigint NOT NULL,
PRIMARY KEY (id),
UNIQUE (name),
FOREIGN KEY (classType) references class_type(id)
);
And I have these classes:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Table(value = "guitars")
public class Guitar {
#Id
long id;
String name;
String description;
#MappedCollection(idColumn = "id")
#Column(value = "classType")
ClassType classType;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder
#Table(value = "class_type")
public class ClassType {
#Id
Long id;
String description;
}
My repository is like this:
#Repository
public interface GuitarRepository extends CrudRepository<Guitar, Long>, PagingAndSortingRepository<Guitar, Long> {}
When I invoke this test:
#SpringBootTest
public class GuitarOneManyTest {
#Autowired
GuitarRepository guitarRepository;
#Test
void findAllByName() {
System.out.println(guitarRepository.findAll());
}
}
This sentence appears with an incorrect LEFT OUTER JOIN
SELECT `guitars`.`id` AS `id`, `guitars`.`name` AS `name`, `guitars`.`description` AS `description`, `classType`.`id` AS `classType_id`, `classType`.`description` AS `classType_description` FROM `guitars` LEFT OUTER JOIN `class_type` AS `classType` ON `classType`.`id` = `guitars`.`id`
But I want the select to be like this:
SELECT (...) FROM `guitars` LEFT OUTER JOIN `class_type` AS `classType` ON `classType`.`id` = `guitars`.`classType`
Did I miss something?
This is not a one-to-many relationship. One Guitar would reference multiple ClassType instances.
Instead it seems to be intended as a many-to-one relationship: Many Guitar instances might reference the same ClassType. This makes ClassType a different aggregate from Guitar and therefore it must not be referenced by a direct java reference, but only by it's id.
See Spring Data JDBC, References, and Aggregates for a more detailed explanation how to model such a relationship with Spring Data JDBC.
I am working with Spring MVC + Hibernate ( Spring data jpa ).
Here I am not getting how to create a table in Hibernate for the following scenario.
I have two tables :
1) User Details
2) Branch
Now, For every user, there is a field for branch, and that value should be from Branch table.
I have knowledge of OneToOne in hibernate. But it inserts a new entry for users branch field in Branch table. What I want is that, when I save user details in User Table, branch details should be just a reference from Branch table for matching row.
Thank you in advance
Suppose your branches can be identified by their names:
UserDetails user = new UserDetails();
...
user.setBranch(branchRepository.findOneByName());
...
userDetailsRepository.save(user);
Having:
#Entity
public class UserDetails {
#Id
#GeneratedValue
Long id;
#ManyToOne
Branch branch;
...
}
#Entity
public class Branch {
#Id
#GeneratedValue
Long id;
...
}
public interface BranchRepository extends Repository<Branch, Long> {
...
}
public interface UserDetailsRepository extends Repository<UserDetails, Long> {
...
}
You can use the User-Branch relationship by controlling the association through its Foreign Key.
And inside the User class, you will specify the OneToOne mapping as follows:
#OneToOne
#JoinColumn(name="User_Branch_ID")
private Branch branch;
And this "User_Branch_ID" refers to the foreign key which you have created while creating the User database table as follows:
create table BRANCH (
branch_id BIGINT NOT NULL AUTO_INCREMENT,
name VARCHAR(30) NOT NULL,
city VARCHAR(30) NOT NULL,
country VARCHAR(30) NOT NULL,
PRIMARY KEY (address_id)
);
create table USER (
user_id BIGINT NOT NULL AUTO_INCREMENT,
user_branch_id BIGINT NOT NULL,
first_name VARCHAR(30) NOT NULL,
last_name VARCHAR(30) NOT NULL,
PRIMARY KEY (user_id),
CONSTRAINT user_branch FOREIGN KEY (user_branch_id) REFERENCES BRANCH ( branch_id)
);
step 1: create a view
create view v_BRANCH_USER as
select
a.branch_id, a.name , a.city, a.country
b.user_id,b.first_name, b.last_name
from BRANCH a, USER b
where a.branch_id = b.user_branch_id
step 2: create a pojo and mapping to hibernate as a table
#Entity
#Table(name = "v_BRANCH_USER")
public class VBranchUser
String userId;
....
}
step 3: You can query it as a table (Criteria, HQL ..)
I have the following relationship with person and transaction (one to one in my case). I want to be able to save a Person with a Transaction attached resulting in two inserts. One in tbl_person and one in tbl_Transaction. But the following only generates one insert instead of two. The one insert is in tbl_Transaction:
`CREATE TABLE `tbl_person` (
`ID` char(36) NOT NULL,
`TransactionID` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `TransactionID` (`TransactionID`),
CONSTRAINT `tbl_person_ibfk_1` FOREIGN KEY (`TransactionID`)
REFERENCES `tbl_Transaction` (`TransactionID`)
);
CREATE TABLE `tbl_transaction` (
`TransactionID` int(11) NOT NULL,
PRIMARY KEY (`TransactionID`)
);
#Table(name="tbl_person")
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
#ToString
#Data
public class Person {
#Id
#GeneratedValue(generator = "hibernate-uuid")
#GenericGenerator(name = "hibernate-uuid", strategy = "uuid2")
#Column(name="ID", nullable = false)
private String ID;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "transactionId")
private Transaction transaction;
}
#Table(name="tbl_transaction")
#Entity
#Data
public class Transaction {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer transactionId;
}
public class Service() {
public void saveTransaction(Transaction transaction) {
Person person = new Person();
person.setTransaction(transaction);
getSessionCurrent().save(person);
}
}
`
service.saveTransaction(transaction);
The service.saveTransaction returns with no exception but it only inserts the transaction but not the person.
Can any one tell me what I am doing wrong ??
you need to define a #OneToOne field in Transaction class
like specified in this question:
#OneToOne bidirectional mapping with #JoinColumn
and then add this line:
transcation.setPerson(person);
In my db I have two tables which look like this:
CREATE TABLE IF NOT EXISTS `Lokal` (
`idLokal` int(11) NOT NULL AUTO_INCREMENT,
`Ocena_idOcena` int(11) NOT NULL,
PRIMARY KEY (`idLokal`,`Ocena_idOcena`),
KEY `fk_Lokal_Ocena_idx` (`Ocena_idOcena`)
)
CREATE TABLE IF NOT EXISTS `Ocena` (
`idOcena` int(11) NOT NULL AUTO_INCREMENT,
`Ocena` int(1) DEFAULT NULL,
PRIMARY KEY (`idOcena`)
)
I want to map my Lokal entity to this Ocena table using #SecondaryTable Hibernate annotation, what I managed to achieve is this:
#Entity
#Table(name="Lokal")
#SecondaryTable(name = "Ocena", pkJoinColumns=#PrimaryKeyJoinColumn(name="Ocena_idOcena"))
public class Lokal {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="idLokal")
private int id;
#Column(table="Ocena" ,name="idOcena")
private int rating;
//--Getters and Setters skipped--//
}
But all I get is an error saying:
ERROR: Unknown column 'this_1_.Ocena_idOcena' in 'on clause'
I think I'm misunderstanding the #SecondaryTable annotation, but this is my first Spring/Hibernate application so I'd be glad for any kind of help.
Try this:
#Entity
#Table(name="Lokal")
#SecondaryTable(name = "Ocena", pkJoinColumns=#PrimaryKeyJoinColumn(name="idOcena"))
public class Lokal {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="idLokal")
private int id;
#Column(table="Ocena" ,name="rating")
private int rating;
//--Getters and Setters skipped--//
}
I use Hibernate to control my database and I have 2 tables:
CREATE TABLE IF NOT EXISTS `User`(
`id` INT NOT NULL AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL DEFAULT '',
`account` VARCHAR(255) NOT NULL DEFAULT '',
`password` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
)
CREATE TABLE IF NOT EXISTS `Project` (
`id` INT NOT NULL AUTO_INCREMENT,
`manager` INT NOT NULL,
`name` VARCHAR(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
FOREIGN KEY (`manager`) REFERENCES `User`(`id`)
)
And I have done the mapping:
User:
// ... import code
#Entity
#Table
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
private String name, account, password;
#OneToMany(mappedBy = "manager")
private List<Project> projects;
public User() {
}
// ... Getter & Setter code
}
Project:
// ... import code
#Entity
#Table
public class Project {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
private String name;
#ManyToOne
#JoinColumn(name = "manager")
private User manager;
public Project () {
}
// ... Getter & Setter code
}
I want to know whether it is possible when I select projects, the project will include its manager data but not have password.
In other ways, I want that each project I get will like this (format as JSON):
{
"id": 0,
"name": "A test project",
"manager": {
"id": 0,
"name": "John Smith"
"accound": "user1",
"password": null
}
}
1. Projection
A projection could be used to limit the fields you want to bring into memory, you could get a projection of all fields except the password.
2. Lazy
Another option can be adding the lazy annotation to the field:
#Basic(fetch = FetchType.LAZY)
#Column(...)
private String password;
3. HQL query
Another way would be to use a direct HQL query and load only the required fields, from this answer.
If you don't want to map this field at all, you could use #Transient annotation:
#Transient
private String password;
If you simply don't want to show this field when you convert the object to a particular JSON representation, then this is a data representation problem, not a ORM mapping problem. You should skip this field when you convert your object to JSON. The implementation depends on what JSON converter you are using. For instance, if you use Jackson, then the #JsonIgnore annotation is what you need.