ssh全注解框架整合

1、ssh框架开发的应用层级结构

j2ee应用可以分为3层:



1、表示层


2、业务逻辑层


3、数据服务层



ssh将应用分层更加细化(ssh将业务逻辑层划分了4层):



1、action层(控制层mvc中的c层)


2、service层  (业务层mvc中的m层)


3、dao层(数据访问层)

4、entity层(数据实体层)

2、ssh中各个框架的作用

strust:

整个应用架构的骨架,负责应用中mvc模型的实现,向下层传递前端请求,向上层传递服务响应,主要作用于action层;

spring:

主要作用是依赖注入和控制反转以达到解耦的目的,用于对整个应用中的bean实例进行管理,作用于ssh各个层;

hibernate:

主要用于数据库操作,提供了便捷的数据库操作框架,主要作用于dao层;

3、ssh全注解框架搭建

1、创建web项目并勾选web.xml

2、导入ssh所需jar

        


可以去官网下载ssh所需jar包或者在我的资源中下载(解压后的lib文件夹中):http://download.csdn.net/download/duoluo9/9990702

3、配置web.xml

<?xmlversion="1.0"encoding="UTF-8"?>

<web-appxmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://java.sun.com/xml/ns/javaee"xsi:schemaLocation="http://java.sun.com/xml/ns/javaeehttp://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"id="WebApp_ID"version="2.5">

 <display-name>demo1</display-name>

 <welcome-file-list>

   <welcome-file>index.html</welcome-file>

   <welcome-file>index.htm</welcome-file>

   <welcome-file>index.jsp</welcome-file>

   <welcome-file>default.html</welcome-file>

   <welcome-file>default.htm</welcome-file>

   <welcome-file>default.jsp</welcome-file>

 </welcome-file-list>

 

 <filter>

   <filter-name>action2</filter-name>

   <filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>

   <!-- 自动扫描action --> 

   <init-param> 

     <param-name>actionPackages</param-name> 

     <param-value>ssh.action</param-value> 

   </init-param>   

 </filter>

 <filter-mapping>

   <filter-name>action2</filter-name>

   <url-pattern>/*</url-pattern>

 </filter-mapping>

 <!-- spring监听器配置开始 -->

 <context-param>

   <param-name>contextConfigLocation</param-name>

   <param-value>classpath:applicationContext.xml</param-value>

 </context-param>

 <listener>

   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

 </listener>

</web-app>

4、配置spring的applicationContext.xml

<?xmlversion="1.0"encoding="UTF-8"?>

<beansxmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-3.0.xsd     

                http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-3.0.xsd     

               http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

 

<!--    <bean id="northMan"class="ssh.entity.NorthMan"></bean>

    <beanid="southMan" class="ssh.entity.SouthMan"></bean>

 

 -->

    <!-- 自动扫描与装配bean -->

    <context:component-scanbase-package="ssh.*"></context:component-scan>

    <context:annotation-config/>

    <!-- dbcp配置 -->

    <beanid="dataSource"class="org.apache.commons.dbcp2.BasicDataSource"

        destroy-method="close">

        <propertyname="driverClassName">

            <value>com.mysql.jdbc.Driver</value>

        </property>

        <propertyname="url">

            <value>jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&amp;characterEncoding=utf-8&amp;useSSL=false</value>

        </property>

        <propertyname="username">

            <value>root</value>

        </property>

        <propertyname="password">

            <value>root</value>

        </property>

    </bean>

 

    <beanid="sessionFactory"

        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">

        <propertyname="dataSource">

            <reflocal="dataSource"/>

        </property>

        <propertyname="hibernateProperties">

            <props>

                <!--配置Hibernate的方言 -->

                <propkey="hibernate.dialect">

                    org.hibernate.dialect.MySQLDialect

                </prop>

                <propkey="hibernate.hbm2ddl.auto">update</prop>

 

                <!--格式化输出sql语句 -->

                <propkey="hibernate.show_sql">true</prop>

                <propkey="hibernate.format_sql">true</prop>

                <propkey="hibernate.use_sql_comments">false</prop>

            </props>

        </property>

 

        <!--自动扫描实体 -->

        <propertyname="packagesToScan"value="ssh.entity"/>

    </bean>

 

    <!-- 用注解来实现事务管理 -->

    <beanid="txManager"

        class="org.springframework.orm.hibernate4.HibernateTransactionManager">

        <propertyname="sessionFactory"ref="sessionFactory"></property>

    </bean>

    <tx:annotation-driventransaction-manager="txManager"/>

</beans>  



4、ssh中用到的注解标签(来源于网络)

strust:



命名空间注释(动作注释):


@ Namespace注释允许在Action类中,而不是基于零配置的约定动作的命名空间的定义。


@Namespace("/content")
public class Employee extends ActionSupport{
 ...
}
结果注释 - (动作译注):


@ Result注解允许在Action类中,而不是一个XML文件中定义的动作结果。


@Result(name="success", value="/success.jsp")
public class Employee extends ActionSupport{
 ...
}
结果注释 - (动作译注):


@ Results注解定义了一套动作的结果。


@Results({
  @Result(name="success", value="/success.jsp"),
  @Result(name="error", value="/error.jsp")
})
public class Employee extends ActionSupport{
 ...
}
注释后(拦截注释):


@After注解标志着一个需要调用后的主要操作方法和执行结果的操作方法。返回值将被忽略。


public class Employee extends ActionSupport{
  @After
  public void isValid() throws ValidationException {
   // validate model object, throw exception if failed
  }
  public String execute() {
   // perform secure action
   return SUCCESS;
  }
}
注释之前(拦截注释):


@ Before注释标记需要一个操作方法的主要操作方法之前被调用执行结果。返回值将被忽略。


public class Employee extends ActionSupport{
  @Before
  public void isAuthorized() throws AuthenticationException {
   // authorize request, throw exception if failed
  }
  public String execute() {
   // perform secure action
   return SUCCESS;
  }
}
BeforeResult注释 - (拦截注释):


@ BeforeResult注解标志着一个结果之前需要执行的操作方法。返回值将被忽略。


public class Employee extends ActionSupport{
  @BeforeResult
  public void isValid() throws ValidationException {
  // validate model object, throw exception if failed
  }
 
  public String execute() {
   // perform action
   return SUCCESS;
  }
}
ConversionErrorFieldValidator注释 - (验证译注):


此验证注解如果有任何转换错误进行了实地检查,并适用于他们,如果他们存在。


public class Employee extends ActionSupport{
  @ConversionErrorFieldValidator(message = "Default message", 
            key = "i18n.key", shortCircuit = true)
  public String getName() {
    return name;
  }
}
DateRangeFieldValidator注释 - (验证译注):


这验证注解检查日期字段的值在指定范围内。


public class Employee extends ActionSupport{
  @DateRangeFieldValidator(message = "Default message", 
  key = "i18n.key", shortCircuit = true, 
  min = "2005/01/01", max = "2005/12/31")
  public String getDOB() {
    return dob;
  }
}
DoubleRangeFieldValidator注释 - (验证译注):


此验证注解检查双字段有一个值,该值在指定范围内。如果既不最小或最大,什么都不会做的。


public class Employee extends ActionSupport{
 
  @DoubleRangeFieldValidator(message = "Default message", 
  key = "i18n.key", shortCircuit = true, 
  minInclusive = "0.123", maxInclusive = "99.987")
  public String getIncome() {
    return income;
  }
}
EmailValidator注释 - (验证译注):


这验证注解检查一个字段是一个有效的E-mail地址,如果它包含一个非空的字符串。


public class Employee extends ActionSupport{
 
  @EmailValidator(message = "Default message", 
  key = "i18n.key", shortCircuit = true)
  public String getEmail() {
    return email;
  }
}
ExpressionValidator注释 - (验证译注):


这种非字段级验证验证所提供的正则表达式。


@ExpressionValidator(message = "Default message", key = "i18n.key", 


shortCircuit = true, expression = "an OGNL expression" )


IntRangeFieldValidator注释 - (验证译注):


这验证注解检查一个数字字段的值在指定的范围内。如果既不最小或最大,什么都不会做的。


public class Employee extends ActionSupport{
 
  @IntRangeFieldValidator(message = "Default message", 
  key = "i18n.key", shortCircuit = true, 
  min = "0", max = "42")
  public String getAge() {
    return age;
  }
}
RegexFieldValidator 注释 - (验证译注):


这个注解验证一个字符串字段,使用正则表达式。


@RegexFieldValidator( key = "regex.field", expression = "yourregexp")


RequiredFieldValidator 注释 - (验证译注):


这验证注解检查一个字段不为空。标注必须被应用在方法层面。


public class Employee extends ActionSupport{
 
  @RequiredFieldValidator(message = "Default message", 
  key = "i18n.key", shortCircuit = true)
  public String getAge() {
    return age;
  }
}
RequiredStringValidator注释 - (验证译注):


这验证注解检查一个字符串字段不为空(即非空,长度> 0)。


public class Employee extends ActionSupport{
 
  @RequiredStringValidator(message = "Default message", 
  key = "i18n.key", shortCircuit = true, trim = true)
  public String getName() {
    return name;
  }
}
StringLengthFieldValidator注释 - (验证译注):


这个验证检查字符串字段是合适的长度。假定该字段是一个字符串。如果设置既不是minLength 也不是最大长度,什么都不会做。


public class Employee extends ActionSupport{
 
  @StringLengthFieldValidator(message = "Default message", 
  key = "i18n.key", shortCircuit = true, 
  trim = true, minLength = "5", maxLength = "12")
  public String getName() {
    return name;
  }
}
UrlValidator注释 - (验证译注):


这个验证检查一个字段是一个有效的URL。


public class Employee extends ActionSupport{
 
  @UrlValidator(message = "Default message", 
  key = "i18n.key", shortCircuit = true)
  public String getURL() {
    return url;
  }
}
验证注释 - (验证译注):


如果想使用多个相同类型的注释,这些注释必须嵌套在@Validations() 注释。


public class Employee extends ActionSupport{
 
 @Validations(
  requiredFields =
   {@RequiredFieldValidator(type = ValidatorType.SIMPLE, 
   fieldName = "customfield", 
   message = "You must enter a value for field.")},
  requiredStrings =
   {@RequiredStringValidator(type = ValidatorType.SIMPLE, 
   fieldName = "stringisrequired", 
   message = "You must enter a value for string.")}
  )
  public String getName() {
    return name;
  }
}
CustomValidator注释 - (验证译注):


这个注解可以用于自定义验证。使用ValidationParameter的注释,以提供额外的 params.


@CustomValidator(type ="customValidatorName", fieldName = "myField")
转换注释 - (类型转换注释):


这是一个标记注释类型转换类型级别。转换注释必须应用在类型级别。


@Conversion()
  public class ConversionAction implements Action {
}
CreateIfNull注释 - (类型转换注释):


这个注解设置类型转换CreateIfNull。必须应用在域或方法级CreateIfNull注解。


@CreateIfNull( value = true )
private List<User> users;
元素注释 - (类型转换注释):


这个注解设置元素进行类型转换。必须应用在字段域或方法级元素的注解。


@Element( value = com.acme.User )
private List<User> userList;
关键注释 - (类型转换注释):


这个注解设置进行类型转换的关键。必须应用在域或方法级的关键注解。


@Key( value = java.lang.Long.class )
private Map<Long, User> userMap;
KeyProperty注释 - (类型转换注释):


这个注解设置类型转换KeyProperty。必须应用在域或方法级KeyProperty注解。


@KeyProperty( value = "userName" )
protected List<User> users = null;
TypeConversion注释 - (类型转换注释):


这个注解的注解是用于类和应用程序的转换规则。注解可以应用于TypeConversion在属性和方法的级别。


@TypeConversion(rule = ConversionRule.COLLECTION, 
converter = "java.util.String")
public void setUsers( List users ) {
  this.users = users;
}


spring :

使用 @Repository、@Service、@Controller 和 @Component 将类标识为 Bean
使用 @PostConstruct 和 @PreDestroy 指定生命周期回调方法
使用 @Required 进行 Bean 的依赖检查
使用 @Resource、@Autowired 和 @Qualifier 指定 Bean 的自动装配策略
使用 @Configuration 和 @Bean 进行 Bean 的声明
混合使用 XML 与注解进行 Bean 的配置


hibernate:



@Entity --注释声明该类为持久类。将一个Javabean类声明为一个实体的数据库表映射类,最好实现序列化.此时,默认情况下,所有的类属性都为映射到数据表的持久性字段.若在类中,添加另外属性,而非映射来数据库的,要用下面的Transient来注解.


@Table(name="promotion_info") --持久性映射的表(表名="promotion_info).@Table是类一级的注解,定义在@Entity下,为实体bean映射表,目录和schema的名字,默认为实体bean的类名,不带包名.


@Id--注释可以表明哪种属性是该类中的独特标识符(即相当于数据表的主键)。 
@GeneratedValue --定义自动增长的主键的生成策略. 
@Transient --将忽略这些字段和属性,不用持久化到数据库.适用于,在当前的持久类中,某些属性不是用于映射到数据表,而是用于其它的业务逻辑需要,这时,须将这些属性进行transient的注解.否则系统会因映射不到数据表相应字段而出错. 
@Temporal(TemporalType.TIMESTAMP)--声明时间格式 
@Enumerated --声明枚举 
@Version --声明添加对乐观锁定的支持 
@OneToOne --可以建立实体bean之间的一对一的关联 
@OneToMany --可以建立实体bean之间的一对多的关联 
@ManyToOne --可以建立实体bean之间的多对一的关联 
@ManyToMany --可以建立实体bean之间的多对多的关联 
@Formula --一个SQL表达式,这种属性是只读的,不在数据库生成属性(可以使用sum、average、max等) 
@OrderBy --Many端某个字段排序(List)
1.2
Hibernate 能够出色地自动生成主键。Hibernate/EBJ 3 注释也可以为主键的自动生成提供丰富的支持,允许实现各种策略。
其生成规则由@GeneratedValue设定的.这里的@id和@GeneratedValue都是JPA的标准用法, JPA提供四种标准用法,由@GeneratedValue的源代码可以明显看出. 
JPA提供的四种标准用法为TABLE,SEQUENCE,IDENTITY,AUTO. 
TABLE:使用一个特定的数据库表格来保存主键。 
SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。 
IDENTITY:主键由数据库自动生成(主要是自动增长型) 
AUTO:主键由程序控制。 


在指定主键时,如果不指定主键生成策略,默认为AUTO。 
@Id
相当于
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
identity:
使用SQL Server 和 MySQL 的自增字段,这个方法不能放到 Oracle 中,Oracle 不支持自增字段,要设定sequence(MySQL 和 SQL Server 中很常用)。
Oracle就要采用sequence了.
同时,也可采用uuid,native等其它策略.(相关用法,上网查询)
[2]
第一个持久性类 
@Entity 
@Table(name="T_MODEL_PLANE") 
public class ModelPlane implements Serializable { 
@Id 
@Column(name="PLANE_ID") 
@GeneratedValue(strategy=GenerationType.AUTO) //注解于属性中 
/* 
对于oracle想使用各自的Sequence,设置如下: 
@GeneratedValue(strategy = GenerationType.AUTO,generator="PROMOTION_SEQ") 
@SequenceGenerator(name="PROMOTION_SEQ",sequenceName="PROMOTION_SEQ")
另外:
对于自动增长后,在数据表中的相应字段,要设置字段为auto_increment.
*/ 
private Long id; 


private String name;//注解写于getter方法之上.请见下. 


//DATE - java.sql.Date 
//TIME - java.sql.Time 
//TIMESTAMP - java.sql.Timestamp 
@Temporal(TemporalType.TIMESTAMP) 
@Column(name="start_time") 
private Date startTime; 


//显示0 隐藏1 
public static enum DisplayType {显示,隐藏} 
@Enumerated(value = EnumType.ORDINAL)//ORDINAL序数 
private DisplayType displayType = DisplayType.显示; 


//1.sql语句中的字段和表名都应该和数据库相应,而不是类中的字段, 
//若带有参数如la.id= id,这个=id才是类中属性 
//2.操作字段一定要用别名 
@Formula(select COUNT(la.id) from largess la) 
private int count; 


//注解于方法中 
@Column(name="PLANE_ID", length=80, nullable=true) //较详细定义 
public String getName() { 
return name; 

public void setName(String name) { 
this.name = name; 

其它的setter,getter省略...... 
}


该内容将映射到下表中: 
CREATE TABLE T_MODEL_PLANE 

PLANE_ID long, 
PLANE_NAME varchar 
其它字段省略... 
)


默认情况下,Hibernate 会将持久类以匹配的名称映射到表和字段中。例如,下例中,若不用注解,则会映射到如下一表中:
CREATE TABLE MODELPLANE
(
ID long,
NAME varchar


其它字段省略...
)


[3]
一对多注解:
1.
在一对多注解中,会用到:
"一"方:
@OneToMany --> mappedBy:"多"方的关联属性(被控方)
"多"方:
@ManyToOne --> @JoinColumn,"多"方定义的外键字段.
如数据表定义外键如下:
FOREIGN KEY (classid) REFERENCES classes(id)
则:
@JoinColumn(name="classid")
2.
在双向关联中,有且仅有一端作为主体(owner)端存在:主体端负责维护联接列(即更新),对于不需要维护这种关系的从表则通过mappedNy属性进行声明。mappedBy的值指向另一主体的关联属性。例子中,mappedBy的值为classes。
附加说明:
mappedBy相当于过去的inverse="true".
inverse=false的side(side其实是指inverse=false所位于的class元素)端有责任维护关系,而inverse=true端无须维护这些关系。
3.
cascade与fetch使用说明:
Cascade
CascadeType.PERSIST (级联新建) 
CascadeType.REMOVE (级联删除) 
CascadeType.REFRESH (级联刷新) 
CascadeType.MERGE (级联更新)中选择一个或多个。 
CascadeType.ALL
fetch属性:
关联关系获取方式,即是否采用延时加载。
LAZY(默认值)采用延时加载,查询数据时,不一起查询关联对象的数据。而是当访问关联对象时(如:getStudnets()时)才触发相应的查询操作,获取关联对象数据。
EAGER:是在查询数据时,也直接一起获取关联对象的数据。
package oneToMany; 
import java.util.Set; 
import javax.persistence.*; 
/* 
注意导入时,是导入:import javax.persistence.*; 
非导入org.hibernate的相关类:import org.hibernate.annotations.Entity; 
*/ 
@Entity 
@Table(name="classes") 
public class Classes implements Serializable { 
@Id 
@GeneratedValue(strategy=GenerationType.AUTO) 
private int id; 
private String name; 


@OneToMany(cascade=CascadeType.ALL,mappedBy="classes") 
private Set<Student> students; 
//getter,setter省略 





package oneToMany; 
import javax.persistence.*; 
@Entity 
@Table(name="student") 
public class Student implements Serializable { 
@Id 
@GeneratedValue(strategy=GenerationType.AUTO) 
private int sid; 


private String sname; 


//若有多个cascade,可以是:{CascadeType.PERSIST,CascadeType.MERGE} 
@ManyToOne(cascade={CascadeType.ALL}) 
@JoinColumn(name="classid") //student类中对应外键的属性:classid 
private Classes classes; 
//getter,setter省略 





public class TestOneToMany { 
/* 
CREATE TABLE student ( --要定义外键!!!!!!! 
`sid` double NOT NULL auto_increment, 
`classid` double NULL, 
`sname` varchar(255) NOT NULL, 
PRIMARY KEY (sid), 
INDEX par_ind (classid), 
FOREIGN KEY (classid) REFERENCES classes(id) ON DELETE CASCADE ON UPDATE CASCADE 
) ENGINE=InnoDB 
*/ 
public static void main(String[] args) throws SQLException 

try 

SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory(); 
Session session=sf.openSession(); 
Transaction tx=session.beginTransaction();
/*
因为mappedBy是定义在classes中,即classes类不负责维护级联关系.即维护者是student.所以,
1.要将clsses的数据,赋给student,即用student的setClasses()方法去捆定class数据;
2.在进行数据插入/更新session.save()/session.update()时,最后操作的是student.
*/
Classes classes=new Classes(); 
classes.setName("access"); 


Student st1=new Student(); 
st1.setSname("jason"); 
st1.setClasses(classes); 
session.save(st1); 


Student st2=new Student(); 
st2.setSname("hwj"); 
st2.setClasses(classes); 
session.save(st2); 
tx.commit();
/*
输出如下:
Hibernate: insert into classes (name) values (?)
Hibernate: insert into student (classid, sname) values (?, ?)
Hibernate: insert into student (classid, sname) values (?, ?)
*/


/*
因为一端维护关系另一端不维护关系的原因,我们必须注意避免在应用中用不维护关系的类(class)建立关系,因为这样建立的关系是不会在数据库中存储的。
如上的代码倒过来,则插入时,student的外键值为空.如下:
*/
// Student st1=new Student(); 
// st1.setSname("jason"); 
// session.save(st1); 
// 
// Student st2=new Student(); 
// st2.setSname("hwj"); 
// session.save(st2); 
// 
// Set<Student> students=new HashSet<Student>(); 
// students.add(st1); 
// students.add(st2); 
// 
// Classes classes=new Classes(); 
// classes.setName("access"); 
// classes.setStudents(students); 
// session.save(classes);
/*
输出如下:
Hibernate: insert into student (classid, sname) values (?, ?)
Hibernate: insert into student (classid, sname) values (?, ?)
Hibernate: insert into classes (name) values (?)
*/

catch(HibernateException e) 

e.printStackTrace(); 


}




[4]
多对多注解:
在多对多注解中,双方都采用@ManyToMany.
其中被控方,像一对多注解中设置一样,也要设置mappedBy.
其中主控方,不像一对多注解那样,采用@joinColumn,而是采用@joinTable.如下:
@JoinTable(name="j_student_course" ,joinColumns={@JoinColumn(name="sid")},inverseJoinColumns={@JoinColumn(name="cid")})
其中,
如上所说,mappedBy,相当于inverse="true".所以,在@joinTable中的inverseJoinColumns中定义的字段为mappedBy所在类的主键.
joinColumns定义的字段,就是当前类的主键.
@Entity 
@Table(name="jcourse") 
public class Jcourse { 
@Id 
@GeneratedValue(strategy=GenerationType.AUTO) 
private int cid; 
private String cname; 


@ManyToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE},fetch=FetchType.LAZY ,mappedBy="courses") 
private Set<Jstudent> students; 
//setter,getter省略.... 





@Entity 
@Table(name="jstudent") 
public class Jstudent { 
@Id 
@GeneratedValue(strategy=GenerationType.AUTO) 
private int sid; 


private String sname; 


@ManyToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE},fetch=FetchType.EAGER) 
//inverseJoinColumns中对应的id为以下属性course的对应id. 
@JoinTable(name="j_student_course" ,joinColumns={@JoinColumn(name="sid")},inverseJoinColumns={@JoinColumn(name="cid")}) 
private Set<Jcourse> courses; 
//setter,getter省略.... 





public class Test { 
public static void main(String[] args) { 
try 

SessionFactory sf = new AnnotationConfiguration().configure().buildSessionFactory(); 
Session session=sf.openSession(); 
Transaction tx=session.beginTransaction(); 


Jcourse course=new Jcourse(); 
course.setCname("jason-english"); 
session.save(course); //先各自保存. 


Jcourse course2=new Jcourse(); 
course2.setCname("herry-english"); 
session.save(course2); 


Set<Jcourse> courses=new HashSet<Jcourse>(); 
courses.add(course); 
courses.add(course2); 


Jstudent student=new Jstudent(); 
student.setSname("jason"); 
student.setCourses(courses); 


session.save(student);// 要用非mapby定义的类(studet)来作为主者(会控制级联关系),一对多,多对一也一样道理. 
//可以尝试反过来. 
tx.commit(); 

catch(HibernateException e) 

e.printStackTrace(); 


}

源码地址:

https://github.com/duoluo9/ssh-conformity


  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值