Many-to-Many
In database design, a “many-to-many” relationship means records in two tables can be associated with each other, with multiple records associated with multiple records. For example, the relationship between a “student” table and a “course” table may be many-to-many, because one student can enroll in multiple courses, and one course can be taken by multiple students.
To implement many-to-many relationships, an intermediate table (association table) is usually used. This intermediate table acts as a bridge, connecting records from both tables through their primary keys.
Characteristics of Many-to-Many Relationships
- Bidirectionality: A can be associated with multiple B, and B can also be associated with multiple A
- Requires Intermediate Table: To represent this relationship, an intermediate table is usually used to maintain the association
- High Flexibility: Many-to-many relationships are very suitable for expressing complex business logic
Query Model
The relationship between user table and role table: one user has multiple roles, one role is used by multiple users. The many-to-many query requirement is to query a user and simultaneously query all roles that user has.
Create Tables
wzk_user_role
CREATE TABLE `wzk_user_role` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
wzk_role
CREATE TABLE `wzk_role` (
`id` int(11) NOT NULL,
`rolename` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
Insert Data
wzk_user_role
INSERT INTO wzk_user_role VALUES(1, 1, 1);
INSERT INTO wzk_user_role VALUES(1, 2, 2);
wzk_role
INSERT INTO wzk_role VALUES(1, "ADMIN");
INSERT INTO wzk_role VALUES(2, "USER");
Query Statement
select u.*,r.*,r.id rid from wzk_user u
left join wzk_user_role ur on u.id=ur.user_id
inner join wzk_role r on ur.role_id=r.id;
Create Classes
WzkUser
package icu.wzk.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WzkUser {
private int id;
private String username;
private String password;
private Date birthday;
private List<WzkOrder> orderList;
private List<WzkRole> roleList;
}
WzkRole
package icu.wzk.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class WzkRole {
private Integer id;
private String rolename;
}
UserMapper
package icu.wzk.mapper;
import icu.wzk.model.WzkUser;
import java.util.List;
public interface UserMapper {
List<WzkUser> findAll();
List<WzkUser> findAllUserAndRole();
}
UserMapper.xml
<resultMap id="userRoleMap" type="icu.wzk.model.WzkUser">
<result column="id" property="id"></result>
<result column="username" property="username"></result>
<result column="password" property="password"></result>
<result column="birthday" property="birthday"></result>
<collection property="roleList" ofType="icu.wzk.model.WzkRole">
<result column="rid" property="id"></result>
<result column="rolename" property="rolename"></result>
</collection>
</resultMap>
<select id="findAllUserAndRole" resultMap="userRoleMap">
SELECT u.*, r.*, r.id rid
FROM wzk_user u
LEFT JOIN wzk_user_role ur on u.id=ur.user_id
INNER JOIN wzk_role r on ur.role_id=r.id;
</select>
Write Code
package icu.wzk;
import icu.wzk.mapper.UserMapper;
import icu.wzk.model.WzkUser;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class WzkIcu10 {
public static void main(String[] args) throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("sqlMapConfig.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
.build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<WzkUser> dataList = userMapper.findAllUserAndRole();
dataList.forEach(System.out::println);
sqlSession.close();
}
}
Running Results
WzkUser(id=1, username=wzk, password=icu, birthday=Mon Nov 11 00:00:00 CST 2024, orderList=null, roleList=[WzkRole(id=1, rolename=ADMIN)])
WzkUser(id=2, username=wzk2, password=icu2, birthday=Mon Nov 11 00:00:00 CST 2024, orderList=null, roleList=[WzkRole(id=2, rolename=USER)])
Notes
- Index Optimization: Create indexes on foreign key fields of the intermediate table to improve query performance
- Relationship Constraints: Use foreign key constraints to maintain data consistency and avoid orphaned data
- Batch Operations: When performing large-scale inserts and updates, try to use batch operations to improve efficiency
- Soft Delete: The intermediate table can be designed with a “soft delete” flag to preserve historical records
Optimizing Many-to-Many Relationship Model
Optimizing many-to-many relationship models requires attention to multiple aspects:
- Improve Performance: Optimize queries and updates through indexing, partitioning, and batch operations
- Ensure Data Consistency: Avoid data anomalies through foreign key constraints and transaction processing
- Increase Flexibility: Preserve historical data through soft deletes and logging, supporting recovery and auditing
- Handle Large Data Scenarios: Ensure system scalability through partitioning, sharding, or database splitting