I want to create entity USERS_GROUP to link some user_ids to group_id.
I guess I overthink this case and now I can't find a solution.
#Entity
#Table(name = "users")
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private String userId;
private String name;
#OneToMany(mappedBy = "user")
private List<UserGroups> userGroups;
#Table(name = "groups")
#Data
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private String groupId;
private String category;
private String name;
private String description;
#OneToMany(mappedBy = "group")
private List<UserGroups> userGroups;
#Entity
#Data
#Table(name = "user_groups")
public class UserGroups {
#EmbeddedId
UserGroupsCompositeKey id;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
private Users user;
#ManyToOne
#MapsId("featureId")
#JoinColumn(name = "group_id")
private Group group;
#Embeddable
public class UserGroupsCompositeKey implements Serializable {
#Column(name = "user_id")
String userId;
#Column(name = "group_id")
String groupId;
}
I want to sent POST requests like "/group/{group_id}/users"
to send in request body some lists of user_ids to connect them.
I think I overconfigured this solution with a composite key.
Is there some easier and more readable solution for that case ?
EDIT: This solution is not working as I want it to do.
I create jpaRepository class with CompositeKey (Not sure if it's correct way )
#Repository
public interface ProductFeaturesRepository extends JpaRepository<UserGroups, UserGroupsCompositeKey> {
}
And I want to add some Controller method to group class to add some users to group with request body like:
Endpoint:/group/{group_id}/users
Body:
[
{
"userId": "USER-NR423423534634"
},
{
"userId": "USER-NR2355321"
}
]
It's not working properly nothing is added to database after that request
Related
I'm having a hard time getting Spring Data to persist the values into an XREF Table. I'm attempting to talk to an existing DB so i cannot change the schema which would have made this easier.
Classes
#Entity
#Table(name="user_type")
class UserType {
#Id
#Column(name="name")
private String name;
}
#Entity
#Table(name="user_role")
class UserRole {
#Id
#Column(name="name")
private String name;
}
#Entity
#Table(name="company")
class Company{
#Id
#Column(name="id")
private UUID id;
#Column(name="name")
private String name;
}
#Entity
#Table(name="user")
class User {
#Id
#Column(name="id")
private UUID id;
#Column(name="email")
private String email;
#ManyToOne
#JoinTable
private UserType userType;
//not sure what to do here or which JoinTable/ManyToOne/Etc
private UserCompanyAccess userCompanyAccess;
}
#Entity
#Table(name="user_company_access")
class UserCompanyAccess {
#EmbeddedId
private UserCompanyAccessId userCompanyAccessId;
// not sure of relationships here either
private User user;
private Company company;
private UserRole userRole;
#Embeddable
static class UserAccessCompanyId implements Serializable {
#Column(name="id")
private UUID id;
}
}
I have tried many different combinations of #JoinColumns specifying the user(id) and userAccessCompany(user_id) as well with company. The code compiles but at runtime hibernate either throws an error saying company_id not provided or other random exceptions regarding trying to compare uuid to character varying etc which was really weird. Any help would be appreciated. I have done many #OneToMany/#ManyToOne/#ManyToMany but somehow never through spring data with an XREF table where the PK is not a composite of the 2 joining tables. That's what is throwing me off. Otherwise the #EmbeddedId would be the combination of the two.
The dark brown section of the spreadsheet is confusing, because it suggests there's a CompanyUser table that you haven't mentioned elsewhere. Is that an issue?
Regardless, it's a shame you can't adjust the schema, because this design has a lot of flaws. However, it should still be possible to use it. I'm a little stale on Java and Spring/Boot, and I don't have a useful way to test them easily, but you may have better luck with these:
#Entity
#Table(name="user")
class User {
#Id
#Column(name="id")
private UUID id;
#Column(name="email")
private String email;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_type", table="user_type", referencedColumnName = "name")
private UserType userType;
}
#Entity
#Table(name="user_type")
class UserType {
#Id
#Column(name="name")
private String name;
}
#Entity
#Table(name="user_role")
class UserRole {
#Id
#Column(name="name")
private String name;
}
#Entity
#Table(name="company")
class Company{
#Id
#Column(name="id")
private UUID id;
#Column(name="name")
private String name;
}
#Entity
#Table(name="user_company_access", uniqueConstraints = {
#UniqueConstraint(columnNames = { "user_id", "company_id" })
})
class UserCompanyAccess {
#Id
#Column(name="id")
private UUID id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "role", table="user_role", referencedColumnName = "name")
private UserRole userRole;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", table="user", referencedColumnName = "id")
private User user;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id", table="company", referencedColumnName = "id")
private Company company;
}
Explanation: The user_company_access table is where everything ultimately gets tied together. It's designed such that any particular combination of user and company to be unique, and #UniqueConstraint enables that in the code; you don't really need an embeddable object for that.
i am currently building an Application with Spring and i have a Question there:
I want to have an Entity Address which looks like this:
#Entity(name = "Address")
#Table(name = "address")
#EntityListeners(AuditingEntityListener.class)
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "city")
private String city;
#Column(name = "country")
private String country;
#Column(name = "postalcode")
private String postalCode;
#Column(name = "state")
private String state;
#Column(name = "street")
private String street;
public Address() {
}
}
I want to use this Address Entity in multiple Entities, for example in the User or Order Entity. Later, i will like to have many Entities which need an Address. But i don't want to specify each Relation in the Address Entity, otherwise it will get to complex. Is it possible to have a Link from the User to the Address with only specifying this Link in the User Entity?
My User Entity looks something like this:
#Entity(name = "User")
#Table(name = "User")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#OneToOne(
mappedBy = "address",
orphanRemoval = true,
cascade = {
CascadeType.PERSIST,
CascadeType.REMOVE
}
)
private Address billingAddress;
public User() {
}
}
Yes, it is possible, but you don't actually need mappedBy property, otherwise you are telling JPA to search for a address property in the other side of the relationship (that you actually want to be unidirectional):
#Entity(name = "User")
#Table(name = "User")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "username")
private String userName;
#OneToOne(
orphanRemoval = true,
cascade = { CascadeType.PERSIST, CascadeType.REMOVE }
)
private Address billingAddress;
public User() {
}
}
You can read more about this in the following online resources:
https://docs.oracle.com/javaee/6/api/javax/persistence/OneToOne.html
https://www.baeldung.com/jpa-one-to-one
https://javabydeveloper.com/one-one-unidirectional-association/
I have two objects. The company that can have multiple nested addresses.
#Entity
#Data
#Table(name = "company")
public class Company {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "phone")
private String phone;
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Address> addresses;
}
Address class looks like this:
#Data
#Entity
#Table(name = "address")
#ToString(exclude = "company")
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "postal_code")
private String postalCode;
#Column(name = "city")
private String city;
#Column(name = "street")
private String street;
#JsonIgnore
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "company_id")
private Company company;
}
I want somehow if it's possible, make a dynamic query that searches through the nested collection property. I made a search method which uses example matcher but the result is wrong. Every time I got everything from DB, not only company with address postal code that I'm looking for.
My search method looks like this:
#PostMapping("/search")
public List<Company> search(#RequestBody final Company company){
return companyRepository.findAll(Example.of(company,
ExampleMatcher.matchingAny()
.withIgnoreNullValues()
.withIgnorePaths("id")
.withStringMatcher(ExampleMatcher.StringMatcher.STARTING)));
}
In my database, I have two objects and this is the result of the search:
As you can see I received everything from DB instead of the only first company which address postal code starts with 1.
Hi you can use Specification<T>
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
For this you need to extend from interface JpaSpecificationExecutor:
public interface UserRepository extends JpaRepository<User> ,JpaSpecificationExecutor<User>{
}
And you also need to implement your custom Specification<T>
And then you can use repository.findAll(your impleneted Specification);
Spring docs :
https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaSpecificationExecutor.html
I think this is helpful.
I have a User Class
#Entity(name = "users")
#Table(name = "users")
public class User implements UserDetails {
static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "user_id", nullable = false)
private Long id;
#Column(name = "username", nullable = false, unique = true)
private String username;
#Column(name = "password", nullable = false)
private String password;
}
Tied to a simple Repository
public interface UserRepository extends PagingAndSortingRepository<User, Long> {
}
And I have an Instructor Class that has a nested User object
#Entity
#Table(name = "instructors")
public class Instructor {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "instructor_id", nullable = false, updatable = false)
private Long id;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "user_id")
private User user;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumn(name = "course_id")
private List<Course> courses;
}
It is saved with the following repository
public interface InstructorRepository extends PagingAndSortingRepository<Instructor, Long> {
}
The JSON I am posting
{
"user": {
"id": 1
}
}
When I try to do a POST to /instructors . User is coming in null. Is there something I am missing to get JPA to tie the two together? I have tried adding CascadeType.ALL onto the field and that only throws a detached persist exception.
Leave the CascadeType.ALL to Instructor like you already tried:
#ManyToOne(fetch = FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name = "user_id")
private User user;
In addition add the following to User. Seems to work with me. It provides the mapping information and makes JPA treat User managed
#OneToMany(mappedBy="user")//, cascade=CascadeType.ALL)
private List<Instructor> instructors = new ArrayList<>();
I have commented out the cascadeType in the above but it might be useful if you want to persist User wit all of its Instructors.
I have the below JSON as input:
{
"type": "Student",
"numOfPeople": "1",
"tenantMembers": [
{
"firstName": "Chris",
"lastName": "C"
}
],
"tenantDetails": {
"firstName": "John",
"lastName": "J",
"email" "xyz#gmail.com"
}
}
I want to use this to do a save:
tenantRepo.save(tenant);
This should save the parent "Tenant" and the children "TenantMembers" and "TenantDetails".
But when I do it does with NULL 'tenant_id's in the children. (If I have foreign keys in the DB gives 'tenant_id' can't be null constraint exception)
My question is: Is this possible in Hibernate?
My models:
Parent class:
#Entity
#Table(name = "tenant")
public class Tenant {
#GeneratedValue
#Id
private Long id;
private String type;
#Column(name = "num_of_people")
private String numOfPeople;
#OneToMany(mappedBy = "tenant", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<TenantMember> tenantMembers;
#OneToOne(mappedBy = "tenant", cascade = CascadeType.ALL)
private TenantDetails tenantDetails;
TenantMember child class:
#Entity
#Table(name = "tenant_member")
public class TenantMember {
#GeneratedValue
#Id
private Long id;
#ManyToOne
#JoinColumn(name = "tenant_id")
private Tenant tenant;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
TenanatDetails child class:
#Entity
#Table(name="tenant_details")
public class TenantDetails {
#GeneratedValue
#Id
private Long id;
#OneToOne
#JoinColumn(name = "tenant_id")
private Tenant tenant;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
private String email;
EDIT:
Following up Dragan Bozanovic's suggestion, tried using #JsonIdentityInfo
for the three tables:
#Entity
#Table(name = "tenant")
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
public class Tenant {
#Entity
#Table(name="tenant_details")
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
public class TenantDetails {
#Entity
#Table(name = "tenant_member")
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
public class TenantMember {
and did the following to save:
#RequestMapping(value = "/set", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public Tenant test(#RequestBody Tenant tenant) {
Tenant t = new Tenant();
t.setType(tenant.getType());
t.setNumOfPeople(tenant.getNumOfPeople());
tenantRepo.save(t);
tenant.setId(t.getId());
tenant.getTenantDetails().setTenant(tenant);
for(TenantMember member: tenant.getTenantMembers()) {
member.setTenant(tenant);
}
return tenantRepo.save(tenant);
}
Would this be the best approach that is possible?
Hibernate does save the children (hence the constraint violation) because of the cascading options you specified, but it does not save the relationship information (join column value) in your case.
TenantMember and TenantDetails are the owners of the association with Tenant (mappedBy attributes in the association annotations in Tenant).
That means that you have to properly update the tenant field in the TenantMember and TenantDetails instances, because Hibernate ignores inverse side of the association when maintaining the relationship.