I've created a new project in Java using Springboot. I've followed a tutorial online to create an Entity called User which I have stored in a PostgreSQL database using Hibernate and a schema file. The User file is shown below. I'm now trying to create a Controller that will allow me to get all the data of a User from the database, but Hibernate creates another table to store the #ElementCollection. This means when I use the default queries, I get the User, but the roles column is not able to be accessed since it has stored in another table. It is stored as a jsonb. How can I get the entire User data?
User class
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private #Getter long id;
private #Getter #Setter String email;
private #Getter #Setter String encryptedPassword;
private #Getter String accessToken;
private #Getter float netWorth;
#ElementCollection
private #Getter #Setter Set<String> roles;
... (Other class functions)
}
Repository and Service class
#Repository
public interface UserRepository extends CrudRepository<User, Long> {}
#Service
public class UserService {
#Autowired // This means to get the bean called userRepository
// Which is auto-generated by Spring, we will use it to handle the data
private UserRepository repository;
public Optional<User> findOne(long id) {
return repository.findById(id);
}
}
Controller Class
#RestController
#RequestMapping(path="/user") // This means URL's start with /user (after Application path)
public class UserController {
#Autowired
private UserService userService;
#GetMapping(path="/get/{id}") // Map ONLY GET Requests
public ResponseEntity<User> getUser(#PathVariable("id") long id) {
// #ResponseBody means the returned String is the response, not a view name
// #RequestParam means it is a parameter from the GET or POST request
try {
Optional<User> savedUser = userService.findOne(id);
if (!savedUser.isPresent()) {
throw new ResponseStatusException(
HttpStatus.NOT_FOUND, "User with ID: " + id + " not found.");
}
return new ResponseEntity<>(savedUser.get(), HttpStatus.FOUND);
} catch (Exception e) {
throw new ResponseStatusException(
HttpStatus.INTERNAL_SERVER_ERROR, "Error in retrieving user.", e);
}
}
}
I have developed two tables in Spring Boot, User and UserMeta. User is the parent and UserMeta is the child table. The foreign-key is user_id. I may be looking at it the wrong way, but I want to be able to first create an entity of User. Then, I want to create an entity of UserMeta. Simply UserMeta should contain additional data to User.
However, when first creating a User and then a UserMeta entity, I get e new User entity (ending up with two User entities and one UserMeta entity.)
The problem I think is that I create a UserMeta object with a User, since I want to have a relationship between User and UserMeta. But if I want to be able to first create a User and then a UserMeta, should I simply ignore a foreign-key? Or, does it exists another way of creating a UserMeta entity without creating a new User?
User
public class User {
#Id
#SequenceGenerator(name = "user_sequence", sequenceName = "user_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_sequence")
//#OneToOne(optional=false)
private Long userId;
private String username;
private String password;
private String email;
#OneToOne(mappedBy = "user")
private UserMeta userMeta;
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
}
UserMeta
public class UserMeta {
#Id
#SequenceGenerator(name = "user_meta_sequence", sequenceName = "user_meta_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_meta_sequence")
private Long userMeta_Id;
private String lastname;
private int age;
#OneToOne(
cascade = CascadeType.ALL,
fetch = FetchType.LAZY,
optional = false
)
#JoinColumn(
name = "user_Id",
referencedColumnName="userId"
)
private User user;
public UserMeta(String lastName, int age, User user){
this.lastname = lastName;
this.age = age;
this.user = user;
}
}
UserRepository
public interface UserRepository extends CrudRepository<User, Long> {
}
UserService
public interface UserService {
User saveUser(User user);
}
UserServiceImpl
#Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
super();
this.userRepository = userRepository;
}
#Override
public User saveUser(User user) {
// TODO Auto-generated method stub
return this.userRepository.save(user);
}
UserController
#RestController
public class UserController {
private UserService userService;
public UserController(UserService userService) {
super();
this.userService = userService;
}
#PostMapping("/user")
public ResponseEntity<User> saveUser(#RequestBody User user) {
return new ResponseEntity<User>(userService.saveUser(user), HttpStatus.CREATED);
}
}
UserMetaRepository
public interface UserMetaRepository extends CrudRepository<UserMeta, Long> {
}
UserMetaService
public interface UserMetaService {
UserMeta saveUserMeta(UserMeta userMeta);
}
UserMetaServiceImpl
#Service
public class UserMetaServiceImpl implements UserMetaService{
private UserMetaRepository userMetaRepo;
public UserMetaServiceImpl(UserMetaRepository userMetaRepo) {
super();
this.userMetaRepo = userMetaRepo;
}
#Override
public UserMeta saveUserMeta(UserMeta userMeta) {
return this.userMetaRepo.save(userMeta);
}
}
UserMetaController
#RestController
public class UserMetaController {
public UserMetaService userMetaService;
public UserMetaController(UserMetaService service) {
super();
this.userMetaService = service;
}
#PostMapping("/userMeta")
public ResponseEntity<UserMeta> saveUserMeta(#RequestBody UserMeta userMeta) {
return new ResponseEntity<UserMeta>(this.userMetaService.saveUserMeta(userMeta), HttpStatus.CREATED);
}
}
you should use this constructor in the User class,
public User(String username, String email, String password, UserMeta userMeta) {
this.username = username;
this.email = email;
this.password = password;
this.userMeta = userMeta;
}
now when you save your user the user Meta will be added to your UserMeta table,
If you want to add a user Meta to an existing user you will only need to set the userMeta and save it with a simple userRepository.save(theUpdatedUser)
you can also create userMeta seperately with your code above, and if you want to assign it to a user already in data base or not you can allows use the power of spring data and use simple userRepository.save(userWithTheAssignedMeta)
the same logic applies the other way for metaUser.
The problem here is that your UserMetadata creation logic is using incomplete JSON:
{ "lastName":"foo", "age":1, "user":{ "username":"foo", "password":"bar", "email":"foo-bar" } }
Within this, the problem is the 'user' has all the data, duplicating what was already created the the database, but does not identify it. Since the mapping has cascade.ALL set on it, Spring/JPA will persist the UserMetadata and find this User instance that doesn't have identity, so persist it - giving it identity from the sequence.
There are a few ways you might correct this. First and easiest is to send the User ID in the json from the previously created instance:
{ "lastName":"foo", "age":1, "user":{ "userId":1, "username":"foo", "password":"bar", "email":"foo-bar" } }
This will allow Spring/JPA to recognize the user's identity and merge it and the data provided into the database. It means though that you must send complete data for the User - it will push incomplete data into the DB.
If that is a concern, you can change the cascade options. You may not want cascading persist/merge at all on this relationship, and I suspect when you delete userMetadata you don't really want to delete the User instance, so I think this might have been done incorrectly (maybe put it on the user->UserMetadata relationship instead?). If you remove the cascade settings, spring/JPA will let you just pass in JSON with the USER id specified, as this gives it enough to set the fk:
{ "lastName":"foo", "age":1, "user":{ "userId":1} }
I have a components whose members are populated from the YAML file. I have a inner component to this component who also have some members populated from the YAML.
But when I start the app, the members of the inner component are not populated though the instance is created. The main components members are loaded up fine.
Need to know why the members of the Department components are not loaded from the YAML.
update
I can see the that inner class is loaded with the members but that is done later during the application load but the Company component is used for loading some other components before that. So I need the inner class/component to be initialized immediately after the Company component is initialized.
This is how my component is defined
#Component
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(Include.NON_NULL)
#ConfigurationProperties(prefix = "company")
public class Company {
#JsonProperty("connectTimeout")
private Integer connectTimeout;
#JsonProperty("socketTimeout")
private Integer socketTimeout;
#JsonProperty("clientTimeout")
private Integer clientTimeout;
#JsonProperty("dept1")
#Autowired
private Department library;
#JsonProperty("dept2")
#Autowired
private Department admin;
#JsonProperty("dept3")
#Autowired
private Department transport;
#JsonProperty("dept4")
#Autowired
private Department finance;
//Getters and Setters
#Component
#SuppressWarnings({"PublicInnerClass", "WeakerAccess"})
public static class Department {
#JsonProperty("connectTimeout")
private Integer connectTimeout;
#JsonProperty("socketTimeout")
private Integer socketTimeout;
#JsonProperty("clientTimeout")
private Integer clientTimeout;
//Getters and Setters
}
}
The below is my YAML file
company:
connectTimeout: 1000
socketTimeout: 20000
clientTimeout: 150
dept1:
connectTimeout: 100
socketTimeout: 100
clientTimeout: 100
dept2:
connectTimeout: 100
socketTimeout: 100
clientTimeout: 100
dept3:
connectTimeout: 100
socketTimeout: 100
clientTimeout: 100
dept4:
connectTimeout: 100
socketTimeout: 100
clientTimeout: 100
Assume we have the following property holder dto's Company & Department;
public class Company {
private Integer connectTimeout;
private Integer socketTimeout;
private Integer clientTimeout;
private Department library;
private Department admin;
private Department transport;
private Department finance;
public void setDept1(Department dept1) {
this.library = dept1;
}
public void setDept2(Department dept2) {
this.admin = dept2;
}
public void setDept3(Department dept3) {
this.transport = dept3;
}
public void setDept4(Department dept4) {
this.finance = dept4;
}
// other getter/setters
public static class Department {
private Integer connectTimeout;
private Integer socketTimeout;
private Integer clientTimeout;
// getter/setters
}
}
& just define a config property containing bean with;
#Configuration
public class MyConfiguration {
#Bean
#ConfigurationProperties(prefix = "company")
public Company company() {
return new Company();
}
}
Using #JsonProperty wouldn't work with such properties, since that annotation only works with de/serialization through Jackson. #ConfigurationProperties focuses on basic getter/setters, just adding setters with those names is sufficient, e.g. setDept1, setDept2 etc.
Read more on externalized configuration on Spring Boot, here
I am writing query using In operator in spring boot using JPA
select * from data where name in ("XYZ","XY")
I am unable to implement DAO implementation for the following input in spring boot with JPA.
{"name":["XYZ","XY"]}
Controller
#PostMapping("/embdata/dto/name")
public List getByBatchIdsWithDTO(#RequestBody EmbDataDTO dto){
return service.getBatchIds(dto);
}
Service Method:
List<Data> obj=repo.findBybatchID(List<String> name)
Repo Interface:
List<Data> findByBatchIdIn(List wageId);
My entity Class
#Entity
#Data
#Table(name="data")
public class Data{
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name="id")
int id;
#Column(name="name")
String name;
#Column(name="address")
String address;
}
My DTO class:
#Data
public class EmbDataDTO {
private int id;
private String name;
private String address;
}
please help me to implement for the following input.
Your repo should look like :
#Repository
#Transactional
class YourRepo {
List<Data> findByBatchIdIn(List wageId) {
Query q = entityManager.createQuery("select * from data where name in (:list)");
q.setListParameter("list", wageId);
return q.execute();
}
}
At least something like that.
I'm working on a professional social network application using couchbase server 4.1 with spring data couchbase 2.2.4 in a spring boot application using java 1.8.
I want to replace the next stream which searches a LikeEntity with specific company in the database by a N1QL based query which searches a user with the same previous likeEntity unnested:
Here is the service containing the stream to replace by the query:
#Service
class CompanyServiceImpl implements CompanyService{
#Override
public void addCompanyToLike(UserEntity user, Company company){
int incrementValue=1;
LikeEntity existingLike=user.getLikeEntities()
.stream()
.filter(le->le.getCompany().getName().equals(company.getName()))
.findFirst();
//rest of process
}
}
Here is the different java beans you will need to look at:
UserEntity class:
#Document
#ViewIndexed(designDoc = "user")
class UserEntity implements Serializable{
#Field private String id;
#Reference
private List<LikeEntity> likeEntities=new ArrayList<LikeEntity>();
//Other attributes plus getters and setters:
}
LikeEntity class:
#Document
class LikeEntity implements serializable{
#Reference
private Company company;
#Reference
private Job job;
// other attributes plus getters and setters
}
As you see above, the LikeEntity class may contain any object liked by the user, it can be a company, a job or another object. Also the LikeEntity is stored only inside a user document as element of user's list of likes and not independately in the database.It's my choice of modelization because the LikeEntity won't by used in other java beans of my application
Company:
#Document
#ViewIndexed(designDoc = "company")
class Company implements Serializable{
#Field private String id;
#Field private String name;
//Other attributes plus getters and setters
}
N.B: I've tried the next query inside UserRepository but it didn't work:
UserRepository:
#Repository
#N1qlPrimaryIndexed
#N1qlSecondaryIndexed(indexName = "user")
public interface UserRepository extends CouchbaseRepository<UserEntity, String> {
#Query("SELECT b.*, likeEntity FROM #{#n1ql.bucket} AS b UNNEST b.likeEntities AS likeEntity WHERE b.id=$1 AND likeEntity.company.name=$2")
UserEntity findByLikedCompanyName(String idUser
, String companyName);
}
I'm looking for your answers, and thank you so much in advance.