Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

这篇文章显示 Hibernate 的多对多实例,在 Spring MVC CRUD Web应用程序中连接表。我们将同时讨论管理多对多关系在视图和后端。 我们将使用应用程序的Web界面创建,更新,删除和查询。
本教程是利用 Spring 的 org.springframework.core.convert.converter.Converter 接口,它帮助我们在项目的数据库中实现实体的映射标识。
完整的示例的说明介绍如下。

使用以下技术:

  • Spring 4.1.7.RELEASE
  • Hibernate Core 4.3.10.Final
  • validation-api 1.1.0.Final
  • hibernate-validator 5.1.3.Final
  • MySQL Server 5.6
  • Maven 3
  • JDK 1.7
  • Tomcat 8.0.21
  • Eclipse JUNO Service Release 2

现在,让我们开始!

步骤1.创建模式用于连接多对多表关联

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

APP_USER :包含用户。一个用户可以有多个配置[USER,ADMIN,DBA]。

USER_PROFILE : 包含用户配置文件。配置文件可以链接到多个用户。

APP_USER_USER_PROFILE : 这是一个连接表连接APP_USER&USER_PROFILE中的多对多关系。

出于演示的目的,我们在这里只讨论许多对多的单向[用户到用户信息]设置。
create table APP_USER (
id BIGINT NOT NULL AUTO_INCREMENT,
sso_id VARCHAR(30) NOT NULL,
password VARCHAR(100) NOT NULL,
first_name VARCHAR(30) NOT NULL,
last_name VARCHAR(30) NOT NULL,
email VARCHAR(30) NOT NULL,
PRIMARY KEY (id),
UNIQUE (sso_id)
); create table USER_PROFILE(
id BIGINT NOT NULL AUTO_INCREMENT,
type VARCHAR(30) NOT NULL,
PRIMARY KEY (id),
UNIQUE (type)
); CREATE TABLE APP_USER_USER_PROFILE (
user_id BIGINT NOT NULL,
user_profile_id BIGINT NOT NULL,
PRIMARY KEY (user_id, user_profile_id),
CONSTRAINT FK_APP_USER FOREIGN KEY (user_id) REFERENCES APP_USER (id),
CONSTRAINT FK_USER_PROFILE FOREIGN KEY (user_profile_id) REFERENCES USER_PROFILE (id)
); /* Populate USER_PROFILE Table */
INSERT INTO USER_PROFILE(type)
VALUES ('USER'); INSERT INTO USER_PROFILE(type)
VALUES ('ADMIN'); INSERT INTO USER_PROFILE(type)
VALUES ('DBA'); commit;
第2步:创建目录结构
以下是最终的项目结构:

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

第3步:更新pom.xml,包括所需的依赖关系
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.yiibai.springmvc</groupId>
<artifactId>SpringMVCMany2ManyCRUD</artifactId>
<packaging>war</packaging>
<version>1.0.0</version>
<name>SpringMVCHibernateManyToManyCRUDExample</name> <properties>
<springframework.version>4.1.7.RELEASE</springframework.version>
<hibernate.version>4.3.10.Final</hibernate.version>
<mysql.connector.version>5.1.31</mysql.connector.version>
</properties> <dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${springframework.version}</version>
</dependency> <!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency> <!-- jsr303 validation -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.3.Final</version>
</dependency> <!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
</dependency> <!-- Servlet+JSP+JSTL -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency> </dependencies> <build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<warSourceDirectory>src/main/webapp</warSourceDirectory>
<warName>SpringMVCMany2ManyCRUD</warName> <failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<finalName>SpringMVCMany2ManyCRUD</finalName>
</build>
</project>
第4步:准备Model类
package com.yiibai.springmvc.model;

import java.util.HashSet;
import java.util.Set; import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table; import org.hibernate.validator.constraints.NotEmpty; @Entity
@Table(name="APP_USER")
public class User { @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id; @NotEmpty
@Column(name="SSO_ID", unique=true, nullable=false)
private String ssoId; @NotEmpty
@Column(name="PASSWORD", nullable=false)
private String password; @NotEmpty
@Column(name="FIRST_NAME", nullable=false)
private String firstName; @NotEmpty
@Column(name="LAST_NAME", nullable=false)
private String lastName; @NotEmpty
@Column(name="EMAIL", nullable=false)
private String email; @NotEmpty
@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "APP_USER_USER_PROFILE",
joinColumns = { @JoinColumn(name = "USER_ID") },
inverseJoinColumns = { @JoinColumn(name = "USER_PROFILE_ID") })
private Set<UserProfile> userProfiles = new HashSet<UserProfile>(); public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getSsoId() {
return ssoId;
} public void setSsoId(String ssoId) {
this.ssoId = ssoId;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getFirstName() {
return firstName;
} public void setFirstName(String firstName) {
this.firstName = firstName;
} public String getLastName() {
return lastName;
} public void setLastName(String lastName) {
this.lastName = lastName;
} public String getEmail() {
return email;
} public void setEmail(String email) {
this.email = email;
} public Set<UserProfile> getUserProfiles() {
return userProfiles;
} public void setUserProfiles(Set<UserProfile> userProfiles) {
this.userProfiles = userProfiles;
} @Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((ssoId == null) ? 0 : ssoId.hashCode());
return result;
} @Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof User))
return false;
User other = (User) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (ssoId == null) {
if (other.ssoId != null)
return false;
} else if (!ssoId.equals(other.ssoId))
return false;
return true;
} @Override
public String toString() {
return "User [id=" + id + ", ssoId=" + ssoId + ", password=" + password
+ ", firstName=" + firstName + ", lastName=" + lastName
+ ", email=" + email + "]";
} }
再看看 UserProfiles 属性是如何标注了多对多。

@ManyToMany表示用户和用户配置之间有多到多关系。一个用户可以有多个资料[USER,ADMIN,DBA]用户资料信息可以属于多个用户。@JoinTable表示它使用两个表的表外键链接来约束自己的主键。这个注解,主要用于关系的拥有方。joinColumns是指拥有方(用户ID)的列名,inverseJoinColumns是指关系的反向端(USER_PROFILE的ID)的列。这个连接表的主键是USER_ID & USER_PROFILE_ID 组合。

延迟加载:

要特别注意fetch = FetchType.LAZY。在这里,我们通知 Hibernate 懒加载用户资料集合。 这也是默认的行为。在此设置中,首先访问仅当查询加载集合将被触发。这是一个很好的方式,以避免加载-这是一个昂贵的操作所有连接的对象。 当在事务/活动会话,并会尝试访问集合,Hibernate会触发不同的选择来获取它们。

但是,如果您不在活动的会话(会话关闭/无事务:如在JSP),并试图访问集合,你会遇到报应:org.hibernate.LazyInitializationException – could not initialize proxy – no Session. 为了避免它,需要通过调用 Hibernate.initialize(user.getUserProfiles()); 来初始化对需要的集合; 在有效会话中[在DAO方法,在显示视图之前,可以调用这个初始化方法]。

还要注意的是,我们没有使用任何级联。这是因为用户个人资料不依赖用户,并能独立生存。
重要:如果是*Many* 关联它总是覆盖 hashCode 和 equals 方法,它是通过 Hibernate 持有合并形成集合。
package com.yiibai.springmvc.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table; @Entity
@Table(name="USER_PROFILE")
public class UserProfile { @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id; @Column(name="TYPE", length=15, unique=true, nullable=false)
private String type = UserProfileType.USER.getUserProfileType(); public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getType() {
return type;
} public void setType(String type) {
this.type = type;
} @Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
} @Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof UserProfile))
return false;
UserProfile other = (UserProfile) obj;
if (id == null) {
if (other.id != null)
return false;
} else if (!id.equals(other.id))
return false;
if (type == null) {
if (other.type != null)
return false;
} else if (!type.equals(other.type))
return false;
return true;
} @Override
public String toString() {
return "UserProfile [id=" + id + ", type=" + type + "]";
} }
既然我们都显示单向关系(User到UserProfile),无须参考用户UserProfile。
package com.yiibai.springmvc.model;

public enum UserProfileType {
USER("USER"),
DBA("DBA"),
ADMIN("ADMIN"); String userProfileType; private UserProfileType(String userProfileType){
this.userProfileType = userProfileType;
} public String getUserProfileType(){
return userProfileType;
} }
第5步:创建DAO层
package com.yiibai.springmvc.dao;

import java.util.List;

import com.yiibai.springmvc.model.User;

public interface UserDao {

	User findById(int id);

	User findBySSO(String sso);

	void save(User user);

	void deleteBySSO(String sso);

	List<User> findAllUsers();

}
package com.yiibai.springmvc.dao;

import java.util.List;

import com.yiibai.springmvc.model.UserProfile;

public interface UserProfileDao {

	List<UserProfile> findAll();

	UserProfile findByType(String type);

	UserProfile findById(int id);
}
package com.yiibai.springmvc.dao;

import java.io.Serializable;

import java.lang.reflect.ParameterizedType;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired; public abstract class AbstractDao<PK extends Serializable, T> { private final Class<T> persistentClass; @SuppressWarnings("unchecked")
public AbstractDao(){
this.persistentClass =(Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass ()).getActualTypeArguments()[1];
} @Autowired
private SessionFactory sessionFactory; protected Session getSession(){
return sessionFactory.getCurrentSession();
} @SuppressWarnings("unchecked")
public T getByKey(PK key) {
return (T) getSession().get(persistentClass, key);
} public void persist(T entity) {
getSession().persist(entity);
} public void delete(T entity) {
getSession().delete(entity);
} protected Criteria createEntityCriteria(){
return getSession().createCriteria(persistentClass);
} }
package com.yiibai.springmvc.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository; import com.yiibai.springmvc.model.User; @Repository("userDao")
public class UserDaoImpl extends AbstractDao<Integer, User> implements UserDao { public User findById(int id) {
User user = getByKey(id);
if(user!=null){
Hibernate.initialize(user.getUserProfiles());
}
return user;
} public User findBySSO(String sso) {
System.out.println("SSO : "+sso);
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("ssoId", sso));
User user = (User)crit.uniqueResult();
if(user!=null){
Hibernate.initialize(user.getUserProfiles());
}
return user;
} @SuppressWarnings("unchecked")
public List<User> findAllUsers() {
Criteria criteria = createEntityCriteria().addOrder(Order.asc("firstName"));
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);//To avoid duplicates.
List<User> users = (List<User>) criteria.list(); // No need to fetch userProfiles since we are not showing them on list page. Let them lazy load.
// Uncomment below lines for eagerly fetching of userProfiles if you want.
/*
for(User user : users){
Hibernate.initialize(user.getUserProfiles());
}*/
return users;
} public void save(User user) {
persist(user);
} public void deleteBySSO(String sso) {
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("ssoId", sso));
User user = (User)crit.uniqueResult();
delete(user);
} }
package com.yiibai.springmvc.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository; import com.yiibai.springmvc.model.UserProfile; @Repository("userProfileDao")
public class UserProfileDaoImpl extends AbstractDao<Integer, UserProfile>implements UserProfileDao{ public UserProfile findById(int id) {
return getByKey(id);
} public UserProfile findByType(String type) {
Criteria crit = createEntityCriteria();
crit.add(Restrictions.eq("type", type));
return (UserProfile) crit.uniqueResult();
} @SuppressWarnings("unchecked")
public List<UserProfile> findAll(){
Criteria crit = createEntityCriteria();
crit.addOrder(Order.asc("type"));
return (List<UserProfile>)crit.list();
} }
第6步:创建服务层
package com.yiibai.springmvc.service;

import java.util.List;
import com.yiibai.springmvc.model.UserProfile; public interface UserProfileService { UserProfile findById(int id); UserProfile findByType(String type); List<UserProfile> findAll(); }
package com.yiibai.springmvc.service;

import java.util.List;
import com.yiibai.springmvc.model.User; public interface UserService { User findById(int id); User findBySSO(String sso); void saveUser(User user); void updateUser(User user); void deleteUserBySSO(String sso); List<User> findAllUsers(); boolean isUserSSOUnique(Integer id, String sso); }
package com.yiibai.springmvc.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.yiibai.springmvc.dao.UserProfileDao;
import com.yiibai.springmvc.model.UserProfile; @Service("userProfileService")
@Transactional
public class UserProfileServiceImpl implements UserProfileService{ @Autowired
UserProfileDao dao; public UserProfile findById(int id) {
return dao.findById(id);
} public UserProfile findByType(String type){
return dao.findByType(type);
} public List<UserProfile> findAll() {
return dao.findAll();
}
}
package com.yiibai.springmvc.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import com.yiibai.springmvc.dao.UserDao;
import com.yiibai.springmvc.model.User; @Service("userService")
@Transactional
public class UserServiceImpl implements UserService{ @Autowired
private UserDao dao; public User findById(int id) {
return dao.findById(id);
} public User findBySSO(String sso) {
User user = dao.findBySSO(sso);
return user;
} public void saveUser(User user) {
dao.save(user);
} /*
* Since the method is running with Transaction, No need to call hibernate update explicitly.
* Just fetch the entity from db and update it with proper values within transaction.
* It will be updated in db once transaction ends.
*/
public void updateUser(User user) {
User entity = dao.findById(user.getId());
if(entity!=null){
entity.setSsoId(user.getSsoId());
entity.setPassword(user.getPassword());
entity.setFirstName(user.getFirstName());
entity.setLastName(user.getLastName());
entity.setEmail(user.getEmail());
entity.setUserProfiles(user.getUserProfiles());
}
} public void deleteUserBySSO(String sso) {
dao.deleteBySSO(sso);
} public List<User> findAllUsers() {
return dao.findAllUsers();
} public boolean isUserSSOUnique(Integer id, String sso) {
User user = findBySSO(sso);
return ( user == null || ((id != null) && (user.getId() == id)));
} }
第7步:创建Hibernate配置
package com.yiibai.springmvc.configuration;

import java.util.Properties;
import javax.sql.DataSource; import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.hibernate4.HibernateTransactionManager;
import org.springframework.orm.hibernate4.LocalSessionFactoryBean;
import org.springframework.transaction.annotation.EnableTransactionManagement; @Configuration
@EnableTransactionManagement
@ComponentScan({ "com.yiibai.springmvc.configuration" })
@PropertySource(value = { "classpath:application.properties" })
public class HibernateConfiguration { @Autowired
private Environment environment; @Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setPackagesToScan(new String[] { "com.yiibai.springmvc.model" });
sessionFactory.setHibernateProperties(hibernateProperties());
return sessionFactory;
} @Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(environment.getRequiredProperty("jdbc.driverClassName"));
dataSource.setUrl(environment.getRequiredProperty("jdbc.url"));
dataSource.setUsername(environment.getRequiredProperty("jdbc.username"));
dataSource.setPassword(environment.getRequiredProperty("jdbc.password"));
return dataSource;
} private Properties hibernateProperties() {
Properties properties = new Properties();
properties.put("hibernate.dialect", environment.getRequiredProperty("hibernate.dialect"));
properties.put("hibernate.show_sql", environment.getRequiredProperty("hibernate.show_sql"));
properties.put("hibernate.format_sql", environment.getRequiredProperty("hibernate.format_sql"));
return properties;
} @Bean
@Autowired
public HibernateTransactionManager transactionManager(SessionFactory s) {
HibernateTransactionManager txManager = new HibernateTransactionManager();
txManager.setSessionFactory(s);
return txManager;
}
}
以上 Hibernate 的配置使用下面提到 application.properties
jdbc.driverClassName = com.mysql.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/yiibai
jdbc.username = myuser
jdbc.password = passwd123
hibernate.dialect = org.hibernate.dialect.MySQLDialect
hibernate.show_sql = true
hibernate.format_sql = true
第8步:创建控制器
package com.yiibai.springmvc.controller;

import java.util.List;
import java.util.Locale; import javax.validation.Valid; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes; import com.yiibai.springmvc.model.User;
import com.yiibai.springmvc.model.UserProfile;
import com.yiibai.springmvc.service.UserProfileService;
import com.yiibai.springmvc.service.UserService; @Controller
@RequestMapping("/")
@SessionAttributes("roles")
public class AppController { @Autowired
UserService userService; @Autowired
UserProfileService userProfileService; @Autowired
MessageSource messageSource; /**
* This method will list all existing users.
*/
@RequestMapping(value = { "/", "/list" }, method = RequestMethod.GET)
public String listUsers(ModelMap model) { List<User> users = userService.findAllUsers();
model.addAttribute("users", users);
return "userslist";
} /**
* This method will provide the medium to add a new user.
*/
@RequestMapping(value = { "/newuser" }, method = RequestMethod.GET)
public String newUser(ModelMap model) {
User user = new User();
model.addAttribute("user", user);
model.addAttribute("edit", false);
return "registration";
} /**
* This method will be called on form submission, handling POST request for
* saving user in database. It also validates the user input
*/
@RequestMapping(value = { "/newuser" }, method = RequestMethod.POST)
public String saveUser(@Valid User user, BindingResult result,
ModelMap model) { if (result.hasErrors()) {
return "registration";
} /*
* Preferred way to achieve uniqueness of field [sso] should be implementing custom @Unique annotation
* and applying it on field [sso] of Model class [User].
*
* Below mentioned peace of code [if block] is to demonstrate that you can fill custom errors outside the validation
* framework as well while still using internationalized messages.
*
*/
if(!userService.isUserSSOUnique(user.getId(), user.getSsoId())){
FieldError ssoError =new FieldError("user","ssoId",messageSource.getMessage("non.unique.ssoId", new String[]{user.getSsoId()}, Locale.getDefault()));
result.addError(ssoError);
return "registration";
} userService.saveUser(user); model.addAttribute("success", "User " + user.getFirstName() + " "+ user.getLastName() + " registered successfully");
//return "success";
return "registrationsuccess";
} /**
* This method will provide the medium to update an existing user.
*/
@RequestMapping(value = { "/edit-user-{ssoId}" }, method = RequestMethod.GET)
public String editUser(@PathVariable String ssoId, ModelMap model) {
User user = userService.findBySSO(ssoId);
model.addAttribute("user", user);
model.addAttribute("edit", true);
return "registration";
} /**
* This method will be called on form submission, handling POST request for
* updating user in database. It also validates the user input
*/
@RequestMapping(value = { "/edit-user-{ssoId}" }, method = RequestMethod.POST)
public String updateUser(@Valid User user, BindingResult result,
ModelMap model, @PathVariable String ssoId) { if (result.hasErrors()) {
return "registration";
} /*//Uncomment below 'if block' if you WANT TO ALLOW UPDATING SSO_ID in UI which is a unique key to a User.
if(!userService.isUserSSOUnique(user.getId(), user.getSsoId())){
FieldError ssoError =new FieldError("user","ssoId",messageSource.getMessage("non.unique.ssoId", new String[]{user.getSsoId()}, Locale.getDefault()));
result.addError(ssoError);
return "registration";
}*/ userService.updateUser(user); model.addAttribute("success", "User " + user.getFirstName() + " "+ user.getLastName() + " updated successfully");
return "registrationsuccess";
} /**
* This method will delete an user by it's SSOID value.
*/
@RequestMapping(value = { "/delete-user-{ssoId}" }, method = RequestMethod.GET)
public String deleteUser(@PathVariable String ssoId) {
userService.deleteUserBySSO(ssoId);
return "redirect:/list";
} /**
* This method will provide UserProfile list to views
*/
@ModelAttribute("roles")
public List<UserProfile> initializeProfiles() {
return userProfileService.findAll();
} }
在下面提到的 messages.properties 文件中定义消息
NotEmpty.user.firstName=First name can not be blank.
NotEmpty.user.lastName=Last name can not be blank.
NotEmpty.user.email=Email can not be blank.
NotEmpty.user.password=Password can not be blank.
NotEmpty.user.ssoId=SSO ID can not be blank.
NotEmpty.user.userProfiles=At least one profile must be selected.
non.unique.ssoId=SSO ID {0} already exist. Please fill in different value.
第9步:创建转换器
这是这篇文章的核心。是需要映射单个 userProfile 的ID在实际的 UserProfile 实体在数据库中。
package com.yiibai.springmvc.converter;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component; import com.yiibai.springmvc.model.UserProfile;
import com.yiibai.springmvc.service.UserProfileService; /**
* A converter class used in views to map id's to actual userProfile objects.
*/
@Component
public class RoleToUserProfileConverter implements Converter<Object, UserProfile>{ @Autowired
UserProfileService userProfileService; /**
* Gets UserProfile by Id
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
*/
public UserProfile convert(Object element) {
Integer id = Integer.parseInt((String)element);
UserProfile profile= userProfileService.findById(id);
System.out.println("Profile : "+profile);
return profile;
} }
第10步、创建Spring配置文件
package com.yiibai.springmvc.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView; import com.yiibai.springmvc.converter.RoleToUserProfileConverter; @Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.yiibai.springmvc")
public class AppConfig extends WebMvcConfigurerAdapter{ @Autowired
RoleToUserProfileConverter roleToUserProfileConverter; /**
* Configure ViewResolvers to deliver preferred views.
*/
@Override
public void configureViewResolvers(ViewResolverRegistry registry) { InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
registry.viewResolver(viewResolver);
} /**
* Configure ResourceHandlers to serve static resources like CSS/ Javascript etc...
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
} /**
* Configure Converter to be used.
* In our example, we need a converter to convert string values[Roles] to UserProfiles in newUser.jsp
*/
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(roleToUserProfileConverter);
} /**
* Configure MessageSource to lookup any validation/error message in internationalized property files
*/
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
} /**Optional. It's only required when handling '.' in @PathVariables which otherwise ignore everything after last '.' in @PathVaidables argument.
* It's a known bug in Spring [https://jira.spring.io/browse/SPR-6164], still present in Spring 4.1.7.
* This is a workaround for this issue.
*/
@Override
public void configurePathMatch(PathMatchConfigurer matcher) {
matcher.setUseRegisteredSuffixPatternMatch(true);
}

第一个有趣的事情是注册转换器,我们在上一步中使用addFormatters创建了Spring配置。其次是方法configurePathMatch它提供了一个解决方法(虽然其他解决方法存在)在Spring中是一个已知的错误,这仍然在Spring4.1.7.RELEASE中有发现。

上述XML配置转换器设置为:
	<mvc:annotation-driven conversion-service="conversionService"/>

	<bean id="conversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean">

		<property name="converters">
<list>
<bean id="roleToUserProfile" class="com.yiibai.springsecurity.configuration.RoleToUserProfileConverter" />
</list>
</property>
</bean>
添加初始化类:
package com.yiibai.springmvc.configuration;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
} @Override
protected Class<?>[] getServletConfigClasses() {
return null;
} @Override
protected String[] getServletMappings() {
return new String[] { "/" };
} }
第11步:添加视图/ JSP
请注意,我们使用 Bootstrap 样式在JSP中。

userslist.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html> <head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Users List</title>
<link href="<c:url value='/static/css/bootstrap.css' />" rel="stylesheet"></link>
<link href="<c:url value='/static/css/app.css' />" rel="stylesheet"></link>
</head> <body>
<div class="generic-container">
<div class="panel panel-default">
<!-- Default panel contents -->
<div class="panel-heading"><span class="lead">List of Users </span></div>
<table class="table table-hover">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
<th>SSO ID</th>
<th width="100"></th>
<th width="100"></th>
</tr>
</thead>
<tbody>
<c:forEach items="${users}" var="user">
<tr>
<td>${user.firstName}</td>
<td>${user.lastName}</td>
<td>${user.email}</td>
<td>${user.ssoId}</td>
<td><a href="<c:url value='/edit-user-${user.ssoId}' />" class="btn btn-success custom-width">edit</a></td>
<td><a href="<c:url value='/delete-user-${user.ssoId}' />" class="btn btn-danger custom-width">delete</a></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
<div class="well">
<a href="<c:url value='/newuser' />">Add New User</a>
</div>
</div>
</body>
</html>

registration.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>User Registration Form</title>
<link href="<c:url value='/static/css/bootstrap.css' />" rel="stylesheet"></link>
<link href="<c:url value='/static/css/app.css' />" rel="stylesheet"></link>
</head> <body> <div class="generic-container">
<div class="well lead">User Registration Form</div>
<form:form method="POST" modelAttribute="user" class="form-horizontal">
<form:input type="hidden" path="id" id="id"/> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="firstName">First Name</label>
<div class="col-md-7">
<form:input type="text" path="firstName" id="firstName" class="form-control input-sm"/>
<div class="has-error">
<form:errors path="firstName" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="lastName">Last Name</label>
<div class="col-md-7">
<form:input type="text" path="lastName" id="lastName" class="form-control input-sm" />
<div class="has-error">
<form:errors path="lastName" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="ssoId">SSO ID</label>
<div class="col-md-7">
<c:choose>
<c:when test="${edit}">
<form:input type="text" path="ssoId" id="ssoId" class="form-control input-sm" disabled="true"/>
</c:when>
<c:otherwise>
<form:input type="text" path="ssoId" id="ssoId" class="form-control input-sm" />
<div class="has-error">
<form:errors path="ssoId" class="help-inline"/>
</div>
</c:otherwise>
</c:choose>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="password">Password</label>
<div class="col-md-7">
<form:input type="password" path="password" id="password" class="form-control input-sm" />
<div class="has-error">
<form:errors path="password" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="email">Email</label>
<div class="col-md-7">
<form:input type="text" path="email" id="email" class="form-control input-sm" />
<div class="has-error">
<form:errors path="email" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-group col-md-12">
<label class="col-md-3 control-lable" for="userProfiles">Roles</label>
<div class="col-md-7">
<form:select path="userProfiles" items="${roles}" multiple="true" itemValue="id" itemLabel="type" class="form-control input-sm" />
<div class="has-error">
<form:errors path="userProfiles" class="help-inline"/>
</div>
</div>
</div>
</div> <div class="row">
<div class="form-actions floatRight">
<c:choose>
<c:when test="${edit}">
<input type="submit" value="Update" class="btn btn-primary btn-sm"/> or <a href="<c:url value='/list' />">Cancel</a>
</c:when>
<c:otherwise>
<input type="submit" value="Register" class="btn btn-primary btn-sm"/> or <a href="<c:url value='/list' />">Cancel</a>
</c:otherwise>
</c:choose>
</div>
</div>
</form:form>
</div>
</body>
</html>

registrationsuccess.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Registration Confirmation Page</title>
<link href="<c:url value='/static/css/bootstrap.css' />" rel="stylesheet"></link>
<link href="<c:url value='/static/css/app.css' />" rel="stylesheet"></link>
</head>
<body>
<div class="generic-container">
<div class="alert alert-success lead">
${success}
</div> <span class="well floatRight">
Go to <a href="<c:url value='/list' />">Users List</a>
</span>
</div>
</body> </html>
这里是一个小自定义样式表文件:

app.css

body, #mainWrapper {
height: 100%;
background-color:rgb(245, 245, 245);
} body, .form-control{
font-size:14px!important;
} .floatRight{
float:right;
margin-right: 18px;
} .has-error{
color:red;
} .generic-container {
position:fixed;
width:80%;
margin-left: 20px;
margin-top: 20px;
margin-bottom: 20px;
padding: 20px;
background-color: #EAE7E7;
border: 1px solid #ddd;
border-radius: 4px;
box-shadow: 0 0 30px black;
} .custom-width {
width: 80px !important;
}
第12步:构建,部署和运行应用程序

现在构建War(前面提到的Eclipse教程)或通过Maven的命令行( mvn clean install). 部署 war 到Servlet3.0容器。

打开浏览器,浏览URL => http://localhost:8080/SpringMVCMany2ManyCRUD/

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

点击 ‘Add New User’

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

直接提交而不填写任何东西。

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

详细填写信息:

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

Submit.

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

点击 ‘Users List’ 链接:

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

查看数据库,三张表的数据结果如下。

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

添加更多的用户信息:

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

点击用户"Si"编辑按钮。修改他的角色为Admin。

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例
提交,如下结果显示:

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

查看数据库,现在存储的数据如下:

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

现在回到列表,点击删除“Si”用户。

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

最后,查看数据库,这里结果如下所示:

Spring4 MVC+Hibernate4 Many-to-many连接表+MySQL+Maven实例

上一篇:20135327郭皓--Linux内核分析第六周 进程的描述和进程的创建


下一篇:CSS/HTML 改变鼠标指针形状