this post was submitted on 08 Jun 2024
-1 points (44.4% liked)

Programming

18304 readers
99 users here now

Welcome to the main community in programming.dev! Feel free to post anything relating to programming here!

Cross posting is strongly encouraged in the instance. If you feel your post or another person's post makes sense in another community cross post into it.

Hope you enjoy the instance!

Rules

Rules

  • Follow the programming.dev instance rules
  • Keep content related to programming in some way
  • If you're posting long videos try to add in some form of tldr for those who don't want to watch videos

Wormhole

Follow the wormhole through a path of communities [email protected]



founded 2 years ago
MODERATORS
 

cross-posted from: https://lemmy.world/post/16301329

Hello Java Community.

Sorry if there's any grammar / spelling mistakes in this post. I'll try to convey my questions as clear as possible.

Introduction

I have basic knowledge about Java Programming. I took Programming Courses at my Uni and somewhat familiar with Java and OOP concepts in general ( barely ).

So for the last few weeks, I've been watching a great course from BouAli Free Code Camp Springboot Full Course on Youtube.

After completing the course, I challenge my self to create a basic E-Commerce Application with Springboot as the Framework, in order to practice my self with the knowledge i got from the course.

This Application will be a Simple Multi Tenant Application, where each tenant can create a product page, and a customer can purchase. Mind you i create this app to challenge my understanding about Springboot, and will not be a complex app for use.

Set Up

In the tutorial, there's a usage of @ManyToMany to map Many to Many Relationship. I thought that i could use Many To Many Mapping on Tenants <-> Users Relationship. In my understanding, A tenantEntity could have multiple users assigned, and multiple usersEntity could belong to a tenantEntity

With that understanding, i try to create my tenantEntity and userEntity as follow

// tenantEntity.java
// Import Ommited for simplicity
// Using Lombok Setter, Getter, AllArgs, NoArgs
@Entity
@Table(name = "T_TENANT")  
public class TenantEntity {  
    @Id  
    @GeneratedValue(strategy = GenerationType.UUID)  
    private UUID id;  
  
    @Column(nullable = false)  
    private String tenantName;  
  
    @Column(length = 2000)  
    private String tenantDescription;  
  
    private String website;  
  
    @Column(nullable = false)  
    private Boolean isEnabled;  
  
    private Instant subscriptionExpiryDate;  
  
    @Column(length = 10)  
    private int gracePeriodDays;  
  
    @ManyToMany(mappedBy = "tenants", fetch = FetchType.LAZY)  
    private Set<UserEntity> users = new HashSet<>();  
  
    @Column(updatable = false, nullable = false)  
    @CreationTimestamp  
    private Instant createdAt;  
  
    @Column  
    @UpdateTimestamp   
    private Instant updatedAt;  
}
// userEntity.java
// Import Ommited for simplicity
// Using Lombok Setter, Getter, AllArgs, NoArgs
@Entity
@Table(name = "T_USER")  
public class UserEntity {  
    @Id  
    @GeneratedValue(strategy = GenerationType.IDENTITY)  
    private Long id;  
  
    @Column(nullable = false, unique = true)  
    private String username;  
  
    @Column(nullable = false, unique = true)  
    private String email;  
  
    @Column(nullable = false)  
    private String firstName;  
  
    private String lastName;  
  
    @Column(nullable = false)  
    private String password;  
  
    @Column(nullable = false)  
    private boolean isEnabled;  
  
    @ManyToMany(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH})  
    @JoinTable(  
            name = "t_users_tenants_mapping",  
            joinColumns = @JoinColumn(  
                    name = "fk_user_id", referencedColumnName = "id"  
            ),  
            inverseJoinColumns = @JoinColumn(  
                    name = "fk_tenant_id", referencedColumnName = "id"  
            )  
    )  
    private Set<TenantEntity> tenants = new HashSet<>();  
  
    @Column(updatable = false, nullable = false)  
    @CreationTimestamp  
    private Instant createdAt;  
  
    @Column  
    @UpdateTimestamp    
    private Instant updatedAt;  
}

Generating this relationship in the database

Creating New User

When creating new user, i could assign the Tenant to the User, resulting in these data

// POST  api/v1/users
{
  "username": "newuser01",
  "first_name": "new",
  "last_name": "user",
  "email": "newuser@localhost",
  "password": "<P@ssw0rd/>",
  "is_enabled": true,
  "tenant_id": [
    "79cf0ecf-976a-472c-b250-2192e630a4e4",
    "ac5b5786-c467-4dd6-b74d-7f70c83e1827"
  ]
}
fk_user_id|fk_tenant_id                        |
----------+------------------------------------+
         6|79cf0ecf-976a-472c-b250-2192e630a4e4|
         6|ac5b5786-c467-4dd6-b74d-7f70c83e1827|

Problem(s)

Now, how does one updates the data for the ManyToMany relationship? Say, i want to modify the membership of a user. i want to add new tenant or remove existing tenant?

I try creating update method on my userRepository

// userRepository
public interface UserEntityRepository extends JpaRepository<UserEntity, Long> {  
  
    @Transactional  
    @Modifying    @Query("""  
            update UserEntity u set u.username = :username, u.email = :email, u.firstName = :firstName, u.lastName = :lastName, u.isEnabled = :isEnabled, u.tenants = :tenants, u.updatedAt = :updatedAt            where u.id = :id""")  
    int updateUsernameAndEmailAndFirstNameAndLastNameAndIsEnabledAndTenantsAndUpdatedAtById(  
            @Param("username") String username,  
            @Param("email") String email,  
            @Param("firstName") String firstName,  
            @Param("lastName") String lastName,  
            @Param("isEnabled") boolean isEnabled,  
            @Param("tenants") Set<TenantEntity> tenants,  
            @Param("updatedAt") Instant updatedAt,  
            @Param("id") Long id  
    );  
}

which being called by userService

public UserResponseDTO updateUser(Long userId, UserRequestDto dto){  
        Instant updateAt = Instant.now().atZone(ZoneId.of("Continent/Ciy")).toInstant();  
        Set<TenantEntity> tenantEntitySet = getTenantEntitiesFromUserRequestDTO(dto);  
  
        int updateRows = userEntityRepository.updateUsernameAndEmailAndFirstNameAndLastNameAndIsEnabledAndTenantsAndUpdatedAtById(  
                dto.username(),  
                dto.email(),  
                dto.first_name(),  
                dto.last_name(),  
                dto.is_enabled(),  
                tenantEntitySet,  
                updateAt,  
                userId  
        );  
  
        UserResponseDTO userResponseDTO;  
        UserEntity userEntity;  
        if (updateRows == 1) {  
            userEntity = userEntityRepository.findById(userId).orElse(new UserEntity());  
        } else {  
            userEntity = new UserEntity();  
        }  
        return userMapper.mapUserEntityToUserResponseDto(userEntity);  
    }

resulting in a NullPointerException java.lang.NullPointerException: Cannot invoke "org.hibernate.sql.ast.tree.expression.Expression.getColumnReference()" because "pathSqlExpression" is null

in which resulted in a Hibernate Forum that states Updating *-to-many collections is not possible, but please submit a bug report to our issue tracker([https://hibernate.atlassian.net 14](https://hibernate.atlassian.net)) to improve the error message.

Questions

So what is the best practice of doing things with ManytoMany associations? I've found many tutorials that state the use of @ManyToMany Annotations, but left after they're done inserting things to the database. How can i update the join table relations when the need to modify the data arises?

Is there a knowledge gap between me and Springboot especially with JPA? should i take another approach?

Any kind of feedback and suggestions are welcome

Thank You :D

Resources

  1. Free Code Camp | Springboot Full Course Youtube
  2. Baledung Explenation of ManyToMany
  3. vladmihalcea -> Just found out today, will read it later
  4. GeeksForGeeks ManyToMany
top 1 comments
sorted by: hot top controversial new old
[–] calcopiritus 1 points 8 months ago

I usually avoid ManyToMany relationships. Instead, I do them "manually" as I would directly in the database. If I want to relate A to B in a many to many relationship, I create C, which contains a ManyToOne (I don't remember if it's ManyToOne or OneToMany) relationship to A and a ManyToOne relationship to B. So basically C just contains a foreign key to A and another to B.