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

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

第3步:更新pom.xml,包括所需的依賴關係


4.0.0
com.yiibai.springmvc
SpringMVCMany2ManyCRUD
war
1.0.0
SpringMVCHibernateManyToManyCRUDExample

  <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>

第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" %>

Users List
List of Users
Firstname Lastname Email SSO ID
${user.firstName} ${user.lastName} ${user.email} ${user.ssoId}
Add New User

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" %>

User Registration Form
 <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:otherwise>
<form:input type="text" path="ssoId" id="ssoId" class="form-control

input-sm" />


<form:errors path="ssoId" class="help-inline"/>





    <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" 

/>


<form:errors path="password" class="help-inline"/>



    <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" />


<form:errors path="userProfiles" class="help-inline"/>



    <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

<c:otherwise>
or <a

href="<c:url value='/list' />">Cancel





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" %>

Registration Confirmation Page
${success}
<span class="well floatRight">
    Go to <a href="<c:url value='/list' />">Users List</a>
</span>

這裏是一個小自定義樣式表文件:

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

點擊 ‘Add New User’

Spring4

直接提交而不填寫任何東西。

Spring4

詳細填寫信息:

Spring4

Submit.

Spring4

點擊 ‘Users List’ 鏈接:

Spring4

查看數據庫,三張表的數據結果如下。

Spring4

添加更多的用戶信息:

Spring4

點擊用戶"Si"編輯按鈕。修改他的角色爲Admin。

Spring4
提交,如下結果顯示:

Spring4

查看數據庫,現在存儲的數據如下:

Spring4

現在回到列表,點擊刪除「Si」用戶。

Spring4

最後,查看數據庫,這裏結果如下所示:

Spring4

到這裏,整個教程介紹完畢,包教不包會!

下載代碼:http://pan.baidu.com/s/1mhy2XwG


Copyright © 2015-2023 億聚網.