Mkyong-中文博客翻译-二-

Mkyong 中文博客翻译(二)

原文:Mkyong

协议:CC BY-NC-SA 4.0

休眠错误-初始会话工厂创建失败。Java . lang . noclassdeffounderror:com/m change/v2/c3p 0/data sources

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-errorinitial-sessionfactory-creation-failed-java-lang-noclassdeffounderror-commchangev2c3p0datasources/

Hibernate 的“C3P0”连接池错误,这是由于缺少依赖库–C3 P0 造成的。

 Initial SessionFactory creation failed.java.lang.NoClassDefFoundError: com/mchange/v2/c3p0/DataSources
Exception in thread "main" java.lang.ExceptionInInitializerError
	at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:19)
	at com.mkyong.persistence.HibernateUtil.<clinit>(HibernateUtil.java:8)
	at com.mkyong.common.App.main(App.java:11)
Caused by: java.lang.NoClassDefFoundError: com/mchange/v2/c3p0/DataSources
	at org.hibernate.connection.C3P0ConnectionProvider.configure(C3P0ConnectionProvider.java:154)
	at org.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:124)
	at org.hibernate.connection.ConnectionProviderFactory.newConnectionProvider(ConnectionProviderFactory.java:56)
	at org.hibernate.cfg.SettingsFactory.createConnectionProvider(SettingsFactory.java:410)
	at org.hibernate.cfg.SettingsFactory.buildSettings(SettingsFactory.java:62)
	at org.hibernate.cfg.Configuration.buildSettings(Configuration.java:2009)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1292)
	at com.mkyong.persistence.HibernateUtil.buildSessionFactory(HibernateUtil.java:13)
	... 2 more
Caused by: java.lang.ClassNotFoundException: com.mchange.v2.c3p0.DataSources
	at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:252)
	at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:320)
	... 10 more 

解决办法

你可以在这里下载图书馆-http://www.mchange.com/projects/c3p0/

或者

使用 Maven 的坐标

 <dependency>
		<groupId>c3p0</groupId>
		<artifactId>c3p0</artifactId>
		<version>0.9.1.2</version>
	</dependency> 

追踪

如何在 Hibernate 中配置 C3P0 连接池

hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190308011746/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

hibernate–获取策略示例

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-fetching-strategies-examples/

Hibernate 几乎没有抓取策略来优化 Hibernate 生成的 select 语句,因此它可以尽可能地高效。获取策略在映射关系中声明,以定义 Hibernate 如何获取其相关的集合和实体。

抓取策略

有四种抓取策略

1.fetch-"join" =禁用延迟加载,总是加载所有的集合和实体。
2。fetch-“select”(默认)=惰性加载所有集合和实体。
3。batch-size="N" =提取多达“N”个集合或实体,不记录
4。fetch-"subselect" =将其集合分组到一个 subselect 语句中。

关于详细的解释,你可以查看 Hibernate 文档

获取策略示例

这里有一个“一对多关系”的例子来演示获取策略。一只股票是属于许多股票的日常记录。

在 XML 文件中声明提取策略的示例

 ...
<hibernate-mapping>
    <class name="com.mkyong.common.Stock" table="stock">
        <set name="stockDailyRecords"  cascade="all" inverse="true" 
            table="stock_daily_record" batch-size="10" fetch="select">
            <key>
                <column name="STOCK_ID" not-null="true" />
            </key>
            <one-to-many class="com.mkyong.common.StockDailyRecord" />
        </set>
    </class>
</hibernate-mapping> 

在注释中声明提取策略的示例

 ...
@Entity
@Table(name = "stock", catalog = "mkyong")
public class Stock implements Serializable{
...
	@OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
	@Cascade(CascadeType.ALL)
	@Fetch(FetchMode.SELECT)
        @BatchSize(size = 10)
	public Set<StockDailyRecord> getStockDailyRecords() {
		return this.stockDailyRecords;
	}
...
} 

让我们探讨一下获取策略如何影响 Hibernate 生成的 SQL 语句。

1.fetch="select "或@Fetch(FetchMode。选择)

这是默认的获取策略。它支持所有相关集合的延迟加载。让我们看看这个例子…

 //call select from stock
Stock stock = (Stock)session.get(Stock.class, 114); 
Set sets = stock.getStockDailyRecords();

//call select from stock_daily_record
for ( Iterator iter = sets.iterator();iter.hasNext(); ) { 
      StockDailyRecord sdr = (StockDailyRecord) iter.next();
      System.out.println(sdr.getDailyRecordId());
      System.out.println(sdr.getDate());
} 

输出

 Hibernate: 
    select ...from mkyong.stock
    where stock0_.STOCK_ID=?

Hibernate: 
    select ...from mkyong.stock_daily_record
    where stockdaily0_.STOCK_ID=? 

Hibernate 生成了两条 select 语句

1.Select 语句检索股票记录-session . get(Stock . class,114)
2。选择其相关集合-sets . iterator()

2.fetch="join "或@Fetch(FetchMode。加入)

“连接”抓取策略将禁用所有相关集合的延迟加载。让我们看看这个例子…

 //call select from stock and stock_daily_record
Stock stock = (Stock)session.get(Stock.class, 114); 
Set sets = stock.getStockDailyRecords();

//no extra select
for ( Iterator iter = sets.iterator();iter.hasNext(); ) { 
      StockDailyRecord sdr = (StockDailyRecord) iter.next();
      System.out.println(sdr.getDailyRecordId());
      System.out.println(sdr.getDate());
} 

输出

 Hibernate: 
    select ...
    from
        mkyong.stock stock0_ 
    left outer join
        mkyong.stock_daily_record stockdaily1_ 
            on stock0_.STOCK_ID=stockdaily1_.STOCK_ID 
    where
        stock0_.STOCK_ID=? 

Hibernate 只生成一个 select 语句,它在股票初始化时检索所有相关的集合。–session . get(stock . class,114)

1.Select 语句来检索股票记录并外部联接其相关集合。

3.batch-size="10 "或@BatchSize(size = 10)

这种“批量”获取策略总是被许多 Hibernate 开发人员误解。让我们看看这里的误解概念…

 Stock stock = (Stock)session.get(Stock.class, 114); 
Set sets = stock.getStockDailyRecords();

for ( Iterator iter = sets.iterator();iter.hasNext(); ) { 
      StockDailyRecord sdr = (StockDailyRecord) iter.next();
      System.out.println(sdr.getDailyRecordId());
      System.out.println(sdr.getDate());
} 

您的预期结果是什么,是从集合中每次提取 10 条记录吗?参见
输出输出

 Hibernate: 
    select ...from mkyong.stock
    where stock0_.STOCK_ID=?

Hibernate: 
    select ...from mkyong.stock_daily_record
    where stockdaily0_.STOCK_ID=? 

批量大小在这里没有任何作用,它不是批量大小如何工作的。见此说法。

批量获取策略没有定义集合中有多少记录被加载。相反,它定义了应该加载多少个集合。

—重复 N 次,直到你记住这句话—

另一个例子

再看一个例子,你想把所有的股票记录及其相关的股票日报表(集合)一个一个打印出来。

 List<Stock> list = session.createQuery("from Stock").list();

for(Stock stock : list){

    Set sets = stock.getStockDailyRecords();

    for ( Iterator iter = sets.iterator();iter.hasNext(); ) { 
            StockDailyRecord sdr = (StockDailyRecord) iter.next();
            System.out.println(sdr.getDailyRecordId());
            System.out.println(sdr.getDate());
    }
} 
没有批量提取策略

输出

 Hibernate: 
    select ...
    from mkyong.stock stock0_

Hibernate: 
    select ...
    from mkyong.stock_daily_record stockdaily0_ 
    where stockdaily0_.STOCK_ID=?

Hibernate: 
    select ...
    from mkyong.stock_daily_record stockdaily0_ 
    where stockdaily0_.STOCK_ID=?

Keep repeat the select statements....depend how many stock records in your table. 

如果数据库中有 20 条股票记录,Hibernate 的默认获取策略将生成 20+1 条 select 语句并命中数据库。

1.Select 语句检索所有股票记录。
2。选择其相关集合
3。选择其相关收藏
4。选择其相关集合
…。
21。选择其相关集合

生成的查询效率不高,导致了严重的性能问题。

启用了 batch-size='10 '提取策略

让我们看另一个例子,batch-size='10 '是启用的。
输出

 Hibernate: 
    select ...
    from mkyong.stock stock0_

Hibernate: 
    select ...
    from mkyong.stock_daily_record stockdaily0_ 
    where
        stockdaily0_.STOCK_ID in (
            ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
        ) 

现在,Hibernate 将使用 select in语句预先获取集合。如果您有 20 条股票记录,它将生成 3 条 select 语句。

1.Select 语句检索所有股票记录。
2。Select In 语句预取其相关集合(一次 10 个集合)
3。Select In 语句预取其相关集合(一次 10 个集合)

启用批量大小后,它将 select 语句从 21 条 select 语句简化为 3 条 select 语句。

4.fetch="subselect "或@Fetch(FetchMode。子选择)

这种获取策略是在一个 sub select 语句中启用其所有相关集合。让我们再次看到相同的查询..

 List<Stock> list = session.createQuery("from Stock").list();

for(Stock stock : list){

    Set sets = stock.getStockDailyRecords();

    for ( Iterator iter = sets.iterator();iter.hasNext(); ) { 
            StockDailyRecord sdr = (StockDailyRecord) iter.next();
            System.out.println(sdr.getDailyRecordId());
            System.out.println(sdr.getDate());
    }
} 

输出

 Hibernate: 
    select ...
    from mkyong.stock stock0_

Hibernate: 
    select ...
    from
        mkyong.stock_daily_record stockdaily0_ 
    where
        stockdaily0_.STOCK_ID in (
            select
                stock0_.STOCK_ID 
            from
                mkyong.stock stock0_
        ) 

启用“子选择”后,它将创建两个 select 语句。

1.Select 语句检索所有股票记录。
2。在子选择查询中选择其所有相关集合。

结论

抓取策略非常灵活,是优化 Hibernate 查询的一个非常重要的调整,但是如果用错了地方,那将是一场灾难。

参考

1.http://docs . JBoss . org/hibernate/core/3.3/reference/en/html/performance . html
2 .https://www.hibernate.org/315.html

hibernate

Hibernate 拦截器示例–审计日志

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-interceptor-example-audit-log/

Hibernate 有一个强大的特性叫做“拦截器”,用来拦截或挂钩不同类型的 Hibernate 事件,比如数据库 CRUD 操作。在本文中,我将演示如何使用 Hibernate 拦截器实现一个应用程序审计日志特性,它将把所有 Hibernate 的保存、更新或删除操作记录到一个名为“审计日志的数据库表中。

Hibernate 拦截器示例–审计日志

1.创建表格

创建一个名为“审计日志”的表来存储所有应用程序审计记录。

 DROP TABLE IF EXISTS `mkyong`.`auditlog`;
CREATE TABLE  `mkyong`.`auditlog` (
  `AUDIT_LOG_ID` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `ACTION` varchar(100) NOT NULL,
  `DETAIL` text NOT NULL,
  `CREATED_DATE` date NOT NULL,
  `ENTITY_ID` bigint(20) unsigned NOT NULL,
  `ENTITY_NAME` varchar(255) NOT NULL,
  PRIMARY KEY (`AUDIT_LOG_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8; 

2.创建标记界面

创建一个标记接口,任何实现这个接口的类都将被审计。这个接口要求实现的类公开它的标识符-getId()和要记录的内容-getLogDeatil()。所有暴露的数据将被存储到数据库中。

 package com.mkyong.interceptor;
//market interface
public interface IAuditLog {

	public Long getId();	
	public String getLogDeatil();
} 

3.映射“审计日志”表

要与表“审计日志”映射的普通注释模型文件。

 @Entity
@Table(name = "auditlog", catalog = "mkyong")
public class AuditLog implements java.io.Serializable {

	private Long auditLogId;
	private String action;
	private String detail;
	private Date createdDate;
	private long entityId;
	private String entityName;
        ...
} 

4.一个类实现了 IAuditLog

一个普通的注释模型文件,用于映射表“stock ”,稍后将用于截取程序演示。它必须实现 IAuditLog 标记接口,并实现 getId()getLogDeatil() 方法。

 ...
@Entity
@Table(name = "stock", catalog = "mkyong"
public class Stock implements java.io.Serializable, IAuditLog {
...
        @Transient
	@Override
	public Long getId(){
		return this.stockId.longValue();
	}

	@Transient
	@Override
	public String getLogDeatil(){
		StringBuilder sb = new StringBuilder();
		sb.append(" Stock Id : ").append(stockId)
		.append(" Stock Code : ").append(stockCode)
		.append(" Stock Name : ").append(stockName);

		return sb.toString();
	}
... 

5.创建一个助手类

一个助手类,用于接收拦截器的数据,并将其存储到数据库中。

 ...
public class AuditLogUtil{

   public static void LogIt(String action,
     IAuditLog entity, Connection conn ){

     Session tempSession = HibernateUtil.getSessionFactory().openSession(conn);

     try {

	AuditLog auditRecord = new AuditLog(action,entity.getLogDeatil()
		, new Date(),entity.getId(), entity.getClass().toString());
	tempSession.save(auditRecord);
	tempSession.flush();

     } finally {	
	tempSession.close();		
     }		
  }
} 

6.创建一个 Hibernate 拦截器类

通过扩展 Hibernateempty interceptor创建一个拦截器类。下面是最流行的拦截器函数。

  • on save–在保存对象时调用,对象尚未保存到数据库中。
  • onflush dirty–当您更新一个对象时调用,该对象尚未更新到数据库中。
  • on delete–当您删除一个对象时调用,该对象尚未被删除到数据库中。
  • 预刷新–在保存、更新或删除的对象提交到数据库之前调用(通常在后刷新之前)。
  • post flush–在保存、更新或删除的对象提交到数据库后调用。

代码相当冗长,它应该是自我探索的。

 ...
public class AuditLogInterceptor extends EmptyInterceptor{

	Session session;
	private Set inserts = new HashSet();
	private Set updates = new HashSet();
	private Set deletes = new HashSet();

	public void setSession(Session session) {
		this.session=session;
	}

	public boolean onSave(Object entity,Serializable id,
		Object[] state,String[] propertyNames,Type[] types)
		throws CallbackException {

		System.out.println("onSave");

		if (entity instanceof IAuditLog){
			inserts.add(entity);
		}
		return false;

	}

	public boolean onFlushDirty(Object entity,Serializable id,
		Object[] currentState,Object[] previousState,
		String[] propertyNames,Type[] types)
		throws CallbackException {

		System.out.println("onFlushDirty");

		if (entity instanceof IAuditLog){
			updates.add(entity);
		}
		return false;

	}

	public void onDelete(Object entity, Serializable id, 
		Object[] state, String[] propertyNames, 
		Type[] types) {

		System.out.println("onDelete");

		if (entity instanceof IAuditLog){
			deletes.add(entity);
		}
	}

	//called before commit into database
	public void preFlush(Iterator iterator) {
		System.out.println("preFlush");
	}	

	//called after committed into database
	public void postFlush(Iterator iterator) {
		System.out.println("postFlush");

	try{

		for (Iterator it = inserts.iterator(); it.hasNext();) {
		    IAuditLog entity = (IAuditLog) it.next();
		    System.out.println("postFlush - insert");		
		    AuditLogUtil.LogIt("Saved",entity, session.connection());
		}	

		for (Iterator it = updates.iterator(); it.hasNext();) {
		    IAuditLog entity = (IAuditLog) it.next();
		    System.out.println("postFlush - update");
		    AuditLogUtil.LogIt("Updated",entity, session.connection());
		}	

		for (Iterator it = deletes.iterator(); it.hasNext();) {
		    IAuditLog entity = (IAuditLog) it.next();
		    System.out.println("postFlush - delete");
		    AuditLogUtil.LogIt("Deleted",entity, session.connection());
		}	

	} finally {
		inserts.clear();
		updates.clear();
		deletes.clear();
	}
       }		
} 

7.启用拦截器

您可以通过将拦截器作为参数传递给 openSession(拦截器)来启用拦截器;

 ...
   Session session = null;
   Transaction tx = null;
   try {

	AuditLogInterceptor interceptor = new AuditLogInterceptor();
	session = HibernateUtil.getSessionFactory().openSession(interceptor);
	interceptor.setSession(session);

	//test insert
	tx = session.beginTransaction();
	Stock stockInsert = new Stock();
	stockInsert.setStockCode("1111");
	stockInsert.setStockName("mkyong");
	session.saveOrUpdate(stockInsert);
	tx.commit();

	//test update
	tx = session.beginTransaction();
	Query query = session.createQuery("from Stock where stockCode = '1111'");
	Stock stockUpdate = (Stock)query.list().get(0);
	stockUpdate.setStockName("mkyong-update");
	session.saveOrUpdate(stockUpdate);
	tx.commit();

	//test delete
	tx = session.beginTransaction();
	session.delete(stockUpdate);
	tx.commit();

   } catch (RuntimeException e) {
	try {
		tx.rollback();
	} catch (RuntimeException rbe) {
		// log.error("Couldn’t roll back transaction", rbe);
   }
	throw e;
   } finally {
	if (session != null) {
		session.close();
	}
   }
... 

在插入测试中

 session.saveOrUpdate(stockInsert); //it will call onSave
tx.commit(); // it will call preFlush follow by postFlush 

在更新测试中

 session.saveOrUpdate(stockUpdate); //it will call onFlushDirty
tx.commit(); // it will call preFlush follow by postFlush 

在删除测试中

 session.delete(stockUpdate); //it will call onDelete
tx.commit();  // it will call preFlush follow by postFlush 
输出
 onSave
Hibernate: 
    insert into mkyong.stock
    (STOCK_CODE, STOCK_NAME) 
    values (?, ?)
preFlush
postFlush
postFlush - insert
Hibernate: 
    insert into mkyong.auditlog
    (ACTION, CREATED_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) 
    values (?, ?, ?, ?, ?)
preFlush
Hibernate: 
    select ...
    from mkyong.stock stock0_ 
    where stock0_.STOCK_CODE='1111'
preFlush
onFlushDirty
Hibernate: 
    update mkyong.stock 
    set STOCK_CODE=?, STOCK_NAME=? 
    where STOCK_ID=?
postFlush
postFlush - update
Hibernate: 
    insert into mkyong.auditlog
    (ACTION, CREATED_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) 
    values (?, ?, ?, ?, ?)
onDelete
preFlush
Hibernate: 
    delete from mkyong.stock where STOCK_ID=?
postFlush
postFlush - delete
Hibernate: 
    insert into mkyong.auditlog 
    (ACTION, CREATED_DATE, DETAIL, ENTITY_ID, ENTITY_NAME) 
    values (?, ?, ?, ?, ?) 
在数据库中
 SELECT * FROM auditlog a; 

所有审计数据都被插入数据库。

interceptor-example

结论

审计日志是一个有用的特性,通常在数据库中使用触发器来处理,但是出于可移植性的考虑,我建议使用应用程序来实现它。

Download this example – Hibernate interceptor example.zipTags : hibernate interceptor

hibernate–多对多示例–连接表+额外列(注释)

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/

在本教程中,我们将向您展示如何使用 Hibernate 来实现“多对多表关系,在连接表中有额外的列”。

Note
For many to many relationship with NO extra column in the join table, please refer to this @many-to-many tutorial

1.多对多表+连接表中的额外列

股票和类别的多对多关系与名为 STOCK_CATEGORY 的第三个/ join 表相链接,并带有额外的“created_by”和“created_date”列。

many to many diagram

MySQL 表格脚本

 CREATE TABLE `stock` (
  `STOCK_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `STOCK_CODE` VARCHAR(10) NOT NULL,
  `STOCK_NAME` VARCHAR(20) NOT NULL,
  PRIMARY KEY (`STOCK_ID`) USING BTREE,
  UNIQUE KEY `UNI_STOCK_NAME` (`STOCK_NAME`),
  UNIQUE KEY `UNI_STOCK_ID` (`STOCK_CODE`) USING BTREE
)

CREATE TABLE `category` (
  `CATEGORY_ID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `NAME` VARCHAR(10) NOT NULL,
  `DESC` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`CATEGORY_ID`) USING BTREE
)

CREATE TABLE  `stock_category` (
  `STOCK_ID` INT(10) UNSIGNED NOT NULL,
  `CATEGORY_ID` INT(10) UNSIGNED NOT NULL,
  `CREATED_DATE` DATE NOT NULL,
  `CREATED_BY` VARCHAR(10) NOT NULL,
  PRIMARY KEY (`STOCK_ID`,`CATEGORY_ID`),
  CONSTRAINT `FK_CATEGORY_ID` FOREIGN KEY (`CATEGORY_ID`) 
             REFERENCES `category` (`CATEGORY_ID`),
  CONSTRAINT `FK_STOCK_ID` FOREIGN KEY (`STOCK_ID`) 
             REFERENCES `stock` (`STOCK_ID`)
) 

2.项目结构

查看本教程的文件项目结构。

many to many project folder

3.Hibernate / JPA 注释

Hibernate / JBoss 工具生成的注释代码在第三个表额外列场景中不起作用。为了让它工作,您应该定制代码,在StockCategory.java中使用“@AssociationOverride”来表示多对多关系。

请参见以下自定义代码:

文件:Stock.java

 package com.mkyong.stock;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
		@UniqueConstraint(columnNames = "STOCK_NAME"),
		@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {

	private Integer stockId;
	private String stockCode;
	private String stockName;
	private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0);

	public Stock() {
	}

	public Stock(String stockCode, String stockName) {
		this.stockCode = stockCode;
		this.stockName = stockName;
	}

	public Stock(String stockCode, String stockName,
			Set<StockCategory> stockCategories) {
		this.stockCode = stockCode;
		this.stockName = stockName;
		this.stockCategories = stockCategories;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "STOCK_ID", unique = true, nullable = false)
	public Integer getStockId() {
		return this.stockId;
	}

	public void setStockId(Integer stockId) {
		this.stockId = stockId;
	}

	@Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
	public String getStockCode() {
		return this.stockCode;
	}

	public void setStockCode(String stockCode) {
		this.stockCode = stockCode;
	}

	@Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
	public String getStockName() {
		return this.stockName;
	}

	public void setStockName(String stockName) {
		this.stockName = stockName;
	}

	@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.stock", cascade=CascadeType.ALL)
	public Set<StockCategory> getStockCategories() {
		return this.stockCategories;
	}

	public void setStockCategories(Set<StockCategory> stockCategories) {
		this.stockCategories = stockCategories;
	}

} 

文件:StockCategory.java

 package com.mkyong.stock;

import java.util.Date;

import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

@Entity
@Table(name = "stock_category", catalog = "mkyongdb")
@AssociationOverrides({
		@AssociationOverride(name = "pk.stock", 
			joinColumns = @JoinColumn(name = "STOCK_ID")),
		@AssociationOverride(name = "pk.category", 
			joinColumns = @JoinColumn(name = "CATEGORY_ID")) })
public class StockCategory implements java.io.Serializable {

	private StockCategoryId pk = new StockCategoryId();
	private Date createdDate;
	private String createdBy;

	public StockCategory() {
	}

	@EmbeddedId
	public StockCategoryId getPk() {
		return pk;
	}

	public void setPk(StockCategoryId pk) {
		this.pk = pk;
	}

	@Transient
	public Stock getStock() {
		return getPk().getStock();
	}

	public void setStock(Stock stock) {
		getPk().setStock(stock);
	}

	@Transient
	public Category getCategory() {
		return getPk().getCategory();
	}

	public void setCategory(Category category) {
		getPk().setCategory(category);
	}

	@Temporal(TemporalType.DATE)
	@Column(name = "CREATED_DATE", nullable = false, length = 10)
	public Date getCreatedDate() {
		return this.createdDate;
	}

	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}

	@Column(name = "CREATED_BY", nullable = false, length = 10)
	public String getCreatedBy() {
		return this.createdBy;
	}

	public void setCreatedBy(String createdBy) {
		this.createdBy = createdBy;
	}

	public boolean equals(Object o) {
		if (this == o)
			return true;
		if (o == null || getClass() != o.getClass())
			return false;

		StockCategory that = (StockCategory) o;

		if (getPk() != null ? !getPk().equals(that.getPk())
				: that.getPk() != null)
			return false;

		return true;
	}

	public int hashCode() {
		return (getPk() != null ? getPk().hashCode() : 0);
	}
} 

文件:StockCategoryId.java

 package com.mkyong.stock;

import javax.persistence.Embeddable;
import javax.persistence.ManyToOne;

@Embeddable
public class StockCategoryId implements java.io.Serializable {

	private Stock stock;
    private Category category;

	@ManyToOne
	public Stock getStock() {
		return stock;
	}

	public void setStock(Stock stock) {
		this.stock = stock;
	}

	@ManyToOne
	public Category getCategory() {
		return category;
	}

	public void setCategory(Category category) {
		this.category = category;
	}

	public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        StockCategoryId that = (StockCategoryId) o;

        if (stock != null ? !stock.equals(that.stock) : that.stock != null) return false;
        if (category != null ? !category.equals(that.category) : that.category != null)
            return false;

        return true;
    }

    public int hashCode() {
        int result;
        result = (stock != null ? stock.hashCode() : 0);
        result = 31 * result + (category != null ? category.hashCode() : 0);
        return result;
    }

} 

文件:Category.java

 package com.mkyong.stock;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "category", catalog = "mkyongdb")
public class Category implements java.io.Serializable {

	private Integer categoryId;
	private String name;
	private String desc;
	private Set<StockCategory> stockCategories = new HashSet<StockCategory>(0);

	public Category() {
	}

	public Category(String name, String desc) {
		this.name = name;
		this.desc = desc;
	}

	public Category(String name, String desc, Set<StockCategory> stockCategories) {
		this.name = name;
		this.desc = desc;
		this.stockCategories = stockCategories;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "CATEGORY_ID", unique = true, nullable = false)
	public Integer getCategoryId() {
		return this.categoryId;
	}

	public void setCategoryId(Integer categoryId) {
		this.categoryId = categoryId;
	}

	@Column(name = "NAME", nullable = false, length = 10)
	public String getName() {
		return this.name;
	}

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

	@Column(name = "[DESC]", nullable = false)
	public String getDesc() {
		return this.desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

	@OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.category")
	public Set<StockCategory> getStockCategories() {
		return this.stockCategories;
	}

	public void setStockCategories(Set<StockCategory> stockCategories) {
		this.stockCategories = stockCategories;
	}

} 

完成后,多对多关系现在应该可以工作了。

4.运行它–案例 1

一个新的类别和新的股票。

 session.beginTransaction();

    Stock stock = new Stock();
    stock.setStockCode("7052");
    stock.setStockName("PADINI");

    Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
    //new category, need save to get the id first
    session.save(category1);

    StockCategory stockCategory = new StockCategory();
    stockCategory.setStock(stock);
    stockCategory.setCategory(category1);
    stockCategory.setCreatedDate(new Date()); //extra column
    stockCategory.setCreatedBy("system"); //extra column

    stock.getStockCategories().add(stockCategory);

    session.save(stock);

    session.getTransaction().commit(); 

输出…

 Hibernate: 
    insert 
    into
        mkyongdb.category
        (`DESC`, NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    values
        (?, ?)
Hibernate: 
    select
        stockcateg_.CATEGORY_ID,
        stockcateg_.STOCK_ID,
        stockcateg_.CREATED_BY as CREATED1_2_,
        stockcateg_.CREATED_DATE as CREATED2_2_ 
    from
        mkyongdb.stock_category stockcateg_ 
    where
        stockcateg_.CATEGORY_ID=? 
        and stockcateg_.STOCK_ID=?
Hibernate: 
    insert 
    into
        mkyongdb.stock_category
        (CREATED_BY, CREATED_DATE, CATEGORY_ID, STOCK_ID) 
    values
        (?, ?, ?, ?) 

5.运行它–案例 2

获取现有类别和新股票。

 session.beginTransaction();

    Stock stock = new Stock();
    stock.setStockCode("7052");
    stock.setStockName("PADINI");

    //assume category id is 7
    Category category1 = (Category)session.get(Category.class, 7);

    StockCategory stockCategory = new StockCategory();
    stockCategory.setStock(stock);
    stockCategory.setCategory(category1);
    stockCategory.setCreatedDate(new Date()); //extra column
    stockCategory.setCreatedBy("system"); //extra column

    stock.getStockCategories().add(stockCategory);

    session.save(stock);

    session.getTransaction().commit(); 

输出…

 Hibernate: 
    select
        category0_.CATEGORY_ID as CATEGORY1_1_0_,
        category0_.`DESC` as DESC2_1_0_,
        category0_.NAME as NAME1_0_ 
    from
        mkyongdb.category category0_ 
    where
        category0_.CATEGORY_ID=?
Hibernate: 
    insert 
    into
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    values
        (?, ?)
Hibernate: 
    select
        stockcateg_.CATEGORY_ID,
        stockcateg_.STOCK_ID,
        stockcateg_.CREATED_BY as CREATED1_2_,
        stockcateg_.CREATED_DATE as CREATED2_2_ 
    from
        mkyongdb.stock_category stockcateg_ 
    where
        stockcateg_.CATEGORY_ID=? 
        and stockcateg_.STOCK_ID=?
Hibernate: 
    insert 
    into
        mkyongdb.stock_category
        (CREATED_BY, CREATED_DATE, CATEGORY_ID, STOCK_ID) 
    values
        (?, ?, ?, ?) 

完成了。

Download it – Hibernate-many-to-many-third-table-annotation.zip (13KB)

参考

  1. Hibernate 映射文档

Tags : hibernate many-to-many

hibernate–多对多示例(注释)

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-many-to-many-relationship-example-annotation/

在本教程中,它将重用之前的“Hibernate many to many example–XML mapping”教程的整个基础设施,并对其进行增强以支持 Hibernare / JPA 注释。

Note
For many to many with extra columns in join table, please refer to this tutorial.

项目结构

查看本教程的新项目结构。

many to many project folder ## 1.“多对多”表关系

再看前面的多对多表关系。

many to many ER diagram ## 2.Hibernate 模型类

更新以前的模型类—Stock.javaCategory.java,并在其中定义新的注释代码。

文件:Stock.java

 package com.mkyong.stock;

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 static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.CascadeType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
		@UniqueConstraint(columnNames = "STOCK_NAME"),
		@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {

	private Integer stockId;
	private String stockCode;
	private String stockName;
	private Set<Category> categories = new HashSet<Category>(0);

	public Stock() {
	}

	public Stock(String stockCode, String stockName) {
		this.stockCode = stockCode;
		this.stockName = stockName;
	}

	public Stock(String stockCode, String stockName, Set<Category> categories) {
		this.stockCode = stockCode;
		this.stockName = stockName;
		this.categories = categories;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "STOCK_ID", unique = true, nullable = false)
	public Integer getStockId() {
		return this.stockId;
	}

	public void setStockId(Integer stockId) {
		this.stockId = stockId;
	}

	@Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
	public String getStockCode() {
		return this.stockCode;
	}

	public void setStockCode(String stockCode) {
		this.stockCode = stockCode;
	}

	@Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
	public String getStockName() {
		return this.stockName;
	}

	public void setStockName(String stockName) {
		this.stockName = stockName;
	}

	@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	@JoinTable(name = "stock_category", catalog = "mkyongdb", joinColumns = { 
			@JoinColumn(name = "STOCK_ID", nullable = false, updatable = false) }, 
			inverseJoinColumns = { @JoinColumn(name = "CATEGORY_ID", 
					nullable = false, updatable = false) })
	public Set<Category> getCategories() {
		return this.categories;
	}

	public void setCategories(Set<Category> categories) {
		this.categories = categories;
	}

} 

文件:Category.java

 package com.mkyong.stock;

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 static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "category", catalog = "mkyongdb")
public class Category implements java.io.Serializable {

	private Integer categoryId;
	private String name;
	private String desc;
	private Set<Stock> stocks = new HashSet<Stock>(0);

	public Category() {
	}

	public Category(String name, String desc) {
		this.name = name;
		this.desc = desc;
	}

	public Category(String name, String desc, Set<Stock> stocks) {
		this.name = name;
		this.desc = desc;
		this.stocks = stocks;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "CATEGORY_ID", unique = true, nullable = false)
	public Integer getCategoryId() {
		return this.categoryId;
	}

	public void setCategoryId(Integer categoryId) {
		this.categoryId = categoryId;
	}

	@Column(name = "NAME", nullable = false, length = 10)
	public String getName() {
		return this.name;
	}

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

	@Column(name = "[DESC]", nullable = false)
	public String getDesc() {
		return this.desc;
	}

	public void setDesc(String desc) {
		this.desc = desc;
	}

	@ManyToMany(fetch = FetchType.LAZY, mappedBy = "categories")
	public Set<Stock> getStocks() {
		return this.stocks;
	}

	public void setStocks(Set<Stock> stocks) {
		this.stocks = stocks;
	}

} 

3.休眠配置文件

将带注释的类Stock.javaCategory.java放在hibernate.cfg.xml中,如下所示:

文件:hibernate.cfg.xml

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyongdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">password</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    <mapping class="com.mkyong.stock.Stock" />
    <mapping class="com.mkyong.stock.Category" />
</session-factory>
</hibernate-configuration> 

4.运行它

运行它,结果不言自明。

文件:App.java

 package com.mkyong;

import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import com.mkyong.stock.Category;
import com.mkyong.stock.Stock;
import com.mkyong.util.HibernateUtil;

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

        System.out.println("Hibernate many to many (Annotation)");
	Session session = HibernateUtil.getSessionFactory().openSession();

	session.beginTransaction();

	Stock stock = new Stock();
        stock.setStockCode("7052");
        stock.setStockName("PADINI");

        Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
        Category category2 = new Category("INVESTMENT", "INVESTMENT COMPANY");

        Set<Category> categories = new HashSet<Category>();
        categories.add(category1);
        categories.add(category2);

        stock.setCategories(categories);

        session.save(stock);

	session.getTransaction().commit();
	System.out.println("Done");
	}
} 

输出

 Hibernate many to many (Annotation)
Hibernate: 
    insert 
    into
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.category
        (`DESC`, NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.category
        (`DESC`, NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.stock_category
        (STOCK_ID, CATEGORY_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.stock_category
        (STOCK_ID, CATEGORY_ID) 
    values
        (?, ?)
Done 

Download it – Hibernate-many-to-many-annotation.zip (9KB)

参考

  1. Hibernate 文档–多对多关系。

hibernate many-to-many

hibernate–多对多示例(XML 映射)

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-many-to-many-relationship-example/

当一个实体中的每个记录在另一个实体中可能有许多链接的记录时,就会出现多对多关系,反之亦然。

在本教程中,我们将向您展示如何通过 XML 映射文件(hbm)在 Hibernate 中处理多对多表关系。

Note
For many to many with extra columns in join table, please refer to this tutorial.

本教程中使用的工具和技术:

  1. Hibernate 3.6.3 .最终版
  2. MySQL 5.1.15
  3. Maven 3.0.3
  4. Eclipse 3.6

项目结构

本教程的项目结构。

many to many project folder

项目依赖性

从 JBoss 存储库中获取最新的 hibernate.jar

文件:pom.xml

 <project ...>

	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>

	<dependencies>

		<!-- MySQL database driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.15</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
		</dependency>

	</dependencies>
</project> 

1.“多对多”示例

这是一个多对多的关系表设计,一个股票表有多个类别,而类别可以属于多个股票,这种关系与第三个名为 STOCK_CATEGORY 的表相链接。

表 STOCK_CATEGORY 只包含两个主键,以及引用 STOCK 和 CATEGORY 外键。

many to many ER diagram

MySQL 表格脚本

 CREATE TABLE `stock` (
  `STOCK_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `STOCK_CODE` varchar(10) NOT NULL,
  `STOCK_NAME` varchar(20) NOT NULL,
  PRIMARY KEY (`STOCK_ID`) USING BTREE,
  UNIQUE KEY `UNI_STOCK_NAME` (`STOCK_NAME`),
  UNIQUE KEY `UNI_STOCK_ID` (`STOCK_CODE`) USING BTREE
)

CREATE TABLE `category` (
  `CATEGORY_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `NAME` varchar(10) NOT NULL,
  `DESC` varchar(255) NOT NULL,
  PRIMARY KEY (`CATEGORY_ID`) USING BTREE
)

CREATE TABLE  `stock_category` (
  `STOCK_ID` int(10) unsigned NOT NULL,
  `CATEGORY_ID` int(10) unsigned NOT NULL,
  PRIMARY KEY (`STOCK_ID`,`CATEGORY_ID`),
  CONSTRAINT `FK_CATEGORY_ID` FOREIGN KEY (`CATEGORY_ID`) REFERENCES `category` (`CATEGORY_ID`),
  CONSTRAINT `FK_STOCK_ID` FOREIGN KEY (`STOCK_ID`) REFERENCES `stock` (`STOCK_ID`)
) 

2.Hibernate 模型类

创建两个模型类—Stock.javaCategory.java,来表示上面的表。不需要为表' stock_category 创建额外的类。

文件:Stock.java

 package com.mkyong.stock;

import java.util.HashSet;
import java.util.Set;

public class Stock implements java.io.Serializable {

	private Integer stockId;
	private String stockCode;
	private String stockName;
	private Set<Category> categories = new HashSet<Category>(0);

	//getter, setter and constructor
} 

文件:Category.java

 package com.mkyong.stock;

import java.util.HashSet;
import java.util.Set;

public class Category implements java.io.Serializable {

	private Integer categoryId;
	private String name;
	private String desc;
	private Set<Stock> stocks = new HashSet<Stock>(0);

	//getter, setter and constructor
} 

3.Hibernate XML 映射

现在,创建两个 Hibernate 映射文件(hbm)—Stock.hbm.xmlCategory.hbm.xml。您会注意到第三个'股票 _ 类别'表是通过“多对多标记引用的。

文件:Stock.hbm.xml

 <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.mkyong.stock.Stock" table="stock" catalog="mkyongdb">
        <id name="stockId" type="java.lang.Integer">
            <column name="STOCK_ID" />
            <generator class="identity" />
        </id>
        <property name="stockCode" type="string">
            <column name="STOCK_CODE" length="10" not-null="true" unique="true" />
        </property>
        <property name="stockName" type="string">
            <column name="STOCK_NAME" length="20" not-null="true" unique="true" />
        </property>
        <set name="categories" table="stock_category" 
        	inverse="false" lazy="true" fetch="select" cascade="all" >
            <key>
                <column name="STOCK_ID" not-null="true" />
            </key>
            <many-to-many entity-name="com.mkyong.stock.Category">
                <column name="CATEGORY_ID" not-null="true" />
            </many-to-many>
        </set>
    </class>
</hibernate-mapping> 

文件:Category.hbm.xml

 <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.mkyong.stock.Category" table="category" catalog="mkyongdb">
        <id name="categoryId" type="java.lang.Integer">
            <column name="CATEGORY_ID" />
            <generator class="identity" />
        </id>
        <property name="name" type="string">
            <column name="NAME" length="10" not-null="true" />
        </property>
        <property name="desc" type="string">
            <column name="[DESC]" not-null="true" />
        </property>
        <set name="stocks" table="stock_category" inverse="true" lazy="true" fetch="select">
            <key>
                <column name="CATEGORY_ID" not-null="true" />
            </key>
            <many-to-many entity-name="com.mkyong.stock.Stock">
                <column name="STOCK_ID" not-null="true" />
            </many-to-many>
        </set>
    </class>
</hibernate-mapping> 

4.休眠配置文件

现在,将Stock.hbm.xmlCategory.hbm.xml以及 MySQL 细节放在hibernate.cfg.xml中。

文件:hibernate.cfg.xml

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyongdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">password</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    <property name="format_sql">true</property>
    <mapping resource="com/mkyong/stock/Stock.hbm.xml" />
    <mapping resource="com/mkyong/stock/Category.hbm.xml" />
</session-factory>
</hibernate-configuration> 

5.运行它

运行它,Hibernate 将在股票表中插入一条记录,在类别表中插入两条记录,在股票类别表中也插入两条记录。

文件:App.java

 package com.mkyong;

import java.util.HashSet;
import java.util.Set;
import org.hibernate.Session;
import com.mkyong.stock.Category;
import com.mkyong.stock.Stock;
import com.mkyong.util.HibernateUtil;

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

        System.out.println("Hibernate many to many (XML Mapping)");
	Session session = HibernateUtil.getSessionFactory().openSession();

	session.beginTransaction();

	Stock stock = new Stock();
        stock.setStockCode("7052");
        stock.setStockName("PADINI");

        Category category1 = new Category("CONSUMER", "CONSUMER COMPANY");
        Category category2 = new Category("INVESTMENT", "INVESTMENT COMPANY");

        Set<Category> categories = new HashSet<Category>();
        categories.add(category1);
        categories.add(category2);

        stock.setCategories(categories);

        session.save(stock);

	session.getTransaction().commit();
	System.out.println("Done");
	}
} 

输出…结果应该是不言自明的

 Hibernate many to many (XML Mapping)
Hibernate: 
    insert 
    into
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.category
        (NAME, `DESC`) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.category
        (NAME, `DESC`) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        stock_category
        (STOCK_ID, CATEGORY_ID) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        stock_category
        (STOCK_ID, CATEGORY_ID) 
    values
        (?, ?)
Done 

Hibernate Annotation
For many-to-many in Hibernate annotation, please refer to this example.Download it – Hibernate-many-to-many-xml-mapping.zip (10KB)

参考

  1. Hibernate 文档–多对多关系。

Tags : hibernate many-to-many

Hibernate 可变示例(类和集合)

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-mutable-example-class-and-collection/

在 hibernate 中,类及其相关集合中的“可变的”默认为“真”,这意味着允许添加、更新和删除类或集合。另一方面,如果可变变量被更改为 false,它在类及其相关集合中具有不同的含义。下面我们举几个例子来深入了解一下。

Hibernate 一对多示例

我将把这个一对多示例用于可变演示。在这个映射文件中,一个股票属于许多 StockDailyRecord。

 <!-- Stock.hbm.xml -->
...
<hibernate-mapping>
    <class name="com.mkyong.common.Stock" table="stock" >
        <set name="stockDailyRecords" mutable="false" cascade="all" 
               inverse="true" lazy="true" table="stock_daily_record">
            <key>
                <column name="STOCK_ID" not-null="true" />
            </key>
            <one-to-many class="com.mkyong.common.StockDailyRecord" />
        </set>
    </class>
...    
</hibernate-mapping> 

如何声明 mutable?

XML 映射文件和注释都支持“可变”。

1.XML 映射文件

在映射文件中,‘可变关键字用于实现可变函数。

 <!-- Stock.hbm.xml -->
...
<hibernate-mapping>
    <class name="com.mkyong.common.Stock" table="stock" mutable="false" >
        <set name="stockDailyRecords" mutable="false" cascade="all" 
               inverse="true" lazy="true" table="stock_daily_record" >
            <key>
                <column name="STOCK_ID" not-null="true" />
            </key>
            <one-to-many class="com.mkyong.common.StockDailyRecord" />
        </set>
    </class>
...    
</hibernate-mapping> 

2.注释

在注释中,关键字被更改为@Immutable (mutable='false ')。

 ...
@Entity
@Immutable
@Table(name = "stock", catalog = "mkyong")
public class Stock implements java.io.Serializable {
...
        @OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
	@Immutable
	public Set<StockDailyRecord> getStockDailyRecords() {
		return this.stockDailyRecords;
	}
... 

在类中可变

如果在 class 元素中声明了 mutable = "false "或@Immutable,这意味着对该类的更新将被忽略,但不会抛出异常,只允许添加和删除操作

1.测试插件

 Stock stock = new Stock();
stock.setStockCode("7277");
stock.setStockName("DIALOG");
session.save(stock); 

如果在类中声明了 mutable = "true "(默认)或 no @Immutable。
输出

 Hibernate: 
    insert into mkyong.stock (STOCK_CODE, STOCK_NAME) 
    values (?, ?) 

如果在类中声明了 mutable = "false "或@Immutable。
输出

 Hibernate: 
    insert into mkyong.stock (STOCK_CODE, STOCK_NAME) 
    values (?, ?) 

类中的可变变量对“插入”操作没有影响。

2.测试更新

 Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
stock.setStockName("DIALOG123");
session.saveOrUpdate(stock); 

如果在类中声明了 mutable = "true "或 no @Immutable。
输出

 Hibernate: 
    select ...from mkyong.stock stock0_ 
    where stock0_.STOCK_CODE='7277'

Hibernate: 
    update mkyong.stock 
    set STOCK_CODE=?,STOCK_NAME=? 
    where STOCK_ID=? 

如果在类中声明了 mutable = "false "或@Immutable。
输出

 Hibernate: 
    select ...from mkyong.stock stock0_ 
    where stock0_.STOCK_CODE='7277' 

类中的可变变量不允许应用程序对其进行更新,“更新”操作将被忽略,并且不会引发异常

3.测试删除

 Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
session.delete(stock); 

如果在类中声明了 mutable = "true "(默认)或 no @Immutable。
输出

 Hibernate: 
    delete from mkyong.stock 
    where STOCK_ID=? 

如果在类中声明了 mutable = "false "或@Immutable。
输出

 Hibernate: 
    delete from mkyong.stock 
    where STOCK_ID=? 

类中的可变变量在“删除”操作中无效。

集合中可变的

如果在集合中声明了 mutable = "false "或@Immutable,这意味着在这个集合中不允许使用 add 和 delete-orphan,只有 update 和' cascade delete all '是允许的

1.测试插件

假设级联插入已启用。

 Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
StockDailyRecord sdr = new StockDailyRecord();
sdr.setDate(new Date());        
sdr.setStock(stock);
stock.getStockDailyRecords().add(sdr);
session.save(stock); 

如果在集合中声明了 mutable = "true "(默认值)或 no @Immutable。
输出

 Hibernate: 
    insert into mkyong.stock_daily_record
    (STOCK_ID, PRICE_OPEN, PRICE_CLOSE, PRICE_CHANGE, VOLUME, DATE) 
    values (?, ?, ?, ?, ?, ?) 

如果在集合中声明了 mutable = "false "或@Immutable。
输出

 Exception in thread "main" org.hibernate.HibernateException: 
changed an immutable collection instance: 
[com.mkyong.common.Stock.stockDailyRecords#111] 

集合中的 Mutable 不允许' add '操作,会抛出异常。

2.测试更新

假设级联更新已启用。

 Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
StockDailyRecord sdr = stock.getStockDailyRecords().iterator().next();
sdr.setPriceChange(new Float(1.30));
session.saveOrUpdate(stock); 

如果在集合中声明了 mutable = "true "(默认值)或 no @Immutable。
输出

 Hibernate: 
    update mkyong.stock_daily_record 
    set PRICE_CHANGE=?, ...
    where DAILY_RECORD_ID=? 

如果在集合中声明了 mutable = "false "或@Immutable。
输出

 Hibernate: 
    update mkyong.stock_daily_record 
    set PRICE_CHANGE=?, ...
    where DAILY_RECORD_ID=? 

集合中的可变变量对“更新”操作没有影响。

3.测试删除-孤立

假设级联删除-孤立被启用。

 Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
StockDailyRecord sdr = stock.getStockDailyRecords().iterator().next();
stock.getStockDailyRecords().remove(sdr);
session.saveOrUpdate(stock); 

如果在集合中声明了 mutable = "true "(默认值)或 no @Immutable。
输出

 Hibernate: 
    delete from mkyong.stock_daily_record 
    where DAILY_RECORD_ID=? 

如果在集合中声明了 mutable = "false "或@Immutable。
输出

 Exception in thread "main" org.hibernate.HibernateException: 
changed an immutable collection instance: 
[com.mkyong.common.Stock.stockDailyRecords#111] 

集合中的可变不允许“删除-孤立”操作,将引发异常。

4.测试删除

假设级联删除已启用。

 Stock stock = (Stock)session.createQuery(
      " from Stock where stockCode = '7277'").list().get(0);
session.saveOrUpdate(stock); 

如果在集合中声明了 mutable = "true "(默认值)或 no @Immutable。
输出

 Hibernate: 
    delete from mkyong.stock_daily_record 
    where DAILY_RECORD_ID=?

Hibernate: 
    delete from mkyong.stock 
    where STOCK_ID=? 

如果在集合中声明了 mutable = "false "或@Immutable。
输出

 Hibernate: 
    delete from mkyong.stock_daily_record 
    where DAILY_RECORD_ID=?

Hibernate: 
    delete from mkyong.stock 
    where STOCK_ID=? 

集合中的可变元素在“删除”操作中无效,如果父元素被删除,它的所有子元素也会被删除,即使它是可变的。

为什么可变?

Mutable 可以避免许多无意的数据库操作,比如添加、更新或删除一些不应该的记录。此外,根据 Hibernate 文档,mutable do 有一些小的性能优化,建议分析映射关系并根据需要实现 mutable。

摘要

1.在类中声明了 mutable = "false "或@Immutable

这意味着对这个类的更新将被忽略,但是不会抛出异常,只允许添加和删除操作。

在具有 mutable = " false "–insert = allow,delete=allow,update=not allow 的类中

2.集合中声明了 mutable = "false "或@Immutable

这意味着在这个集合中不允许添加和删除孤儿,只允许更新。但是,如果级联删除被启用,当父代被删除时,它的所有子代也会被删除,即使它是可变的。

在集合中使用 mutable = " false "–insert =不允许,delete-orphan =不允许,delete =允许,update =允许

完全不可改变?

一个类可以对任何动作完全不可变吗?是的,把一个 mutable =“false”放在它的所有关系中(insert =不允许,delete-orphan =不允许),把一个 mutable =“false”放在你希望不可变的类中(update =不允许)。现在,您有了一个完全不可变的类,但是,如果级联删除选项被启用,当您的不可变类的父类被删除时,您的不可变类也将被删除。

参考

1.http://docs . JBoss . org/hibernate/stable/annotations/reference/en/html _ single/

Hibernate 命名查询示例

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-named-query-examples/

很多时候,开发人员喜欢将 HQL 字符串分散在 Java 代码中,这种方法很难维护,而且看起来很难看。幸运的是,Hibernate 出来了一种叫做“ names queries ”的技术,它让开发者把所有的 HQL 放到 XML 映射文件中或者通过注释。

如何声明命名查询

HQL 或本机 SQL 都支持命名查询。查看示例…

1.XML 映射文件

映射文件中的 HQL

 <!-- stock.hbm.xml -->
<hibernate-mapping>
    <class name="com.mkyong.common.Stock" table="stock" ...>
        <id name="stockId" type="java.lang.Integer">
            <column name="STOCK_ID" />
            <generator class="identity" />
        </id>
        <property name="stockCode" type="string">
            <column name="STOCK_CODE" length="10" not-null="true" unique="true" />
        </property>
        ...
    </class>

    <query name="findStockByStockCode">
        <![CDATA[from Stock s where s.stockCode = :stockCode]]>
    </query>

</hibernate-mapping> 

映射文件中的本机 SQL

 <!-- stock.hbm.xml -->
<hibernate-mapping>
    <class name="com.mkyong.common.Stock" table="stock" ...>
        <id name="stockId" type="java.lang.Integer">
            <column name="STOCK_ID" />
            <generator class="identity" />
        </id>
        <property name="stockCode" type="string">
            <column name="STOCK_CODE" length="10" not-null="true" unique="true" />
        </property>
        ...
    </class>

    <sql-query name="findStockByStockCodeNativeSQL">
	<return alias="stock" class="com.mkyong.common.Stock"/>
	<![CDATA[select * from stock s where s.stock_code = :stockCode]]>
    </sql-query>
</hibernate-mapping> 

您可以在' hibernate-mapping 元素中放置一个命名查询,但是不要放在'元素之前,hibernate 会提示无效的映射文件,您的所有命名查询都必须放在'元素之后。

Note
Regarding the CDATA , it’s always good practice to wrap your query text with CDATA, so that the XML parser will not prompt error for some special XML characters like ‘>’ , <‘ and etc. ## 2.注释

注释中的 HQL

 @NamedQueries({
	@NamedQuery(
	name = "findStockByStockCode",
	query = "from Stock s where s.stockCode = :stockCode"
	)
})
@Entity
@Table(name = "stock", catalog = "mkyong")
public class Stock implements java.io.Serializable {
... 

批注中的本机 SQL

 @NamedNativeQueries({
	@NamedNativeQuery(
	name = "findStockByStockCodeNativeSQL",
	query = "select * from stock s where s.stock_code = :stockCode",
        resultClass = Stock.class
	)
})
@Entity
@Table(name = "stock", catalog = "mkyong")
public class Stock implements java.io.Serializable {
... 

在原生 SQL 中,你必须声明' resultClass '来让 Hibernate 知道什么是返回类型,否则会导致异常“org . Hibernate . CFG . notyeimplemented exception:尚不支持纯原生标量查询”。

调用命名查询

在 Hibernate 中,可以通过 getNamedQuery 方法调用命名查询。

 Query query = session.getNamedQuery("findStockByStockCode")
.setString("stockCode", "7277"); 
 Query query = session.getNamedQuery("findStockByStockCodeNativeSQL")
.setString("stockCode", "7277"); 

结论

命名查询是全局访问,这意味着查询的名称在 XML 映射文件或注释中必须是唯一的。在实际环境中,将所有命名查询隔离到它们自己的文件中总是一个好的做法。此外,存储在 Hibernate 映射文件或注释中的命名查询比分散在 Java 代码中的查询更容易维护。

hibernate

Hibernate 本地 SQL 查询示例

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-native-sql-queries-examples/

在 Hibernate 中,HQL 或标准查询应该能够让你执行几乎任何你想要的 SQL 查询。然而,许多开发人员抱怨 Hibernate 生成的 SQL 语句太慢,更喜欢生成自己的 SQL (native SQL)语句。

本地 SQL 查询示例

Hibernate 提供了一个 createSQLQuery 方法,让您可以直接调用本地 SQL 语句。

1.在本例中,您告诉 Hibernate 返回一个 Stock.class,所有 select 数据(*)将自动匹配您的 Stock.class 属性。

 Query query = session.createSQLQuery(
"select * from stock s where s.stock_code = :stockCode")
.addEntity(Stock.class)
.setParameter("stockCode", "7277");
List result = query.list(); 

2.在这个例子中,Hibernate 将返回一个对象数组。

 Query query = session.createSQLQuery(
"select s.stock_code from stock s where s.stock_code = :stockCode")
.setParameter("stockCode", "7277");
List result = query.list(); 

或者,您也可以使用命名查询来调用您的本地 SQL 语句。这里的见 Hibernate 命名查询示例。

Hibernate 生成的 SQL 语句很慢!?

我不同意“Hibernate 生成的 SQL 语句很慢”的说法。很多时候,我发现这是因为开发人员没有很好地理解表关系,并且做了一些错误的表映射或者误用了获取策略。这将导致 Hibernate 生成一些不必要的 SQL 语句或加入一些不必要的表…而开发人员喜欢以此为借口,创建自己的 SQL 语句来快速修复 bug,而没有意识到核心问题将导致更多的 bug 等待。

结论

我承认有时候原生 SQL 语句确实比 HQL 更方便、更容易,但是,请仔细想想为什么需要原生 SQL 语句?这真的是冬眠做不到吗?如果是,那就去吧~

P.S 在 Oracle 数据库中,我更喜欢在许多关键性能查询中使用原生 SQL 语句,因为我需要放“提示”来提高 Oracle 查询性能。

hibernate

hibernate–一对多示例(注释)

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-one-to-many-relationship-example-annotation/

在本教程中,它将重用之前的“ Hibernate 一对多关系示例 XML 映射”教程的整个基础设施,并对其进行增强以支持 Hibernate / JPA 注释。

项目结构

查看本教程的新项目结构。

one to many annotation folderNote
Since Hibernate 3.6, annotation codes are merged into the Hibernate core module, so, the previous pom.xml file can be reuse.

1.“一对多”表关系

再看前面的一对多表关系。

one to many table relationship

2.Hibernate 模型类

更新以前的模型类—Stock.javaStockDailyRecord.java,并在里面定义注释代码。

文件:Stock.java

 package com.mkyong.stock;

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 static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
		@UniqueConstraint(columnNames = "STOCK_NAME"),
		@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {

	private Integer stockId;
	private String stockCode;
	private String stockName;
	private Set<StockDailyRecord> stockDailyRecords = new HashSet<StockDailyRecord>(
			0);

	public Stock() {
	}

	public Stock(String stockCode, String stockName) {
		this.stockCode = stockCode;
		this.stockName = stockName;
	}

	public Stock(String stockCode, String stockName,
			Set<StockDailyRecord> stockDailyRecords) {
		this.stockCode = stockCode;
		this.stockName = stockName;
		this.stockDailyRecords = stockDailyRecords;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "STOCK_ID", unique = true, nullable = false)
	public Integer getStockId() {
		return this.stockId;
	}

	public void setStockId(Integer stockId) {
		this.stockId = stockId;
	}

	@Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
	public String getStockCode() {
		return this.stockCode;
	}

	public void setStockCode(String stockCode) {
		this.stockCode = stockCode;
	}

	@Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
	public String getStockName() {
		return this.stockName;
	}

	public void setStockName(String stockName) {
		this.stockName = stockName;
	}

	@OneToMany(fetch = FetchType.LAZY, mappedBy = "stock")
	public Set<StockDailyRecord> getStockDailyRecords() {
		return this.stockDailyRecords;
	}

	public void setStockDailyRecords(Set<StockDailyRecord> stockDailyRecords) {
		this.stockDailyRecords = stockDailyRecords;
	}

} 

文件:StockDailyRecord.java

 package com.mkyong.stock;

import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "stock_daily_record", catalog = "mkyongdb", 
uniqueConstraints = @UniqueConstraint(columnNames = "DATE"))
public class StockDailyRecord implements java.io.Serializable {

	private Integer recordId;
	private Stock stock;
	private Float priceOpen;
	private Float priceClose;
	private Float priceChange;
	private Long volume;
	private Date date;

	public StockDailyRecord() {
	}

	public StockDailyRecord(Stock stock, Date date) {
		this.stock = stock;
		this.date = date;
	}

	public StockDailyRecord(Stock stock, Float priceOpen, Float priceClose,
			Float priceChange, Long volume, Date date) {
		this.stock = stock;
		this.priceOpen = priceOpen;
		this.priceClose = priceClose;
		this.priceChange = priceChange;
		this.volume = volume;
		this.date = date;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "RECORD_ID", unique = true, nullable = false)
	public Integer getRecordId() {
		return this.recordId;
	}

	public void setRecordId(Integer recordId) {
		this.recordId = recordId;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "STOCK_ID", nullable = false)
	public Stock getStock() {
		return this.stock;
	}

	public void setStock(Stock stock) {
		this.stock = stock;
	}

	@Column(name = "PRICE_OPEN", precision = 6)
	public Float getPriceOpen() {
		return this.priceOpen;
	}

	public void setPriceOpen(Float priceOpen) {
		this.priceOpen = priceOpen;
	}

	@Column(name = "PRICE_CLOSE", precision = 6)
	public Float getPriceClose() {
		return this.priceClose;
	}

	public void setPriceClose(Float priceClose) {
		this.priceClose = priceClose;
	}

	@Column(name = "PRICE_CHANGE", precision = 6)
	public Float getPriceChange() {
		return this.priceChange;
	}

	public void setPriceChange(Float priceChange) {
		this.priceChange = priceChange;
	}

	@Column(name = "VOLUME")
	public Long getVolume() {
		return this.volume;
	}

	public void setVolume(Long volume) {
		this.volume = volume;
	}

	@Temporal(TemporalType.DATE)
	@Column(name = "DATE", unique = true, nullable = false, length = 10)
	public Date getDate() {
		return this.date;
	}

	public void setDate(Date date) {
		this.date = date;
	}

} 

3.休眠配置文件

将带注释的类Stock.javaStockDailyRecord.java放在hibernate.cfg.xml中,如下所示:

文件:hibernate.cfg.xml

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyongdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">password</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    <mapping class="com.mkyong.stock.Stock" />
    <mapping class="com.mkyong.stock.StockDailyRecord" />
</session-factory>
</hibernate-configuration> 

4.运行它

运行它,Hibernate 将在 STOCK 表中插入一行,在 STOCK_DAILY_RECORD 表中插入一行。

文件:App.java

 package com.mkyong;

import java.util.Date;

import org.hibernate.Session;

import com.mkyong.stock.Stock;
import com.mkyong.stock.StockDailyRecord;
import com.mkyong.util.HibernateUtil;

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

        System.out.println("Hibernate one to many (Annotation)");
	Session session = HibernateUtil.getSessionFactory().openSession();

	session.beginTransaction();

	Stock stock = new Stock();
        stock.setStockCode("7052");
        stock.setStockName("PADINI");
        session.save(stock);

        StockDailyRecord stockDailyRecords = new StockDailyRecord();
        stockDailyRecords.setPriceOpen(new Float("1.2"));
        stockDailyRecords.setPriceClose(new Float("1.1"));
        stockDailyRecords.setPriceChange(new Float("10.0"));
        stockDailyRecords.setVolume(3000000L);
        stockDailyRecords.setDate(new Date());

        stockDailyRecords.setStock(stock);        
        stock.getStockDailyRecords().add(stockDailyRecords);

        session.save(stockDailyRecords);

	session.getTransaction().commit();
	System.out.println("Done");
	}
} 

输出

 Hibernate one to many (Annotation)
Hibernate: 
    insert 
    into
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.stock_daily_record
        (DATE, PRICE_CHANGE, PRICE_CLOSE, PRICE_OPEN, STOCK_ID, VOLUME) 
    values
        (?, ?, ?, ?, ?, ?)
Done 

Download it – Hibernate-one-to-many-annotation.zip (9KB)

参考

  1. Hibernate 文档–一对多关系

Tags : hibernate one-to-many

hibernate–一对多示例(XML 映射)

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-one-to-many-relationship-example/

当一个实体与另一个实体中的多个事件相关时,就会出现一对多关系。

在本教程中,我们将向您展示如何通过 XML 映射文件(hbm)在 Hibernate 中处理一对多表关系。

本教程中使用的工具和技术:

  1. Hibernate 3.6.3 .最终版
  2. MySQL 5.1.15
  3. Maven 3.0.3
  4. Eclipse 3.6

项目结构

本教程的项目结构。

one to many folder

项目依赖性

从 JBoss 仓库获取 hibernate.jar ,Maven 会为您处理所有相关的依赖关系

文件:pom.xml

 <project ...>

	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>

	<dependencies>

		<!-- MySQL database driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.15</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
		</dependency>

	</dependencies>
</project> 

1.“一对多”示例

这是一个一对多关系表的设计,一个股票表有多个出现次数 STOCK_DAILY_RECORD 表。

one to many table relationship

请参见 MySQL 表脚本

 DROP TABLE IF EXISTS `stock`;
CREATE TABLE `stock` (
  `STOCK_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `STOCK_CODE` varchar(10) NOT NULL,
  `STOCK_NAME` varchar(20) NOT NULL,
  PRIMARY KEY (`STOCK_ID`) USING BTREE,
  UNIQUE KEY `UNI_STOCK_NAME` (`STOCK_NAME`),
  UNIQUE KEY `UNI_STOCK_ID` (`STOCK_CODE`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=34 DEFAULT CHARSET=utf8;

DROP TABLE IF EXISTS `mkyongdb`.`stock_daily_record`;
CREATE TABLE  `mkyongdb`.`stock_daily_record` (
  `RECORD_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `PRICE_OPEN` float(6,2) DEFAULT NULL,
  `PRICE_CLOSE` float(6,2) DEFAULT NULL,
  `PRICE_CHANGE` float(6,2) DEFAULT NULL,
  `VOLUME` bigint(20) unsigned DEFAULT NULL,
  `DATE` date NOT NULL,
  `STOCK_ID` int(10) unsigned NOT NULL,
  PRIMARY KEY (`RECORD_ID`) USING BTREE,
  UNIQUE KEY `UNI_STOCK_DAILY_DATE` (`DATE`),
  KEY `FK_STOCK_TRANSACTION_STOCK_ID` (`STOCK_ID`),
  CONSTRAINT `FK_STOCK_TRANSACTION_STOCK_ID` FOREIGN KEY (`STOCK_ID`) 
  REFERENCES `stock` (`STOCK_ID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=36 DEFAULT CHARSET=utf8; 

2.Hibernate 模型类

创建两个模型类—Stock.javaStockDailyRecord.java,来表示上面的表。

文件:Stock.java

 package com.mkyong.stock;

import java.util.HashSet;
import java.util.Set;

public class Stock implements java.io.Serializable {

	private Integer stockId;
	private String stockCode;
	private String stockName;
	private Set<StockDailyRecord> stockDailyRecords = 
				new HashSet<StockDailyRecord>(0);

	//getter, setter and constructor
} 

文件:StockDailyRecord.java

 package com.mkyong.stock;

import java.util.Date;

public class StockDailyRecord implements java.io.Serializable {

	private Integer recordId;
	private Stock stock;
	private Float priceOpen;
	private Float priceClose;
	private Float priceChange;
	private Long volume;
	private Date date;

	//getter, setter and constructor
} 

3.Hibernate XML 映射

现在,创建两个 Hibernate 映射文件(hbm)—Stock.hbm.xmlStockDailyRecord.hbm.xml

文件:Stock.hbm.xml

 <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.mkyong.stock.Stock" table="stock" catalog="mkyongdb">
        <id name="stockId" type="java.lang.Integer">
            <column name="STOCK_ID" />
            <generator class="identity" />
        </id>
        <property name="stockCode" type="string">
            <column name="STOCK_CODE" length="10" not-null="true" unique="true" />
        </property>
        <property name="stockName" type="string">
            <column name="STOCK_NAME" length="20" not-null="true" unique="true" />
        </property>
        <set name="stockDailyRecords" table="stock_daily_record" 
				inverse="true" lazy="true" fetch="select">
            <key>
                <column name="STOCK_ID" not-null="true" />
            </key>
            <one-to-many class="com.mkyong.stock.StockDailyRecord" />
        </set>
    </class>
</hibernate-mapping> 

文件:StockDailyRecord.hbm.xml

 <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.mkyong.stock.StockDailyRecord" table="stock_daily_record" 
		catalog="mkyongdb">
        <id name="recordId" type="java.lang.Integer">
            <column name="RECORD_ID" />
            <generator class="identity" />
        </id>
        <many-to-one name="stock" class="com.mkyong.stock.Stock" fetch="select">
            <column name="STOCK_ID" not-null="true" />
        </many-to-one>
        <property name="priceOpen" type="java.lang.Float">
            <column name="PRICE_OPEN" precision="6" />
        </property>
        <property name="priceClose" type="java.lang.Float">
            <column name="PRICE_CLOSE" precision="6" />
        </property>
        <property name="priceChange" type="java.lang.Float">
            <column name="PRICE_CHANGE" precision="6" />
        </property>
        <property name="volume" type="java.lang.Long">
            <column name="VOLUME" />
        </property>
        <property name="date" type="date">
            <column name="DATE" length="10" not-null="true" unique="true" />
        </property>
    </class>
</hibernate-mapping> 

4.休眠配置文件

Stock.hbm.xmlStockDailyRecord.hbm.xml放入 Hibernate 配置文件,以及 MySQL 连接细节。

文件:hibernate.cfg.xml

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyongdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">password</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    <property name="format_sql">true</property>
    <mapping resource="com/mkyong/stock/Stock.hbm.xml" />
    <mapping resource="com/mkyong/stock/StockDailyRecord.hbm.xml" />
</session-factory>
</hibernate-configuration> 

5.运行它

运行它,Hibernate 将在 STOCK 表中插入一行,在 STOCK_DAILY_RECORD 表中插入一行。

文件:App.java

 package com.mkyong;

import java.util.Date;

import org.hibernate.Session;

import com.mkyong.stock.Stock;
import com.mkyong.stock.StockDailyRecord;
import com.mkyong.util.HibernateUtil;

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

        System.out.println("Hibernate one to many (XML Mapping)");
	Session session = HibernateUtil.getSessionFactory().openSession();

	session.beginTransaction();

	Stock stock = new Stock();
        stock.setStockCode("7052");
        stock.setStockName("PADINI");
        session.save(stock);

        StockDailyRecord stockDailyRecords = new StockDailyRecord();
        stockDailyRecords.setPriceOpen(new Float("1.2"));
        stockDailyRecords.setPriceClose(new Float("1.1"));
        stockDailyRecords.setPriceChange(new Float("10.0"));
        stockDailyRecords.setVolume(3000000L);
        stockDailyRecords.setDate(new Date());

        stockDailyRecords.setStock(stock);        
        stock.getStockDailyRecords().add(stockDailyRecords);

        session.save(stockDailyRecords);

	session.getTransaction().commit();
	System.out.println("Done");
	}
} 

输出…

 Hibernate one to many (XML Mapping)
Hibernate: 
    insert 
    into
        mkyongdb.stock
        (STOCK_CODE, STOCK_NAME) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        mkyongdb.stock_daily_record
        (STOCK_ID, PRICE_OPEN, PRICE_CLOSE, PRICE_CHANGE, VOLUME, DATE) 
    values
        (?, ?, ?, ?, ?, ?) 

Hibernate Annotation
For one-to-many in Hibernate annotation, please refer to this example.Download it – Hibernate-one-to-many-xml-mapping.zip (10KB)

参考

  1. Hibernate 文档–一对多关系。

Tags : hibernate one-to-many

hibernate–一对一示例(注释)

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-one-to-one-relationship-example-annotation/

在本教程中,它将重用之前的“ Hibernate 一对一关系示例 XML 映射”教程的整个基础设施,并对其进行增强以支持 Hibernate / JPA 注释。

项目结构

请参见本教程的最终项目结构。

project structureNote
Since Hibernate 3.6, annotation codes are merged into the Hibernate core module, so, the [“previous pom.xml](<a href=) file can be reuse.

1.“一对一”表关系

再看前面的一对一表关系。

one to one relationship

2.Hibernate 模型类

创建两个模型类—Stock.javaStockDetail.java,并将注释代码放入其中。

文件:Stock.java

 package com.mkyong.stock;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Table(name = "stock", catalog = "mkyongdb", uniqueConstraints = {
		@UniqueConstraint(columnNames = "STOCK_NAME"),
		@UniqueConstraint(columnNames = "STOCK_CODE") })
public class Stock implements java.io.Serializable {

	private Integer stockId;
	private String stockCode;
	private String stockName;
	private StockDetail stockDetail;

	public Stock() {
	}

	public Stock(String stockCode, String stockName) {
		this.stockCode = stockCode;
		this.stockName = stockName;
	}

	public Stock(String stockCode, String stockName, StockDetail stockDetail) {
		this.stockCode = stockCode;
		this.stockName = stockName;
		this.stockDetail = stockDetail;
	}

	@Id
	@GeneratedValue(strategy = IDENTITY)
	@Column(name = "STOCK_ID", unique = true, nullable = false)
	public Integer getStockId() {
		return this.stockId;
	}

	public void setStockId(Integer stockId) {
		this.stockId = stockId;
	}

	@Column(name = "STOCK_CODE", unique = true, nullable = false, length = 10)
	public String getStockCode() {
		return this.stockCode;
	}

	public void setStockCode(String stockCode) {
		this.stockCode = stockCode;
	}

	@Column(name = "STOCK_NAME", unique = true, nullable = false, length = 20)
	public String getStockName() {
		return this.stockName;
	}

	public void setStockName(String stockName) {
		this.stockName = stockName;
	}

	@OneToOne(fetch = FetchType.LAZY, mappedBy = "stock", cascade = CascadeType.ALL)
	public StockDetail getStockDetail() {
		return this.stockDetail;
	}

	public void setStockDetail(StockDetail stockDetail) {
		this.stockDetail = stockDetail;
	}

} 

文件:StockDetail.java

 package com.mkyong.stock;

import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

@Entity
@Table(name = "stock_detail", catalog = "mkyongdb")
public class StockDetail implements java.io.Serializable {

	private Integer stockId;
	private Stock stock;
	private String compName;
	private String compDesc;
	private String remark;
	private Date listedDate;

	public StockDetail() {
	}

	public StockDetail(Stock stock, String compName, String compDesc,
			String remark, Date listedDate) {
		this.stock = stock;
		this.compName = compName;
		this.compDesc = compDesc;
		this.remark = remark;
		this.listedDate = listedDate;
	}

	@GenericGenerator(name = "generator", strategy = "foreign", 
	parameters = @Parameter(name = "property", value = "stock"))
	@Id
	@GeneratedValue(generator = "generator")
	@Column(name = "STOCK_ID", unique = true, nullable = false)
	public Integer getStockId() {
		return this.stockId;
	}

	public void setStockId(Integer stockId) {
		this.stockId = stockId;
	}

	@OneToOne(fetch = FetchType.LAZY)
	@PrimaryKeyJoinColumn
	public Stock getStock() {
		return this.stock;
	}

	public void setStock(Stock stock) {
		this.stock = stock;
	}

	@Column(name = "COMP_NAME", nullable = false, length = 100)
	public String getCompName() {
		return this.compName;
	}

	public void setCompName(String compName) {
		this.compName = compName;
	}

	@Column(name = "COMP_DESC", nullable = false)
	public String getCompDesc() {
		return this.compDesc;
	}

	public void setCompDesc(String compDesc) {
		this.compDesc = compDesc;
	}

	@Column(name = "REMARK", nullable = false)
	public String getRemark() {
		return this.remark;
	}

	public void setRemark(String remark) {
		this.remark = remark;
	}

	@Temporal(TemporalType.DATE)
	@Column(name = "LISTED_DATE", nullable = false, length = 10)
	public Date getListedDate() {
		return this.listedDate;
	}

	public void setListedDate(Date listedDate) {
		this.listedDate = listedDate;
	}

} 

3.休眠配置文件

将带注释的类Stock.javaStockDetail.java放入 Hibernate 配置文件,以及 MySQL 连接细节。

文件:hibernate.cfg.xml

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyongdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">password</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    <mapping class="com.mkyong.stock.Stock" />
    <mapping class="com.mkyong.stock.StockDetail" />
</session-factory>
</hibernate-configuration> 

4.运行它

运行它,Hibernate 将在 STOCK 表中插入一行,在 STOCK_DETAIL 表中插入一行。

文件:App.java

 package com.mkyong;

import java.util.Date;

import org.hibernate.Session;

import com.mkyong.stock.Stock;
import com.mkyong.stock.StockDetail;
import com.mkyong.util.HibernateUtil;

public class App {
	public static void main(String[] args) {
		System.out.println("Hibernate one to one (Annotation)");
		Session session = HibernateUtil.getSessionFactory().openSession();

		session.beginTransaction();

		Stock stock = new Stock();

		stock.setStockCode("7052");
		stock.setStockName("PADINI");

		StockDetail stockDetail = new StockDetail();
		stockDetail.setCompName("PADINI Holding Malaysia");
		stockDetail.setCompDesc("one stop shopping");
		stockDetail.setRemark("vinci vinci");
		stockDetail.setListedDate(new Date());

		stock.setStockDetail(stockDetail);
		stockDetail.setStock(stock);

		session.save(stock);
		session.getTransaction().commit();

		System.out.println("Done");
	}
} 

输出

 Hibernate one to one (Annotation)
Hibernate: insert into mkyongdb.stock (STOCK_CODE, STOCK_NAME) values (?, ?)
Hibernate: insert into mkyongdb.stock_detail 
(COMP_DESC, COMP_NAME, LISTED_DATE, REMARK, STOCK_ID) values (?, ?, ?, ?, ?)
Done 

Download it – Hibernate-one-to-one-annotation,zip (9KB)

参考

  1. Hibernate 文档–一对一关系

Tags : hibernate one-to-one

hibernate–一对一示例(XML 映射)

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-one-to-one-relationship-example/

当一个实体恰好与另一个实体中的一个事件相关时,就会出现一对一的关系。

在本教程中,我们将向您展示如何通过 XML 映射文件(hbm)在 Hibernate 中处理一对一的表关系。

本教程中使用的工具和技术:

  1. Hibernate 3.6.3 .最终版
  2. MySQL 5.1.15
  3. Maven 3.0.3
  4. Eclipse 3.6

项目结构

请参见本教程的最终项目结构。

one to one project structure ## 项目依赖性

从 JBoss 仓库获取 hibernate.jar ,Maven 会为您处理所有相关的依赖关系。

文件:pom.xml

 <project ...>

	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>

	<dependencies>

		<!-- MySQL database driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.15</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

		<dependency>
			<groupId>javassist</groupId>
			<artifactId>javassist</artifactId>
			<version>3.12.1.GA</version>
		</dependency>

	</dependencies>
</project> 

1.“一对一”表关系

一个一对一的关系表设计,一个股票表在 STOCK_DETAIL 表中正好包含一条记录。两个表都有相同的 Stock_Id 作为主键。在 STOCK_DETAIL 表中,Stock_Id 是主键,也是 STOCK 表的外键。这是定义“一对一”表关系的常用方法。

one to one relationship

要获得 STOCK 和 STOCK_DETAIL 表脚本,请参考这篇 MySQL 中的“一对一表关系”文章。

2.Hibernate 模型类

创建两个模型类—Stock.javaStockDetail.java,来表示上面的表。

文件:Stock.java

 package com.mkyong.stock;

public class Stock implements java.io.Serializable {

	private Integer stockId;
	private String stockCode;
	private String stockName;
	private StockDetail stockDetail;

	//constructor & getter and setter methods

} 

文件:StockDetail.java

 package com.mkyong.stock;

public class StockDetail implements java.io.Serializable {

	private Integer stockId;
	private Stock stock;
	private String compName;
	private String compDesc;
	private String remark;
	private Date listedDate;

	//constructor & getter and setter methods

} 

3.Hibernate XML 映射

现在,创建两个 Hibernate 映射文件(hbm)—Stock.hbm.xmlStockDetail.hbm.xml

文件:Stock.hbm.xml

 <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 25 April 2011 7:52:33 PM by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
	<class name="com.mkyong.stock.Stock" table="stock" catalog="mkyongdb">
		<id name="stockId" type="java.lang.Integer">
			<column name="STOCK_ID" />
			<generator class="identity" />
		</id>
		<property name="stockCode" type="string">
			<column name="STOCK_CODE" length="10" not-null="true" unique="true" />
		</property>
		<property name="stockName" type="string">
			<column name="STOCK_NAME" length="20" not-null="true" unique="true" />
		</property>
		<one-to-one name="stockDetail" class="com.mkyong.stock.StockDetail"
			cascade="save-update"></one-to-one>
	</class>
</hibernate-mapping> 

文件:StockDetail.hbm.xml

 <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 25 April 2011 7:52:33 PM by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping>
	<class name="com.mkyong.stock.StockDetail" table="stock_detail"
		catalog="mkyongdb">
		<id name="stockId" type="java.lang.Integer">
			<column name="STOCK_ID" />
			<generator class="foreign">
				<param name="property">stock</param>
			</generator>
		</id>
		<one-to-one name="stock" class="com.mkyong.stock.Stock"
			constrained="true"></one-to-one>
		<property name="compName" type="string">
			<column name="COMP_NAME" length="100" not-null="true" />
		</property>
		<property name="compDesc" type="string">
			<column name="COMP_DESC" not-null="true" />
		</property>
		<property name="remark" type="string">
			<column name="REMARK" not-null="true" />
		</property>
		<property name="listedDate" type="date">
			<column name="LISTED_DATE" length="10" not-null="true" />
		</property>
	</class>
</hibernate-mapping> 

Note
The main difficulty in this one-to-one relationship is ensuring both are assigned the same primary key. In StockDetail.hbm.xml, a special foreign identifier generator is declared, it will know get the primary key value from STOCK table. With constrained=”true”, it ensure the Stock must exists.

4.休眠配置文件

Stock.hbm.xmlStockDetail.hbm.xml放入 Hibernate 配置文件,以及 MySQL 连接细节。
文件:hibernate.cfg.xml

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
<session-factory>
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyongdb</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">password</property>
    <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
    <property name="show_sql">true</property>
    <mapping resource="com/mkyong/stock/Stock.hbm.xml" />
    <mapping resource="com/mkyong/stock/StockDetail.hbm.xml" />
</session-factory>
</hibernate-configuration> 

5.运行它

运行它,Hibernate 将在 STOCK 表中插入一行,在 STOCK_DETAIL 表中插入一行。

文件:App.java

 package com.mkyong;

import java.util.Date;
import org.hibernate.Session;
import com.mkyong.stock.Stock;
import com.mkyong.stock.StockDetail;
import com.mkyong.util.HibernateUtil;

public class App {
	public static void main(String[] args) {
		System.out.println("Hibernate one to one (XML mapping)");
		Session session = HibernateUtil.getSessionFactory().openSession();

		session.beginTransaction();

		Stock stock = new Stock();

		stock.setStockCode("4715");
		stock.setStockName("GENM");

		StockDetail stockDetail = new StockDetail();
		stockDetail.setCompName("GENTING Malaysia");
		stockDetail.setCompDesc("Best resort in the world");
		stockDetail.setRemark("Nothing Special");
		stockDetail.setListedDate(new Date());

		stock.setStockDetail(stockDetail);
		stockDetail.setStock(stock);

		session.save(stock);
		session.getTransaction().commit();

		System.out.println("Done");
	}
} 

输出

 Hibernate one to one (XML mapping)
Hibernate: insert into mkyongdb.stock (STOCK_CODE, STOCK_NAME) values (?, ?)
Hibernate: insert into mkyongdb.stock_detail 
(COMP_NAME, COMP_DESC, REMARK, LISTED_DATE, STOCK_ID) values (?, ?, ?, ?, ?)
Done 

Hibernate Annotation
For one-to-one in Hibernate annotation, please refer to this exampleDownload it – Hibernate-one-to-one-xml-mapping.zip (10KB)

参考

  1. Hibernate 文档–一对一关系

hibernate one-to-one

Hibernate 参数绑定示例

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-parameter-binding-examples/

如果没有参数绑定,您必须像这样连接参数字符串(错误代码) :

 String hql = "from Stock s where s.stockCode = '" + stockCode + "'";
List result = session.createQuery(hql).list(); 

将用户输入中未经检查的值传递给数据库会引发安全问题,因为它很容易被 SQL 注入破解。您必须避免上述糟糕的代码,而是使用参数绑定。

休眠参数绑定

参数绑定有两种方式:命名参数或位置参数。

1.命名参数

这是最常见和用户友好的方式。它使用冒号后跟参数名(:example)来定义命名参数。查看示例…

示例 1–设置参数

setParameter 足够智能,可以为您发现参数数据类型。

 String hql = "from Stock s where s.stockCode = :stockCode";
List result = session.createQuery(hql)
.setParameter("stockCode", "7277")
.list(); 
示例 2–设置字符串

你可以用 setString 告诉 Hibernate 这个参数日期类型是 String。

 String hql = "from Stock s where s.stockCode = :stockCode";
List result = session.createQuery(hql)
.setString("stockCode", "7277")
.list(); 
示例 3–设置属性

这个功能太棒了!您可以将对象传递到参数绑定中。Hibernate 将自动检查对象的属性并与冒号参数匹配。

 Stock stock = new Stock();
stock.setStockCode("7277");
String hql = "from Stock s where s.stockCode = :stockCode";
List result = session.createQuery(hql)
.setProperties(stock)
.list(); 

2.位置参数

是用问号(?)来定义一个命名参数,并且你必须根据位置序列来设置你的参数。参见示例…

 String hql = "from Stock s where s.stockCode = ? and s.stockName = ?";
List result = session.createQuery(hql)
.setString(0, "7277")
.setParameter(1, "DIALOG")
.list(); 

这种方法不支持 setProperties 功能。此外,它很容易被破坏,因为绑定参数位置的每次改变都需要改变参数绑定代码。

 String hql = "from Stock s where s.stockName = ? and s.stockCode = ?";
List result = session.createQuery(hql)
.setParameter(0, "DIALOG")
.setString(1, "7277")
.list(); 

结论

在 Hibernate 参数绑定中,我建议总是使用“命名参数,因为它更容易维护,并且编译后的 SQL 语句可以重用(如果绑定参数改变的话)以提高性能。

休眠–将图像保存到数据库中

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-save-image-into-database/

要将图像保存到数据库中,您需要在 MySQL 中将表列定义为 blob 数据类型,或者在其他数据库中定义为等效的二进制类型。在 Hibernate 端,你可以声明一个字节数组变量来存储图像数据。

Download this example – Hibernate-Image-Example.zip

下面是一个 Maven 项目,它使用 Hibernate 将图像保存到 MySQL ' avatar 表中。

1.表格创建

MySQL 中的头像表创建脚本。

 CREATE TABLE  `mkyong`.`avatar` (
  `AVATAR_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `IMAGE` blob NOT NULL,
  PRIMARY KEY (`AVATAR_ID`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 

2.Maven 依赖性

添加 Hibernate 和 MySQL 依赖。

pom.xml

 <project  
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
  http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mkyong.common</groupId>
  <artifactId>HibernateExample</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>HibernateExample</name>
  <url>http://maven.apache.org</url>
  <dependencies>

        <dependency>
               <groupId>junit</groupId>
               <artifactId>junit</artifactId>
               <version>3.8.1</version>
               <scope>test</scope>
        </dependency>

        <!-- MySQL database driver -->
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>5.1.9</version>
	</dependency>

	<!-- Hibernate framework -->
	<dependency>
		<groupId>hibernate</groupId>
		<artifactId>hibernate3</artifactId>
		<version>3.2.3.GA</version>
	</dependency>

	<!-- Hibernate library dependecy start -->
	<dependency>
		<groupId>dom4j</groupId>
		<artifactId>dom4j</artifactId>
		<version>1.6.1</version>
	</dependency>

	<dependency>
		<groupId>commons-logging</groupId>
		<artifactId>commons-logging</artifactId>
		<version>1.1.1</version>
	</dependency>

	<dependency>
		<groupId>commons-collections</groupId>
		<artifactId>commons-collections</artifactId>
		<version>3.2.1</version>
	</dependency>

	<dependency>
		<groupId>cglib</groupId>
		<artifactId>cglib</artifactId>
		<version>2.2</version>
	</dependency>
	<!-- Hibernate library dependecy end -->

  </dependencies>
</project> 

3.化身模型

创建一个模型类来存储头像数据。图像数据类型是字节数组。

Avatar.java

 package com.mkyong.common;

public class Avatar implements java.io.Serializable {

	private Integer avatarId;
	private byte[] image;

	public Avatar() {
	}

	public Avatar(byte[] image) {
		this.image = image;
	}

	public Integer getAvatarId() {
		return this.avatarId;
	}

	public void setAvatarId(Integer avatarId) {
		this.avatarId = avatarId;
	}

	public byte[] getImage() {
		return this.image;
	}

	public void setImage(byte[] image) {
		this.image = image;
	}

} 

4.映射文件

为 avatar 创建一个 Hibernate 映射文件。图像的数据类型是二进制。

Avatar.hbm.xml

 <?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.mkyong.common.Avatar" table="avatar" catalog="mkyong">
        <id name="avatarId" type="java.lang.Integer">
            <column name="AVATAR_ID" />
            <generator class="identity" />
        </id>
        <property name="image" type="binary">
            <column name="IMAGE" not-null="true" />
        </property>
    </class>
</hibernate-mapping> 

5.休眠配置文件

定义数据库连接的 Hibernate 配置文件和 Hibernate 映射文件。

hibernate.cfg.xml

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.bytecode.use_reflection_optimizer">false</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">password</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyong</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">true</property>
        <mapping resource="com/mkyong/common/Avatar.hbm.xml"></mapping>
    </session-factory>
</hibernate-configuration> 

6.休眠实用程序

用于获取数据库连接的 Hibernate 实用程序类。

HibernateUtil.java

 package com.mkyong.persistence;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            return new Configuration().configure().buildSessionFactory();
        }
        catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void shutdown() {
    	// Close caches and connection pools
    	getSessionFactory().close();
    }

} 

7.运行它

读取一个文件“C:\ \ mavan-hibernate-image-MySQL . gif”并保存到数据库中,之后从数据库中获取并保存到另一个镜像文件“ C:\test.gif ”。

 package com.mkyong.common;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.hibernate.Session;
import com.mkyong.persistence.HibernateUtil;

public class App 
{
    public static void main( String[] args )
    {
        System.out.println("Hibernate save image into database");
        Session session = HibernateUtil.getSessionFactory().openSession();

        session.beginTransaction();

        //save image into database
    	File file = new File("C:\\mavan-hibernate-image-mysql.gif");
        byte[] bFile = new byte[(int) file.length()];

        try {
	     FileInputStream fileInputStream = new FileInputStream(file);
	     //convert file into array of bytes
	     fileInputStream.read(bFile);
	     fileInputStream.close();
        } catch (Exception e) {
	     e.printStackTrace();
        }

        Avatar avatar = new Avatar();
        avatar.setImage(bFile);

        session.save(avatar);

        //Get image from database
        Avatar avatar2 = (Avatar)session.get(Avatar.class, avatar.getAvatarId());
        byte[] bAvatar = avatar2.getImage();

        try{
            FileOutputStream fos = new FileOutputStream("C:\\test.gif"); 
            fos.write(bAvatar);
            fos.close();
        }catch(Exception e){
            e.printStackTrace();
        }

        session.getTransaction().commit();
    }
} 

完成了。

Tags : hibernate image

hibernate–不推荐使用类型注释配置

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-the-type-annotationconfiguration-is-deprecated/

问题

使用 Hibernate 3.6 时,注意到前面的“org.hibernate.cfg.AnnotationConfiguration”被标记为“,已弃用”。

代码片段……

 import org.hibernate.cfg.AnnotationConfiguration;
//...
private static SessionFactory buildSessionFactory() {
	try {

		return new AnnotationConfiguration().configure().buildSessionFactory();

	} catch (Throwable ex) {

		System.err.println("Initial SessionFactory creation failed." + ex);
		throw new ExceptionInInitializerError(ex);
	}
} 

代码还在工作,只是一直显示不推荐使用的警告信息,AnnotationConfiguration有没有替换?

解决办法

在 Hibernate 3.6 中,“org.hibernate.cfg.AnnotationConfiguration”已被弃用,其所有功能已被移至“org.hibernate.cfg.Configuration”。

因此,您可以安全地用“配置类替换您的“注释配置”。

代码片段……

 import org.hibernate.cfg.Configuration;
//...
private static SessionFactory buildSessionFactory() {
	try {

		return new Configuration().configure().buildSessionFactory();

	} catch (Throwable ex) {

		System.err.println("Initial SessionFactory creation failed." + ex);
		throw new ExceptionInInitializerError(ex);
	}
} 

参考

  1. http://docs . JBoss . org/hibernate/core/3.6/javadocs/org/hibernate/CFG/annotation configuration . html
  2. http://docs . JBoss . org/hibernate/core/3.6/javadocs/org/hibernate/CFG/configuration . html

deprecated hibernate

Hibernate 事务处理示例

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-transaction-handle-example/

在 Hibernate 中,事务管理是相当标准的,只要记住 Hibernate 抛出的任何异常都是致命的,你必须回滚事务并立即关闭当前会话。

下面是一个 Hibernate 事务模板:

 Session session = null;
    	Transaction tx = null;

    	try{
    		session = HibernateUtil.getSessionFactory().openSession();
    		tx = session.beginTransaction();
    		tx.setTimeout(5);

    		//doSomething(session);

    		tx.commit();

    	}catch(RuntimeException e){
    		try{
    			tx.rollback();
    		}catch(RuntimeException rbe){
    			log.error("Couldn’t roll back transaction", rbe);
    		}
    		throw e;
    	}finally{
    		if(session!=null){
    			session.close();
    		}
    	} 

hibernate transaction (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190309052716/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

Hibernate 教程

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/tutorials/hibernate-tutorials/

Hibernate tutorials

Hibernate ,由 Gavin King 创造,被称为 Java 开发者最好的、占主导地位的对象/关系持久化(ORM)工具(现在是 support。网)。它提供了许多优雅和创新的方法来简化 Java 中的关系数据库处理任务。

Hibernate 在很多方面都很棒,但它需要被恰当地使用。在本教程中,它提供了许多使用 Hibernate3 的逐步示例和解释。

页(page 的缩写)的教程更新到 Hibernate v3.6.1.Final。

休眠快速启动

Hello World 示例来体验 Hibernate 框架。

休眠关联(表关系)

Hibernate 中如何定义一对一,一对多,多对五的表关系?

Hibernate / JBoss 工具+ Eclipse IDE

学习如何使用 Hibernate 工具是必须的!

休眠日志记录

如何在 Hibernate 中进行日志记录

休眠连接池

如何在 Hibernate 中配置数据库连接池

休眠级联

Hibernate cascade 用于自动管理对方的状态。

Hibernate 查询语言(HQL)

Hibernate 自己的数据操作语言,它很类似于数据库 SQL 语言。

休眠标准

Hibernate Criteria API 是 Hibernate 查询语言(HQL)的替代产品。在许多可选的搜索标准中,这总是一个好的解决方案。

休眠本机 SQL

在某些场景中,Hibernate HQL 或 Criteria 仅仅是不够的,在这里你可以直接使用原生数据库 SQL 语言。

Hibernate 命名查询

出于可维护性的目的,命名查询允许开发人员将 HQL 放入 XML 映射文件或注释中,您只是不希望所有的 HQL 语法分散在 Java 代码中。🙂

休眠事务

所有与 Hibernate 事务相关的事情

冬眠高级技术

一些 Hibernate 高级技术,很少使用但是实用的技巧(数据过滤器和拦截器)。

休眠性能

一些调整会让你的 Hibernate 运行得更快🙂

  • 动态插入属性示例
    使用动态插入来避免在 SQL INSERT 语句中包含未修改的属性。
  • 动态更新属性示例
    使用动态插入来避免在 SQL UPDATE 语句中包含未修改的属性。
  • Hibernate mutable 示例(类和集合)
    使用 mutable 关键字避免生成不必要的 SQL 语句。
  • Hibernate–抓取策略示例
    Hibernate 抓取策略用于优化 Hibernate 生成的 select 语句,这是任何 Hibernate 开发人员必须学习的技能。
  • 【session.get()和 session.load()
    理解什么时候应该使用 get 或 load 来检索对象以免对数据库造成不必要的冲击。

将 Hibernate 与其他框架集成

Hibernate 与其他框架集成的例子。

休眠常见问题

一些常见的回答问题:

休眠常见错误

以下是 Hibernate 开发中常见错误消息的列表。

跑题了

  • 为什么我的项目选择 Hibernate 框架?
    我喜欢在未来项目中实现 Hibernate 的原因。

休眠引用

Tags : hibernate tutorials

hibernate–如果列名为关键字,如 DESC,则无法插入

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/hibernate-unable-to-insert-if-column-named-is-keyword-such-as-desc/

问题

MySQL 数据库中一个名为“category”的表,包含一个“ DESC 关键字作为列名。

 CREATE TABLE `category` (
  `CATEGORY_ID` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `NAME` varchar(10) NOT NULL,
  `DESC` varchar(255) NOT NULL,
  PRIMARY KEY (`CATEGORY_ID`) USING BTREE
); 

Hibernate XML 映射文件

 <hibernate-mapping>
    <class name="com.mkyong.stock.Category" table="category" catalog="mkyongdb">
        ...
        <property name="desc" type="string">
            <column name="DESC" not-null="true" />
        </property>
       ...
    </class>
</hibernate-mapping> 

或休眠注释

 @Column(name = "DESC", nullable = false)
	public String getDesc() {
		return this.desc;
	} 

当插入到类别表中时,会出现以下错误消息:

 Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: 
   You have an error in your SQL syntax; check the manual that corresponds to your 
   MySQL server version for the right syntax to use near 'DESC) 
   values ('CONSUMER', 'CONSUMER COMPANY')' at line 1
   ... 35 more 

解决办法

在 Hibernate 中,要插入" keyword "列名,应该像这样用'【列名】'括起来。

Hibernate XML 映射文件

 <hibernate-mapping>
    <class name="com.mkyong.stock.Category" table="category" catalog="mkyongdb">
        ...
        <property name="desc" type="string">
            <column name="[DESC]" not-null="true" />
        </property>
       ...
    </class>
</hibernate-mapping> 

或休眠注释

 @Column(name = "[DESC]", nullable = false)
	public String getDesc() {
		return this.desc;
	} 

hibernate insert (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190305140545/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

Struts 隐藏值示例

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts/struts-htmlhidden-hidden-value-example/

Download this Struts hidden value example – Struts-HiddenValue-Example.zip

在这个 Struts 示例中,您将学习如何使用 StrutsHTML:hidden标签创建一个 HTML 隐藏字段。

1.文件夹结构

这是 Maven 创建的最终项目结构。请创建相应的文件夹。

Struts-hidden-value-folder

2.动作类

创建一个 Action 类,除了转发请求什么也不做。

HtmlHiddenAction.java

 package com.mkyong.common.action;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

public class HtmlHiddenAction extends Action{

	public ActionForward execute(ActionMapping mapping,ActionForm form,
			HttpServletRequest request,HttpServletResponse response) 
        throws Exception {

		return mapping.findForward("success");
	}

} 

3.属性文件

创建一个属性文件,并声明错误和标签消息。

公共属性

 #error message
error.common.html.hidden.name.required = Hidden value "Name" is required.

#label message
label.common.html.hidden.button.submit = Submit
label.common.html.hidden.button.reset = Reset 

4.动作形式

创建一个 ActionForm,包含一个名称变量和表单验证–validate()。

html 格式。java

 package com.mkyong.common.form;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;

public class HtmlHiddenForm extends ActionForm{

	String name;

	public String getName() {
		return name;
	}

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

	@Override
	public ActionErrors validate(ActionMapping mapping,
			HttpServletRequest request) {

	    ActionErrors errors = new ActionErrors();

	    if( getName() == null || ("".equals(getName()))) {
	       errors.add("common.name.err",
                  new ActionMessage("error.common.html.hidden.name.required"));
	    }

	    return errors;
	}

	@Override
	public void reset(ActionMapping mapping, HttpServletRequest request) {
		// reset properties
		name = "";
	}

} 

5.JSP 页面

使用 Struts 的 html 标签 < html:hidden > 创建一个 HTML 隐藏值。

hidden.jsp

<%@taglib uri="http://struts.apache.org/tags-html" prefix="html"%>
<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>

Struts html:隐藏示例

A hidden field, please view it from source file.

从 ActionForm 获取隐藏值并显示它

display.jsp

<%@taglib uri="http://struts.apache.org/tags-bean" prefix="bean"%>

隐藏值:

6.struts-config.xml

创建一个 Struts 配置文件,并将它们链接在一起。

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE struts-config PUBLIC 
"-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" 
"http://jakarta.apache.org/struts/dtds/struts-config_1_3.dtd">

<struts-config>

	<form-beans>
		<form-bean
			name="htmlHiddenForm"
			type="com.mkyong.common.form.HtmlHiddenForm"/>

	</form-beans>

	<action-mappings>

	    <action
			path="/HiddenPage"
			type="org.apache.struts.actions.ForwardAction"
			parameter="/pages/hidden.jsp"/>

		<action
			path="/Hidden"
			type="com.mkyong.common.action.HtmlHiddenAction"
			name="htmlHiddenForm"
			validate="true"
			input="/pages/hidden.jsp"
			>	

			<forward name="success" path="/pages/display.jsp"/>
		</action>
	</action-mappings>

	<message-resources
		parameter="com.mkyong.common.properties.Common" />

</struts-config> 

7.web.xml

最后一步,为 Strut 框架集成创建一个 web.xml。

 <!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Maven Struts Examples</display-name>

  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>
        org.apache.struts.action.ActionServlet
    </servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>
         /WEB-INF/struts-config.xml
        </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
       <servlet-name>action</servlet-name>
       <url-pattern>*.do</url-pattern>
  </servlet-mapping>

</web-app> 

访问它

http://localhost:8080/struts example/hidden page . do

Struts-hidden-value-example1

hidden.jspHTML 源代码。


Struts html:隐藏示例

A hidden field, please view it from source file.

按下提交按钮,它将转发到

http://localhost:8080/struts example/hidden . do

并显示隐藏的值。

Struts-hidden-value-example2Tags : hidden value struts

如何在 Java 中计算运行/执行时间

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-do-calculate-elapsed-execute-time-in-java/

在 Java 中,您可以使用以下方法来测量 Java 中的运行时间。

1.System.nanoTime()

这是测量 Java 运行时间的推荐解决方案。

ExecutionTime1.java

 package com.mkyong.time;

import java.util.concurrent.TimeUnit;

public class ExecutionTime1 {

    public static void main(String[] args) throws InterruptedException {

		//start
        long lStartTime = System.nanoTime();

		//task
        calculation();

		//end
        long lEndTime = System.nanoTime();

		//time elapsed
        long output = lEndTime - lStartTime;

        System.out.println("Elapsed time in milliseconds: " + output / 1000000);

    }

    private static void calculation() throws InterruptedException {

        //Sleep 2 seconds
        TimeUnit.SECONDS.sleep(2);

    }
} 

输出可能会有所不同。

 2004 

2.System.currentTimeMillis()

ExecutionTime2.java

 package com.mkyong.time;

import java.util.concurrent.TimeUnit;

public class ExecutionTime2 {

    public static void main(String[] args) throws InterruptedException {

        long lStartTime = System.currentTimeMillis();

        calculation();

        long lEndTime = System.currentTimeMillis();

        long output = lEndTime - lStartTime;

        System.out.println("Elapsed time in milliseconds: " + output);

    }

    private static void calculation() throws InterruptedException {

        //Sleep 2 seconds
        TimeUnit.SECONDS.sleep(2);

    }
} 

输出可能会有所不同。

 2006 

3.Instant.now()。托普奇米利()

在 Java 8 中,你可以尝试新的java.time.Instant

ExecutionTime3.java

 package com.mkyong.time;

import java.time.Instant;
import java.util.concurrent.TimeUnit;

public class ExecutionTime3 {

    public static void main(String[] args) throws InterruptedException {

        long lStartTime = Instant.now().toEpochMilli();

        calculation();

        long lEndTime = Instant.now().toEpochMilli();

        long output = lEndTime - lStartTime;

        System.out.println("Elapsed time in milliseconds: " + output);

    }

    private static void calculation() throws InterruptedException {

        //Sleep 2 seconds
        TimeUnit.SECONDS.sleep(2);

    }
} 

输出可能会有所不同。

 2006 

4.日期()。getTime()

ExecutionTime4.java

 package com.mkyong.time;

import java.util.Date;
import java.util.concurrent.TimeUnit;

public class ExecutionTime4 {

    public static void main(String[] args) throws InterruptedException {

        long lStartTime = new Date().getTime();

        calculation();

        long lEndTime = new Date().getTime();

        long output = lEndTime - lStartTime;

        System.out.println("Elapsed time in milliseconds: " + output);

    }

    private static void calculation() throws InterruptedException {

        //Sleep 2 seconds
        TimeUnit.SECONDS.sleep(2);

    }
} 

输出可能会有所不同。

 2007 

参考

  1. stack overflow–system . nano time()完全没用吗?
  2. stack overflow–system . current time millis vs system . nano time
  3. 系统#nanoTime JavaDoc
  4. Instant # toEpochMilli JavaDoc

如何在 Wicket 中改变 html 文件的位置

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-change-the-html-file-location-wicket/

Wicket 要求 html 和 java 文件位于同一个包目录中。这里我们向你展示如何把 java 和 html 文件放到不同的目录中。

举个例子,

  1. Index.java 位于项目/src/main/Java/com/mkyong
  2. Index.Html 位于项目 /src/main/webapp/pages ,根上下文“/”位于“/src/main/webapp”。

显示动作中的便门

Wicket 定位资源的默认方式使您能够在开发过程中在 Java 文件和标记文件之间快速切换,因为它们紧挨着。此外,使用这种算法,您的包组件可以立即重用,而无需用户配置模板的加载位置;如果可以在类路径中找到组件的类,那么也可以找到它们的资源。这是一个强大的默认设置,在您实现自定义的东西之前,您可能要三思而行

检票口 1.3

如果您仍然坚持定制资源路径,这里我提供了 2 种在 Wicket 1.3 中实现的方法。

1.使用 web 上下文定位资源

创建一个扩展 ResourceStreamLocator 类的类,并覆盖以下函数

  1. 定位(类别分类,字符串路径)
 package com.mkyong;

import java.net.MalformedURLException;
import java.net.URL;

import org.apache.wicket.WicketRuntimeException;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.UrlResourceStream;
import org.apache.wicket.util.resource.locator.ResourceStreamLocator;
import org.apache.wicket.util.string.Strings;

public class MyOwnStreamLocator extends ResourceStreamLocator
{
	@Override
	public IResourceStream locate(Class<?> clazz, String path)
	{

		String location;

		String extension = path.substring(path.lastIndexOf('.') + 1);
		String simpleFileName = Strings.lastPathComponent(clazz.getName(), '.');
		location = "/pages/" + simpleFileName + "." + extension;

		URL url;
		try
		{
			// try to load the resource from the web context
			url = WebApplication.get().getServletContext().getResource(location);

			if (url != null)
			{
				return new UrlResourceStream(url);
			}
		}
		catch (MalformedURLException e)
		{
			throw new WicketRuntimeException(e);
		}

		// resource not found; fall back on class loading
		return super.locate(clazz, path);
	}

} 

Wicket 应用程序类

 package com.mkyong;

import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.util.file.WebApplicationPath;

public class MyApplication extends WebApplication
{    
	public Class<Index> getHomePage()
	{
		return Index.class;
	}

	@Override
	protected void init() {

        getResourceSettings().setResourceStreamLocator(new MyOwnStreamLocator());

	}

} 

2.使用 ResourceFinder 定位资源

创建一个扩展 ResourceStreamLocator 类的类,并覆盖以下两个函数

  1. 定位(类别、字符串路径、字符串样式、区域设置、字符串扩展名)
  2. locateByResourceFinder(类 clazz,字符串路径)
 package com.mkyong;

import java.util.Locale;

import org.apache.wicket.util.file.IResourceFinder;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.locator.ResourceStreamLocator;
import org.apache.wicket.util.string.Strings;

public class MyOwnFinderStreamLocator extends ResourceStreamLocator
{

	private IResourceFinder resourceFinder;

	public void addFinder(IResourceFinder resourceFinder) {
		if (resourceFinder != null) {
			this.resourceFinder = resourceFinder;
		}
	}

	@Override
	public IResourceStream locate(Class<?> clazz, String path, String style,
		Locale locale, String extension) {

		String simpleFileName = 
                Strings.lastPathComponent(clazz.getName(), '.') + "." + extension;

		IResourceStream stream = locate(clazz, simpleFileName);

		if(stream!=null)
			return stream;
		else
			return super.locate(clazz, path,style,locale,extension);

	}

	@Override
	protected IResourceStream locateByResourceFinder(Class<?> clazz, String path) {
		IResourceStream resourceStream = null;

		resourceStream = resourceFinder.find(clazz, path);

		if (resourceStream == null) {
			// try by using class loader
			resourceStream = locateByClassLoader(clazz, path);
		}

		return resourceStream;
	}
} 

Wicket 应用程序类

 package com.mkyong;

import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.util.file.WebApplicationPath;

public class MyApplication extends WebApplication
{    
	public Class<Index> getHomePage()
	{
		return Index.class;
	}

	@Override
	protected void init() {

	  //resource finder
	  MyOwnFinderStreamLocator resourceStreamLocator = new MyOwnFinderStreamLocator();

	  WebApplicationPath webContainerPathFinder = new WebApplicationPath(getServletContext());
	  webContainerPathFinder.add("/pages/");
	  resourceStreamLocator.addFinder(webContainerPathFinder);

	  getResourceSettings().setResourceStreamLocator(resourceStreamLocator);
	}
} 

完成了。

Download It – Wicket-1.3-Resource-Loading.zip (7KB)

检票口 1.4

对于 Wicket 1.4,就更简单了。请参见以下目录结构:

  1. Hello.java 在/src/main/Java/com/mkyong/hello
  2. Hello.html 在/src/main/web app/pages/com/mkyong/hello

wicket change html location

您可以自定义 html 在 init() 方法中的加载位置,如下所示:

 package com.mkyong;

import org.apache.wicket.Page;
import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.settings.IResourceSettings;
import com.mkyong.hello.Hello;

public class MyApplication extends WebApplication {

	@Override
	public Class<? extends Page> getHomePage() {
		return Hello.class; //return default page
	}

	@Override
	protected void init() {
		super.init();

		IResourceSettings resourceSettings = getResourceSettings();
                resourceSettings.addResourceFolder("pages");

	}
} 

Download It – Wicket1.4-resource-loading.zip (8KB)Maven ways
Alternatively, you can control where the HTML files is loaded via Maven resource tag like this :

文件:pom.xml

 <project ...>
	<build>
		<finalName>WicketExamples</finalName>

		<resources>
			<resource>
				<directory>src/main/resources</directory>
			</resource>
			<resource>
				<directory>src/main/webapp/pages</directory>
			</resource>
		</resources>

	</build>
</project> 

以 Maven 的方式,您不需要覆盖 WebApplication init() 方法。

参考

  1. Wicket——控制从加载 HTML 文件的位置

wicket

如何改变 Wicket URL bookmarkablePage 结构?

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-change-wicket-url-bookmarkablepage-structure-url-mounting/

问题

默认情况下,Wicket 生成的 URL 又长又丑,包含页面的全限定类名。它看起来像这样:

 http://localhost:8080/mkyong?wicket:bookmarkablePage=:com.mkyong.page.ResultPage&url=google.com 

Note
What the heck is “wicket:bookmarkablePage” in URL, and why Wicket generated such an ugly URL structure? After deployed Wicket application to client site, many clients’ emails sending in and complaint about the ugly bookmarkablePage URL structure. It’s just sound wired and doesn’t make sense at all, what is the advantages of this? Are you going to ask my visitor to bookmark this ugly URL address?. ## 解决办法

幸运的是,Wicket 提供了" URL mounting "特性,将难看的 URL 可加书签页面隐藏到我们应用程序中的特定路径。

要解决这个问题,请更改 Wicket 应用程序类中默认的丑陋 URL 结构," init() "方法,如下所示

 import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.request.target.coding.QueryStringUrlCodingStrategy;
import com.mkyong.user.SuccessPage;

public class WicketApplication extends WebApplication {

	@Override
	protected void init() {
		super.init();
		mount(new QueryStringUrlCodingStrategy("result",ResultPage.class));
	}
} 

使用QueryStringUrlCodingStrategy()将“ ResultPage.class ”页面挂载到一个整洁友好的 URL 结构“结果,见输出:

 http://localhost:8080/mkyong/result?url=google.com 

url structure wicket (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190114230057/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

在 Wicket 中配置 404 错误页面

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-configure-404-error-page-in-wicket-solution/

总是建议为“404 页面未找到”错误定制一个漂亮的错误页面。本指南向您展示了如何在 Wicket 中配置 404 错误页面。

1.错误页面

为 Wicket 创建错误页面和类。

 package com.mkyong.user;

import org.apache.wicket.PageParameters;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;

public class ErrorPage404 extends WebPage {

	public ErrorPage404(final PageParameters parameters) {

		add(new Label("404", "Page Not Found!"));

	}
} 
 <html>
<body>
	<h1>Wicket Error 404 example</h1>

	<h1><span wicket:id="404"></span></h1> 

</body>
</html> 

2.将 404 放在 web.xml 中

web.xml 文件中指定“404 错误代码”,将常见的“404 错误”路由到 wicket 的“/error404”文件路径。

 <?xml version="1.0" encoding="ISO-8859-1"?>
<web-app ...>

	<filter>
		<filter-name>wicket.wicketTest</filter-name>
		<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
		<init-param>
			<param-name>applicationClassName</param-name>
			<param-value>com.mkyong.WicketApplication</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>wicket.wicketTest</filter-name>
		<url-pattern>/*</url-pattern>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>ERROR</dispatcher>
	</filter-mapping>

	<error-page>
		<error-code>404</error-code>
		<location>/error404</location>
	</error-page>

</web-app> 

Note
Both dispatcher tags “REQUEST” and “ERROR” are required.

3.将错误与页面匹配

在 Wicket 应用程序中,覆盖 init() ,将“ /error404 ”匹配到错误页面“【ErrorPage404.html】”。

 public class WicketApplication extends WebApplication {

	@Override
	protected void init() {

		mount(new QueryStringUrlCodingStrategy("error404",ErrorPage404.class));

	}

} 

4.演示

搞定,现在所有找不到的 URL,“404 页面找不到”错误,会重定向到“/error404”,而“/error404”会显示“ErrorPage404.html”文件

图:如果没有找到 URL(404),显示您的自定义错误页面。

wicket 404Download it – Wicket-404-Example.zip (8KB)Tags : 404 wicket

相关文章

使用 JDBC 驱动程序连接到 PostgreSQL

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/jdbc/how-do-connect-to-postgresql-with-jdbc-driver-java/

一个 JDBC 的例子,展示了如何用 JDBC 驱动程序连接到 PostgreSQL 数据库。

测试者:

  • Java 8
  • PostgreSQL 11
  • PostgreSQL JDBC 驱动程序

1.下载 PostgreSQL JDBC 驱动程序

访问 http://jdbc.postgresql.org/download.html 下载最新的 PostgreSQL JDBC 驱动程序。

postgresql driver

2.JDBC 连接

2.1 建立到 PostgreSQL 数据库的连接。

JDBCExample.java

 import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class JDBCExample {

    public static void main(String[] args) {

		// https://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html#package.description
        // auto java.sql.Driver discovery -- no longer need to load a java.sql.Driver class via Class.forName

        // register JDBC driver, optional, since java 1.6
        /*try {
            Class.forName("org.postgresql.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }*/

        // auto close connection
        try (Connection conn = DriverManager.getConnection(
                "jdbc:postgresql://127.0.0.1:5432/test", "postgres", "password")) {

            if (conn != null) {
                System.out.println("Connected to the database!");
            } else {
                System.out.println("Failed to make connection!");
            }

        } catch (SQLException e) {
            System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
} 

输出,没有驱动?

 > javac JDBCExample.java

> java JDBCExample
SQL State: 08001
No suitable driver found for jdbc:postgresql://127.0.0.1:5432/test 

要用java命令运行它,我们需要手动加载 PostgreSQL JDBC 驱动程序。假设所有东西都存储在c:\db文件夹中,用-cp选项再次运行它。

project layout

 > java -cp "c:\db\postgresql-42.2.5.jar;c:\db" JDBCExample
Connected to the database! 

3.专家

PostgreSQL JDBC 驱动程序可以在 Maven 中央存储库中获得。

pom.xml

 <dependency>
		<groupId>org.postgresql</groupId>
		<artifactId>postgresql</artifactId>
		<version>42.2.5</version>
	</dependency> 

4.JDBC 精选

4.1 从表中获取所有行的另一个 JDBC 示例。

JDBCExample2.java

 package com.mkyong.jdbc;

import com.mkyong.jdbc.model.Employee;

import java.math.BigDecimal;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class JDBCExample2 {

    public static void main(String[] args) {

        List<Employee> result = new ArrayList<>();

        String SQL_SELECT = "Select * from EMPLOYEE";

        // auto close connection and preparedStatement
        try (Connection conn = DriverManager.getConnection(
                "jdbc:postgresql://127.0.0.1:5432/test", "postgres", "password");
             PreparedStatement preparedStatement = conn.prepareStatement(SQL_SELECT)) {

            ResultSet resultSet = preparedStatement.executeQuery();

            while (resultSet.next()) {

                long id = resultSet.getLong("ID");
                String name = resultSet.getString("NAME");
                BigDecimal salary = resultSet.getBigDecimal("SALARY");
                Timestamp createdDate = resultSet.getTimestamp("CREATED_DATE");

                Employee obj = new Employee();
                obj.setId(id);
                obj.setName(name);
                obj.setSalary(salary);
                // Timestamp -> LocalDateTime
                obj.setCreatedDate(createdDate.toLocalDateTime());

                result.add(obj);

            }
            result.forEach(x -> System.out.println(x));

        } catch (SQLException e) {
            System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

} 

Employee.java

 package com.mkyong.jdbc.model;

import java.math.BigDecimal;
import java.time.LocalDateTime;

public class Employee {

    private Long id;
    private String name;
    private BigDecimal salary;
    private LocalDateTime createdDate;

    //...
} 

表定义。

 CREATE TABLE EMPLOYEE
(
    ID serial,
    NAME varchar(100) NOT NULL,
    SALARY numeric(15, 2) NOT NULL,
    CREATED_DATE timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP
    PRIMARY KEY (ID)
); 

下载源代码

$ git clone https://github.com/mkyong/java-jdbc.git

参考

如何在 Java 中将 byte[]数组转换成字符串

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-do-convert-byte-array-to-string-in-java/

在 Java 中,我们可以使用new String(bytes, StandardCharsets.UTF_8)将一个byte[]转换成一个String

 // string to byte[]
  byte[] bytes = "hello".getBytes(StandardCharsets.UTF_8);

  // byte[] to string
  String s = new String(bytes, StandardCharsets.UTF_8); 

目录

1。文本和二进制数据中的字节[]

对于文本或字符数据,我们使用new String(bytes, StandardCharsets.UTF_8)byte[]直接转换为String。然而,对于byte[]保存图像或其他非文本数据等二进制数据的情况,最佳实践是将byte[]转换为 Base64 编码的字符串。

 // convert file to byte[]
  byte[] bytes = Files.readAllBytes(Paths.get("/path/image.png"));

  // Java 8 - Base64 class, finally.

  // encode, convert byte[] to base64 encoded string
  String s = Base64.getEncoder().encodeToString(bytes);

  System.out.println(s);

  // decode, convert base64 encoded string back to byte[]
  byte[] decode = Base64.getDecoder().decode(s);

  // This Base64 encode decode string is still widely use in
  // 1\. email attachment
  // 2\. embed image files inside HTML or CSS 

  • 对于text data byte[],试试new String(bytes, StandardCharsets.UTF_8)
  • 对于binary data byte[],尝试 Base64 编码。

2。将字节[]转换为字符串(文本数据)

以下示例将一个string转换为一个字节数组或byte[],反之亦然。

警告
常见的错误是试图使用bytes.toString()从字节中获取字符串;bytes.toString()只返回对象在内存中的地址,不会将byte[]转换成string!将byte[]转换成string的正确方法是new String(bytes, StandardCharsets.UTF_8)

ConvertBytesToString2.java

 package com.mkyong.string;

import java.nio.charset.StandardCharsets;

public class ConvertBytesToString2 {

  public static void main(String[] args) {

      String str = "This is raw text!";

      // string to byte[]
      byte[] bytes = str.getBytes(StandardCharsets.UTF_8);

      System.out.println("Text : " + str);
      System.out.println("Text [Byte Format] : " + bytes);

      // no, don't do this, it returns the address of the object in memory
      System.out.println("Text [Byte Format] toString() : " + bytes.toString());

      // convert byte[] to string
      String s = new String(bytes, StandardCharsets.UTF_8);
      System.out.println("Output : " + s);

      // old code, UnsupportedEncodingException
      // String s1 = new String(bytes, "UTF_8");

  }

} 

输出

Terminal

 Text : This is raw text!
Text [Byte Format] : [B@372f7a8d
Text [Byte Format] toString() : [B@372f7a8d
Output : This is raw text! 

3。将字节[]转换为字符串(二进制数据)

下面的示例将图像phone.png转换为byte[],并使用 Java 8 Base64类将byte[]转换为 Base64 编码的字符串。

稍后,我们将 Base64 编码的字符串转换回原始的byte[],并将其保存到另一个名为phone2.png的图像中。

ConvertBytesToStringBase64.java

 package com.mkyong.string;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Base64;

public class ConvertBytesToStringBase64 {

    public static void main(String[] args) {

        String filepath = "/Users/mkyong/phone.png";
        Path path = Paths.get(filepath);

        if (Files.notExists(path)) {
            throw new IllegalArgumentException("File is not exists!");
        }

        try {

            // convert the file's content to byte[]
            byte[] bytes = Files.readAllBytes(path);

            // encode, byte[] to Base64 encoded string
            String s = Base64.getEncoder().encodeToString(bytes);
            System.out.println(s);

            // decode, Base64 encoded string to byte[]
            byte[] decode = Base64.getDecoder().decode(s);

            // save into another image file.
            Files.write(Paths.get("/Users/mkyong/phone2.png"), decode);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

} 

输出

Terminal

 bh5aLyZALN4othXL2mByHo1aZA5ts5k/uw/sc7DBngGY......

  # if everything ok, it save the byte[] into a new image phone2.png 

4。下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-string

5。参考文献

gson——如何在 Java 对象和 JSON 之间进行转换

原文:http://web.archive.org/web/20230101150211/https://www.mkyong.com/java/how-do-convert-java-object-to-from-json-format-gson-api/

在本教程中,我们将向您展示如何使用 Gson 在 Java 对象和 JSON 之间进行转换。

页(page 的缩写)所有的例子都经过 Gson 2.8.5 的测试

Note
JSON stands for JavaScript Object Notation, it is a lightweight data-interchange format. You can see many Java applications started to throw away XML format and start using JSON as a new data-interchange format. Java is all about object, often times, you need to convert an object into JSON format for data-interchange or vice verse.Note
Jackson is another high performance JSON processor, try this Jackson 2 – Java object to / from JSON

1.下载 Gson

pom.xml

 <dependency>
		<groupId>com.google.code.gson</groupId>
		<artifactId>gson</artifactId>
		<version>2.8.5</version>
	</dependency> 

2.Gson 基本

toJson()–将 Java 对象转换为 JSON

 Gson gson = new Gson();

	Staff obj = new Staff();

	// 1\. Java object to JSON file
	gson.toJson(obj, new FileWriter("C:\\projects\\staff.json"));

	// 2\. Java object to JSON string
	String jsonInString = gson.toJson(obj); 

fromJson()–将 JSON 转换成 Java 对象

 Gson gson = new Gson();

	// 1\. JSON file to Java object
	Staff staff = gson.fromJson(new FileReader("C:\\projects\\staff.json"), Staff.class);

	// 2\. JSON string to Java object
	String json = "{'name' : 'mkyong'}";
	Staff staff = gson.fromJson(json, Staff.class);

	// 3\. JSON file to JsonElement, later String
	JsonElement json = gson.fromJson(new FileReader("C:\\projects\\staff.json"), JsonElement.class);
    String result = gson.toJson(json); 

3.JSON 的 Java 对象

3.1 一个 Java POJO,后来用这个进行转换。

Staff.java

 package com.mkyong;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class Staff {

    private String name;
    private int age;
    private String[] position;              // array
    private List<String> skills;            // list
    private Map<String, BigDecimal> salary; // map

    //getters and setters
} 

3.2 在 Gson 中,我们可以使用gson.toJson()将 Java 对象转换成 JSON。

GsonExample1.java

 package com.mkyong;

import com.google.gson.Gson;

import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class GsonExample1 {

    public static void main(String[] args) {

        Gson gson = new Gson();

        Staff staff = createStaffObject();

        // Java objects to String
        // String json = gson.toJson(staff);

        // Java objects to File
        try (FileWriter writer = new FileWriter("C:\\projects\\staff.json")) {
            gson.toJson(staff, writer);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private static Staff createStaffObject() {

        Staff staff = new Staff();

        staff.setName("mkyong");
        staff.setAge(35);
        staff.setPosition(new String[]{"Founder", "CTO", "Writer"});
        Map<String, BigDecimal> salary = new HashMap() {{
            put("2010", new BigDecimal(10000));
            put("2012", new BigDecimal(12000));
            put("2018", new BigDecimal(14000));
        }};
        staff.setSalary(salary);
        staff.setSkills(Arrays.asList("java", "python", "node", "kotlin"));

        return staff;

    }

} 

默认情况下,Gson 以紧凑模式编写 JSON。

C:\projects\staff.json

 {"name":"mkyong","age":35,"position":["Founder","CTO","Writer"],"skills":["java","python","node","kotlin"],"salary":{"2018":14000,"2012":12000,"2010":10000}} 

要启用漂亮打印模式:

 import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

	Gson gson = new GsonBuilder().setPrettyPrinting().create(); 

输出

C:\projects\staff.json

 {
  "name": "mkyong",
  "age": 35,
  "position": [
    "Founder",
    "CTO",
    "Writer"
  ],
  "skills": [
    "java",
    "python",
    "node",
    "kotlin"
  ],
  "salary": {
    "2018": 14000,
    "2012": 12000,
    "2010": 10000
  }
} 

4.JSON 到 Java 对象

4.1 在 Gson 中,我们可以使用gson.fromJson将 JSON 转换回 Java 对象。

GsonExample2.java

 package com.mkyong;

import com.google.gson.Gson;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class GsonExample2 {

    public static void main(String[] args) {

        Gson gson = new Gson();

        try (Reader reader = new FileReader("c:\\projects\\staff.json")) {

            // Convert JSON File to Java Object
            Staff staff = gson.fromJson(reader, Staff.class);

			// print staff object
            System.out.println(staff);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

} 

输出

 Staff{name='mkyong', age=35, position=[Founder, CTO, Writer], skills=[java, python, node, kotlin], salary={2018=14000, 2012=12000, 2010=10000}} 

4.2 转换为JsonElement

GsonExample3.java

 package com.mkyong;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class GsonExample3 {

    public static void main(String[] args) {

		// pretty print 
        Gson gson = new GsonBuilder().setPrettyPrinting().create();

        try (Reader reader = new FileReader("c:\\projects\\staff.json")) {

            // Convert JSON to JsonElement, and later to String
            JsonElement json = gson.fromJson(reader, JsonElement.class);

            String jsonInString = gson.toJson(json);

            System.out.println(jsonInString);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

} 

输出

 {
  "name": "mkyong",
  "age": 35,
  "position": [
    "Founder",
    "CTO",
    "Writer"
  ],
  "skills": [
    "java",
    "python",
    "node",
    "kotlin"
  ],
  "salary": {
    "2018": 14000,
    "2012": 12000,
    "2010": 10000
  }
} 

Note
More Gson examples

参考

在 Wicket 中检测浏览器是否支持 javascript 或 ajax?

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-detect-browser-javascript-or-ajax-disabled-in-wicket/

Wicket 内置了许多 ajax 后备组件,如果 Ajax 不可用或 Javascript 被禁用,这些组件就会降级为普通请求。一些很好的例子是 AjaxFallbackButton,AjaxFallbackLink 等。

Note
However there are some components that didn’t implement the fallback behavior, for instance AjaxLazyLoadPanel, it will keep display the Wicket’s default image forever if JavaScript is disabled.

解决办法

实际上,Wicket 有一个内置机制来检测浏览器是否支持 JavaScript。但是,Wicket 默认关闭了这个功能是有原因的。

1.收集您的浏览器信息

在 Wicket 应用程序中,用setgathereextendedbrowserinfo(true)覆盖init(),它告诉 Wicket 从浏览器收集额外信息。

 protected void init() {		
		getRequestCycleSettings().setGatherExtendedBrowserInfo(true);
	} 

2.检测 JavaScript

现在,您可以像这样检测浏览器是否支持 JavaScript:

 WebClientInfo clientInfo = (WebClientInfo)WebRequestCycle.get().getClientInfo();
if(clientInfo.getProperties().isJavaEnabled()){
  //Javascript is enable
}else{
 //Javascript is enable
} 

说明

当用户请求一个页面时,Wicket 将在客户端浏览器中执行一个简单的 JavaScript 重定向测试,以测试浏览器的 JavaScript 是否受支持,并将其保存到用户的会话中。执行重定向测试后,Wicket 将重定向到最初请求的页面。整个过程非常快,但是你会看到一闪而过的空白页。这就是 Wicket 默认关闭它的原因。黑色页面的闪烁真的很讨厌,当你看到这种闪烁页面时,你可能会认为这个网站是一个钓鱼网站:),你的客户也是如此。

Wicket 重定向测试页面是通用的,适用于所有情况,但是空白页面的闪现确实是不可接受的,至少对我来说是这样。

ajax browser javascript wicket

如何在 Wicket 中加密/编码 URL

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-encrypt-encode-url-in-wicket/

在 Wicket 中,编码或加密 URL 是一件非常容易的事情,这个特性是默认提供的,你只需要激活它。

Wicket 默认普通 URL

 http://localhost:8080/WicketExamples/?wicket:interface=:0:urlQueryPanel: 

Wicket 编码或加密的 URL

 http://localhost:8080/WicketExamples/?x=YwbAGQPpvT9MHF2-6S6FwvocqYPuA 

要启用这个特性,请将下面的代码粘贴到 Wicket 的 web 应用程序类中。

 @Override
	protected IRequestCycleProcessor newRequestCycleProcessor() {

		return new WebRequestCycleProcessor() {
			protected IRequestCodingStrategy newRequestCodingStrategy() {
				return new CryptedUrlWebRequestCodingStrategy(
					new WebRequestCodingStrategy());
			}
		};
	} 

查看完整示例…

 package com.mkyong;

import org.apache.wicket.protocol.http.WebApplication;
import org.apache.wicket.protocol.http.WebRequestCycleProcessor;
import org.apache.wicket.protocol.http.request.CryptedUrlWebRequestCodingStrategy;
import org.apache.wicket.protocol.http.request.WebRequestCodingStrategy;
import org.apache.wicket.request.IRequestCodingStrategy;
import org.apache.wicket.request.IRequestCycleProcessor;
import com.mkyong.user.UserPage;

public class WicketApplication extends WebApplication {

	@Override
	public Class<UserPage> getHomePage() {

		return UserPage.class; // return default page
	}

	@Override
	protected IRequestCycleProcessor newRequestCycleProcessor() {

		return new WebRequestCycleProcessor() {
			protected IRequestCodingStrategy newRequestCodingStrategy() {
				return new CryptedUrlWebRequestCodingStrategy(
					new WebRequestCodingStrategy());
			}
		};
	}

} 

完成了。

一点想法

Wicket 使用基于密码的加密机制来编码和解码 URL。所有必需的类都位于"wicket-1.4-rc1-sources . jar \ org \ Apache \ wicket \ util \ crypt"中。我认为最强大的功能是个人会话随机加密密钥,它使用会话和 UUID 来生成它,以便每个访问者使用自己的加密密钥(不同的 http 会话)。

文件:KeyInSessionSunJceCryptFactory.java

 if (key == null)
{
	// generate new key
	key = session.getId() + "." + UUID.randomUUID().toString();
	session.setAttribute(keyAttr, key);
} 

在基于密码的加密机制中,“Salt”和“Iterator count”是公开的,但是使用如上所述的强加密密钥(会话+ UUID ),这使得 wicket 的 URL 编码函数非常难以解码,即使您手头有 Wicket 的源代码。

如果你认为 Wicket 的默认加密机制不够安全,你可以很容易地实现不同的加密机制,如 AESSHA

Note
Wicket’s is using SunJceCrypt class as default URL encode and decode implementation.

参考

  1. Wicket URL 编码策略
  2. Wicket 混淆网址

Tags : url wicket

相关文章

如何在 Wicket 中获取 web 应用程序的根上下文

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-get-root-context-of-a-web-application-in-wicket/

在 Wicket 中,您可以使用下面的方法来获取项目的"根上下文

 WebApplication.get().getServletContext().getServletContextName() 

举个例子,

 public static String getRootContext(){

		String rootContext = "";

		WebApplication webApplication = WebApplication.get();
		if(webApplication!=null){
			ServletContext servletContext = webApplication.getServletContext();
			if(servletContext!=null){
				rootContext = servletContext.getServletContextName();
			}else{
				//do nothing
			}
		}else{
			//do nothing
		}

		return rootContext;

} 

context wicket (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190114230103/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

如何在 Wicket 中获取 ServletContext

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-get-servletcontext-in-wicket/

问题

ServletContext 是一个强大的 serlvet 类,它可以提供关于 web 应用程序的大量信息。有什么方法可以在 Wicket 中获得 ServletContext 类?

回答

是的,您可以通过 Wicket 的 WebApplication 类获得 ServletContext 类,如下所示:

 import javax.servlet.ServletContext;
import org.apache.wicket.Page;
import org.apache.wicket.protocol.http.WebApplication;
import com.mkyong.hello.Hello;

public class CustomApplication extends WebApplication {

	@Override
	public Class<? extends Page> getHomePage() {

		ServletContext servletContext = WebApplication.get().getServletContext();
		return Hello.class; //return default page

	}

} 

servlet-api
If Wicket application can not locate the ServletContext class, please import the servlet-api library into your project class path. For Maven, add this into your pom.xml file.

 <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>servlet-api</artifactId>
       <version>2.4</version>
    </dependency> 

servletcontext wicket (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190304032545/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

Wicket + Kaptcha 集成示例

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-integrate-kaptcha-in-wicket-solution/

Kaptcha 是一个简单易用的 Java 库,用于生成验证码图片验证。在本教程中,我们将向您展示如何通过 Spring 将 Kaptcha 与 Wicket 框架集成。

使用的库:

  1. 2.3.2 节
  2. Wicket v1.4.17
  3. 小门弹簧 v1.4.17
  4. Spring v3.0.5.RELEASE

Note
This article is mainly describes how to integrate Kaptcha with Wicket framework, for Wicket + Spring, please refer to this “Wicket + Spring integration example“.

1.去吧

根据这个线程,主人不喜欢 Maven,所以你必须手动安装这个库到你的本地 Maven 库。

1.在这里得到卡普查图书馆http://code.google.com/p/kaptcha/T2【2】。发出以下 Maven 命令来手动安装它。

 mvn install:install-file -Dfile=c:\kaptcha-2.3.2.jar -DgroupId=com.google.code 
	-DartifactId=kaptcha -Dversion=2.3.2 -Dpackaging=jar 

3.稍后,您可以在 pom.xml 文件中包含 kaptcha。

文件:pom.xml

 <dependency>
		<groupId>com.google.code</groupId>
		<artifactId>kaptcha</artifactId>
		<version>2.3.2</version>
	</dependency> 

2.DefaultKaptcha via Spring

DefaultKaptcha 创建一个春豆,命名为 captchaProducer

文件:applicationContext.xml

 <beans 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

	<bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
		<property name="config">
			<bean class="com.google.code.kaptcha.util.Config">
				<constructor-arg type="java.util.Properties" value="null">
				</constructor-arg>
			</bean>
		</property>
	</bean>

</beans> 

3.CaptchaImage

创建一个扩展 Wicket 的NonCachingImage的 CaptchaImage 类,并使用DynamicImageResource类动态生成 captcha 图像。

文件:CaptchaImage.java

 package com.mkyong.user;

import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import javax.servlet.http.HttpServletRequest;
import org.apache.wicket.Request;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.markup.html.image.NonCachingImage;
import org.apache.wicket.markup.html.image.resource.DynamicImageResource;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.spring.injection.annot.SpringBean;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;

public class CaptchaImage extends NonCachingImage {

	private static final String CAPTCHA_PRODUCER = "captchaProducer";

	// inject via Spring
	@SpringBean
	private DefaultKaptcha captchaProducer;

	// private DefaultKaptcha captchaProducer;
	public CaptchaImage(String id) {
		super(id);

		setImageResource(new DynamicImageResource() {

			public byte[] getImageData() {
				ByteArrayOutputStream os = new ByteArrayOutputStream();

				JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(os);

				try {
					BufferedImage bi = getImageCaptchaService();
					encoder.encode(bi);
					return os.toByteArray();
				} catch (Exception e) {
					throw new RuntimeException(e);
				}

			};

			private BufferedImage getImageCaptchaService() {

				Request request = RequestCycle.get().getRequest();
				HttpServletRequest httpRequest = ((WebRequest) request)
						.getHttpServletRequest();

				String capText = captchaProducer.createText();

				// store the text in the session
				httpRequest.getSession().setAttribute(
						Constants.KAPTCHA_SESSION_KEY, capText);

				// create the image with the text
				BufferedImage bi = captchaProducer.createImage(capText);

				return bi;

			}
		});

	}
} 

4.验证码验证器

创建一个自定义验证器,命名为" CaptchaValidator ",用于验证用户输入并与 Kaptcha 生成的代码进行比较。

文件:CaptchaValidator.java

 package com.mkyong.user;

import javax.servlet.http.HttpServletRequest;
import org.apache.wicket.Request;
import org.apache.wicket.RequestCycle;
import org.apache.wicket.protocol.http.WebRequest;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.validator.AbstractValidator;

public class CaptchaValidator extends AbstractValidator<String> {

	private static final long serialVersionUID = 1L;
	private String INVALID_CODE = "captcha.invalid";

	public void onValidate(IValidatable validatable) {
		String kaptchaReceived = (String) validatable.getValue();

		Request request = RequestCycle.get().getRequest();
		HttpServletRequest httpRequest = ((WebRequest) request)
				.getHttpServletRequest();

		String kaptchaExpected = (String) httpRequest.getSession()
			.getAttribute(com.google.code.kaptcha.Constants.KAPTCHA_SESSION_KEY);

		if (kaptchaReceived == null
				|| !kaptchaReceived.equalsIgnoreCase(kaptchaExpected)) {
			error(validatable);
		}

	}

	// validate on numm value as well
	@Override
	public boolean validateOnNullValue() {

		return true;

	}

	@Override
	protected String resourceKey() {
		return INVALID_CODE;
	}
} 

文件:package.properties

 captcha.invalid = Incorrect answer, type words in image again! 

5.Wicket 组件

将 Kaptcha 与 Wicket 组件集成。

 package com.mkyong.user;

import org.apache.wicket.PageParameters;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.model.PropertyModel;

public class KaptchaPage extends WebPage {

	private String captchaInput;

	public KaptchaPage(final PageParameters parameters) {

		final CaptchaImage captchaImage = new CaptchaImage("kaptchaImage");
		captchaImage.setOutputMarkupId(true);

		TextField<String> captchaTF = new TextField<String>("captcha",
				new PropertyModel<String>(this, "captchaInput"));
		captchaTF.add(new CaptchaValidator());

		Form<?> form = new Form<Void>("form") {
			@Override
			protected void onSubmit() {

				info("Image words are correct!!!");
			};
		};

		form.add(new AjaxFallbackLink("link") {
			@Override
			public void onClick(final AjaxRequestTarget target) {

				captchaImage.detach();

				if (target != null) {
					target.addComponent(captchaImage);
				} else {
					// javascript is disable
				}
			}
		}.add(captchaImage));

		form.add(captchaTF);
		add(form);
		add(new FeedbackPanel("feedback"));
	}

} 

Note
captchaImage.detach(); is enables generate a new captcha image dynamically while user click on the captcha image.

 <html>
<head>
<style>
.feedbackPanelINFO {
	color: green;
}

.feedbackPanelERROR {
	color: red;
}
</style>
</head>
<body>

	<h1>Wicket + Kaptcha integration example</h1>
	<div wicket:id="feedback"></div>
	<form wicket:id="form">
		<a wicket:id="link" title="Refresh image words"> 
		<img wicket:id="kaptchaImage" /></a>
		<br>
		<label>Type the image words :</label>
		<input type="text"wicket:id="captcha">
		<input type="submit" value="Submit" />
	</form>

</body>
</html> 

6.演示

开始并访问—http://localhost:8080/wicket examples/

如果输入不正确:

wicket kaptcha

如果输入正确:

wicket kaptchaDownload it – Wicket-Kaptcha-Integration-Example.zip (10 KB)

参考

  1. Kaptcha 官网
  2. Wicket + Spring 集成示例
  3. 将库包含到本地 Maven 库中

Tags : captcha wicket

相关文章

如何在 Eclipse 中设置 Wicket 示例

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-setup-wicket-examples-in-eclipse/

Wicket examples 是通过示例学习 Apache Wicket 的好地方,也是新的或有经验的 Wicket 开发人员必须参考的网站。在这个 wicket 示例站点中,它几乎包含了通用 Wicket 组件的所有用法。

在本指南中,我们将向您展示如何在您的本地开发环境(Eclipse IDE)中设置上面的 Apache Wicket 示例站点。

使用的工具:

  1. Apache Wicket 1.4.17
  2. Eclipse 3.6
  3. maven3

1.下载源代码

http://wicket.apache.org/下载 Apache Wicket 1.4.17。Wicket 示例代码打包在“src”文件夹中。

2.审查目录

提取下载的 Wicket zip 文件,检查目录结构。文件夹“ wicket-examples ”,它在“$WICKET_PATH/src”文件夹里面就是你所需要的。

wicket example folder structure

3.Maven 构建

导航到“ wicket-examples ”文件夹,用 Maven 编译和构建,并使其支持 Eclipse WTP 特性。

$WICKET_EXAMPLE_FILE_PATH> mvn eclipse:eclipse -Dwtpversion=2.0

P.S Maven 将自动配置项目并下载项目依赖项。

4.Eclipse 项目+ WTP

将项目导入 Eclipse IDE(您应该知道如何进行:)。

然而,在 Wicket 1.4.17 中,Maven " -Dwtpversion=2.0"选项似乎在我的 Eclipse 3.6 中不起作用,因为我注意到 Eclipse facets 文件和部署依赖库没有正确配置。如果您有同样的问题,请执行以下步骤:

1.右键单击项目–>属性–>“项目方面”。选择“动态 Web 模块”和“ Java ”。

wicket example eclipse facets

2.相同的窗口–>选择"部署程序集,确保库和根部署路径"/"配置正确。缺了就补上。

wicket example eclipse depoyment dependency

5.部署+测试

在 Eclipse IDE 中,创建一个 Tomcat 实例,将您配置的“wicket-example”项目分配给新的 Tomcat 实例并启动它。

访问此 URL:http://localhost:8080/wicket-examples/

wicket example cloned site

完成了。整个 wicket examples 站点被克隆到您的本地开发环境中。

Tags : eclipse wicket

相关文章

如何在 Wicket 中使用 AjaxLazyLoadPanel

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-do-use-ajaxlazyloadpanel-in-wicket/

Ajax azionloadpanel 定义:

一个面板,你可以懒加载另一个面板。如果您有一个创建起来相当繁重的面板/组件,并且您首先想要向用户显示页面并在面板准备好时替换它,那么可以使用这种方法。

这个功能真的让人印象深刻。这里我们向你展示如何将一个普通的面板转换成这个强大的AjaxLazyLoadPanel

原始面板

普通边门面板。

 add(new PricePanel("price")); 

惰性负载面板

转换到 Wicket AjaxLazyLoadPanel

 add(new AjaxLazyLoadPanel("price")
{
  @Override
  public Component getLazyLoadComponent(String id)
  {
       return PricePanel(id);
  }
}); 

搞定了,现在 PricePanel 有懒加载效果了。尼斯(法国城市名)

当心!

这个 AjaxLazyLoadPanel 的一个缺点是它不包含后备版本。如果浏览器的 JavaScript 被禁用,懒惰图像将永远保持加载。

这里有一个恶作剧。

1.将以下代码放入 Wicket 的应用程序类中

 protected void init() {		
		getRequestCycleSettings().setGatherExtendedBrowserInfo(true);
} 

2.检查一下

 WebClientInfo clientInfo = (WebClientInfo)WebRequestCycle.get().getClientInfo();
if(clientInfo.getProperties().isJavaEnabled()){
add(new AjaxLazyLoadPanel("price")
{
  @Override
  public Component getLazyLoadComponent(String id)
  {
       return PricePanel("price");
  }
});
}else{
  add(new PricePanel("price"));
} 

如果浏览器支持 JavaScript,上述函数将运行 AjaxLazyLoadPanel 函数,否则委托给正常请求。

参考

  1. 如何在 Wicket 中检测浏览器是否支持 JavaScript

lazy load wicket

如何在 JSF 添加全球导航规则?

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/how-to-add-a-global-navigation-rule-in-jsf/

问题

全局导航规则是适用于所有页面的规则。例如,应用允许每个页面访问“注销”导航规则。有办法在 JSF 配置吗?

解决办法

在 JSF,导航规则支持通配符,您可以使用“ ***** ”来指定适用于所有页面的导航规则。

 <navigation-rule>
   <from-view-id>*</from-view-id>
   <navigation-case>
	<from-outcome>logout</from-outcome>
	<to-view-id>logout.xhtml</to-view-id>
   </navigation-case>
</navigation-rule> 

在本例中,您的 web 应用程序中的所有页面都能够访问“ logout ”导航。

看另一个例子,

 <navigation-rule>
   <from-view-id>/customer/*</from-view-id>
   <navigation-case>
	<from-outcome>validation</from-outcome>
	<to-view-id>customer-validation.xhtml</to-view-id>
   </navigation-case>
</navigation-rule> 

这条规则适用于所有以前缀 /customer/ 开头的页面。

Note
The “” wildcard functionality is limited in JSF, because only one “” is allowed, and it must be at the end of the “form-view-id” string. For example,

它工作了。

 <from-view-id>/customer/*</from-view-id> 

永远比不上…

 <from-view-id>/cus*mer/</from-view-id>
<from-view-id>/c*sto*er/*</from-view-id>
<from-view-id>*/customer</from-view-id> 

jsf jsf2 navigation rule (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190223132221/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

如何以编程方式添加 Hibernate XML 映射文件(hbm.xml)

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/how-to-add-hibernate-xml-mapping-file-hbm-xml-programmatically/

Hibernate XML 映射文件包含 Java 类和数据库表之间的映射关系。这总是被命名为“xx.hbm.xml”,并在 Hibernate 配置文件“hibernate.cfg.xml”中声明。

例如,映射文件(hbm.xml)在“映射标签中声明

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
  <property name="hibernate.bytecode.use_reflection_optimizer">false</property>
  <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  <property name="hibernate.connection.password">password</property>
  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyong</property>
  <property name="hibernate.connection.username">root</property>
  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="show_sql">true</property>
  <mapping resource="com/mkyong/common/Stock.hbm.xml"></mapping>
</session-factory>
</hibernate-configuration> 

以编程方式添加 Hibernate 的映射文件(hbm.xml)

出于任何原因,您不想将映射文件包含在hibernate.cfg.xml中。Hibernate 为开发人员提供了一种以编程方式添加映射文件的方法。

只需修改默认的 Hibernate SessionFactory类,将您的“ hbm.xml ”文件路径作为参数传递给addResource()方法:

 SessionFactory sessionFactory = new Configuration()
   .addResource("com/mkyong/common/Stock.hbm.xml")
   .buildSessionFactory(); 

HibernateUtil.java

HibernateUtil.java完整示例,以编程方式加载 Hibernate XML 映射文件“xx.hbm.xml”。

 import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

	private static final SessionFactory sessionFactory = buildSessionFactory();

	private static SessionFactory buildSessionFactory() {
		try {

			SessionFactory sessionFactory = new Configuration()
					.configure("/com/mkyong/persistence/hibernate.cfg.xml")
					.addResource("com/mkyong/common/Stock.hbm.xml")
					.buildSessionFactory();

			return sessionFactory;

		} catch (Throwable ex) {
			// Make sure you log the exception, as it might be swallowed
			System.err.println("Initial SessionFactory creation failed." + ex);
			throw new ExceptionInInitializerError(ex);
		}
	}

	public static SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public static void shutdown() {
		// Close caches and connection pools
		getSessionFactory().close();
	}

} 

完成了。

hibernate (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190213132705/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

如何在您的 Maven 本地存储库中添加 Oracle JDBC 驱动程序

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/maven/how-to-add-oracle-jdbc-driver-in-your-maven-local-repository/

这里有一个简单的指南,向您展示如何将 Oracle JDBC 驱动程序添加到您的 Maven 本地存储库中,以及如何在pom.xml中引用它

使用 Oracle 数据库 19c 和 Java 8 进行测试

Note
Due to Oracle license restrictions, the Oracle JDBC driver is not available in the public Maven repository. To use the Oracle JDBC driver with Maven, you have to download and install it into your Maven local repository manually.

1.获取 Oracle JDBC 驱动程序

Note
Read this What are the Oracle JDBC releases Vs JDK versions?

访问 Oracle 数据库网站并下载。

Oracle JDBC driverOracle JDBC driver

在本例中,我们为 Java 8 项目选择了 Oracle 数据库 19c 和ojdbc8.jar

Note
Alternatively, you can get the Oracle JDBC driver from the Oracle database installed folder, for example: {ORACLE_HOME}\jdbc\lib\ojdbc8.jar

2.Maven 安装 ojdbc8.jar

2.1 将下载的ojdbc.jar上传或安装到 Maven 本地库。

ojdbc8.jar

 $ mvn install:install-file -Dfile=path/to/your/ojdbc8.jar -DgroupId=com.oracle 
	-DartifactId=ojdbc8 -Dversion=19.3 -Dpackaging=jar 

对于旧版本。

ojdbc7.jar

 $ mvn install:install-file -Dfile=path/to/your/ojdbc7.jar -DgroupId=com.oracle 
	-DartifactId=ojdbc7 -Dversion=12.2.0.1 -Dpackaging=jar 

ojdbc6.jar

 $ mvn install:install-file -Dfile=path/to/your/ojdbc6.jar -DgroupId=com.oracle 
	-DartifactId=ojdbc6 -Dversion=11.2.0.4 -Dpackaging=jar 

Note
The -Dversion= is depends on your database version, in this example, we are using Oracle database 19c, so put -Dversion=19.3

2.2 安装一个ojdbc8.jar的完整示例

Terminal

 C:\> mvn install:install-file -Dfile=d:/projects/ojdbc8.jar -DgroupId=com.oracle -DartifactId=ojdbc8 -Dversion=19.3 -Dpackaging=jar
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] --- maven-install-plugin:2.4:install-file (default-cli) @ standalone-pom ---
[INFO] Installing d:\projects\ojdbc8.jar to C:\Users\mkyong\.m2\repository\com\oracle\ojdbc8\19.3\ojdbc8-19.3.jar
[INFO] Installing C:\Users\mkyong\AppData\Local\Temp\mvninstall14285592711568231406.pom 
		to C:\Users\mkyong\.m2\repository\com\oracle\ojdbc8\19.3\ojdbc8-19.3.pom
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.872 s
[INFO] Finished at: 2019-06-20T12:36:18+08:00
[INFO] ------------------------------------------------------------------------ 

3.pom.xml

现在,我们可以这样定义 Oracle JDBC 驱动程序依赖关系:

pom.xml

 <dependency>
		<groupId>com.oracle</groupId>
		<artifactId>ojdbc8</artifactId>
		<version>19.3</version>
	</dependency> 

对于旧版本:

pom.xml

 <!-- ojdbc7.jar -->
	<dependency>
		<groupId>com.oracle</groupId>
		<artifactId>ojdbc7</artifactId>
		<version>12.2.0.1</version>
	</dependency>

	<!-- ojdbc6.jar -->
	<dependency>
		<groupId>com.oracle</groupId>
		<artifactId>ojdbc6</artifactId>
		<version>11.2.0.4</version>
	</dependency> 

4.系统路径

或者,我们可以下载.jar并告诉项目在系统路径中找到.jar,如下所示:

pom.xml

 <dependency>
		<groupId>com.oracle</groupId>
		<artifactId>ojdbc</artifactId>
		<version>8</version>
		<scope>system</scope>
		<systemPath>d:/projects/ojdbc8.jar</systemPath>
	</dependency> 

pom.xml

 <dependency>
		<groupId>com.oracle</groupId>
		<artifactId>ojdbc</artifactId>
		<version>8</version>
		<scope>system</scope>
		<systemPath>${project.basedir}/lib/ojdbc8.jar</systemPath>
	</dependency> 

下载源代码

$ git clone https://github.com/mkyong/java-jdbc.git

参考

如何在 Maven 中添加远程存储库

原文:http://web.archive.org/web/20230101150211/https://www.mkyong.com/maven/how-to-add-remote-repository-in-maven-pom-xml/

并不是每个库都存储在 Maven Central Repository 中,有些库只在 Java.net 或 JBoss repository(远程库)中可用。

1.Java.net 知识库

pom.xml

 <repositories>
        <repository>
            <id>java-net-repo</id>
            <url>https://maven.java.net/content/repositories/public/</url>
        </repository>     
  </repositories> 

2.JBoss 仓库

pom.xml

 <repositories>
        <repository>
            <id>jboss-repo</id>
            <url>http://repository.jboss.org/nexus/content/groups/public/</url>
        </repository>
  </repositories> 

3.Spring 知识库

pom.xml

 <repositories>
        <repository>
            <id>spring-repo</id>
            <url>https://repo.spring.io/release</url>
        </repository>
  </repositories> 

参考

  1. 配置 Maven 使用 JBoss 存储库
  2. 泉库

如何在 jQuery 中动态添加/删除 CSS 类

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-add-remove-css-class-dynamically-in-jquery/

jQuery 自带 addClass()removeClass() 来动态添加或移除 CSS 类。举个例子,

  1. $('#para1 ')。addClass(“突出显示”);–向包含 id“para 1”的元素添加“highlight”CSS 类。
  2. $('#para1 ')。removeClass('高亮');–从包含 id“para 1”的元素中删除“highlight”CSS 类。

例子

 <html>
<head>
<style type="text/css">
  .highlight { 
  	background:green;
  }
 </style>
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
</head>
<body>
  <h1>jQuery add / remove css class example</h1>

  <p id="para1">This is paragraph 1</p>
  <p>This is paragraph 2</p>
  <p>This is paragraph 3</p>
  <p>This is paragraph 4</p>

  <button id="addClass">Add highlight</button>
  <button id="removeClass">Remove highlight</button>

<script type="text/javascript">

    $("#addClass").click(function () {

	  $('#para1').addClass('highlight');

    });

    $("#removeClass").click(function () {

	  $('#para1').removeClass('highlight');

    });

</script>
</body>
</html> 

http://web.archive.org/web/20190224165922if_/http://www.mkyong.com/wp-content/uploads/jQuery/jQuery-add-remove-css-class.html

Try Democss jquery (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190224165922/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

如何用 jQuery 动态添加/删除文本框

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-add-remove-textbox-dynamically-with-jquery/

在 jQuery 中,动态添加或删除文本框非常容易。这个想法很简单,只是结合使用 'counter '变量,jQuery createElement()html()remove() 方法。请参见下面的示例:

jQuery 动态文本框示例

 <html>
<head>
<title>jQuery add / remove textbox example</title>

<script type="text/javascript" src="jquery-1.3.2.min.js"></script>

<style type="text/css">
	div{
		padding:8px;
	}
</style>

</head>

<body>

<h1>jQuery add / remove textbox example</h1>

<script type="text/javascript">

$(document).ready(function(){

    var counter = 2;

    $("#addButton").click(function () {

	if(counter>10){
            alert("Only 10 textboxes allow");
            return false;
	}   

	var newTextBoxDiv = $(document.createElement('div'))
	     .attr("id", 'TextBoxDiv' + counter);

	newTextBoxDiv.after().html('<label>Textbox #'+ counter + ' : </label>' +
	      '<input type="text" name="textbox' + counter + 
	      '" id="textbox' + counter + '" value="" >');

	newTextBoxDiv.appendTo("#TextBoxesGroup");

	counter++;
     });

     $("#removeButton").click(function () {
	if(counter==1){
          alert("No more textbox to remove");
          return false;
       }   

	counter--;

        $("#TextBoxDiv" + counter).remove();

     });

     $("#getButtonValue").click(function () {

	var msg = '';
	for(i=1; i<counter; i++){
   	  msg += "\n Textbox #" + i + " : " + $('#textbox' + i).val();
	}
    	  alert(msg);
     });
  });
</script>
</head><body>

<div id='TextBoxesGroup'>
	<div id="TextBoxDiv1">
		<label>Textbox #1 : </label><input type='textbox' id='textbox1' >
	</div>
</div>
<input type='button' value='Add Button' id='addButton'>
<input type='button' value='Remove Button' id='removeButton'>
<input type='button' value='Get TextBox Value' id='getButtonValue'>

</body>
</html> 

http://web.archive.org/web/20221225130025if_/https://www.mkyong.com/wp-content/uploads/jQuery/jQuery-add-remove-textbox.html

Try Demo

如何在 JSF 数据表中添加行

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/how-to-add-row-in-jsf-datatable/

这个例子增强了前面的删除数据表行的例子,通过添加一个“add”函数在数据表中添加一行。

下面是一个 JSF 2.0 的例子,向您展示如何在数据表中添加一行。

1.受管 Bean

一个名为“order”的托管 bean,不言自明。

 package com.mkyong;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean(name="order")
@SessionScoped
public class OrderBean implements Serializable{

	private static final long serialVersionUID = 1L;

	String orderNo;
	String productName;
	BigDecimal price;
	int qty;

	//getter and setter methods

	private static final ArrayList<Order> orderList = 
		new ArrayList<Order>(Arrays.asList(

		new Order("A0001", "Intel CPU", 
				new BigDecimal("700.00"), 1),
		new Order("A0002", "Harddisk 10TB", 
				new BigDecimal("500.00"), 2),
		new Order("A0003", "Dell Laptop", 
				new BigDecimal("11600.00"), 8),
		new Order("A0004", "Samsung LCD", 
				new BigDecimal("5200.00"), 3),
		new Order("A0005", "A4Tech Mouse", 
				new BigDecimal("100.00"), 10)
	));

	public ArrayList<Order> getOrderList() {

		return orderList;

	}

	public String addAction() {

		Order order = new Order(this.orderNo, this.productName, 
			this.price, this.qty);

		orderList.add(order);
		return null;
	}

	public String deleteAction(Order order) {

		orderList.remove(order);
		return null;
	}

	public static class Order{

		String orderNo;
		String productName;
		BigDecimal price;
		int qty;

		public Order(String orderNo, String productName, 
				BigDecimal price, int qty) {
			this.orderNo = orderNo;
			this.productName = productName;
			this.price = price;
			this.qty = qty;
		}

		//getter and setter methods
	}
} 

2.JSF·佩奇

显示带有 dataTable 标记的数据的 JSF 页面,以及键入订单数据的条目表单。

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html    
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      >
    <h:head>
    	<h:outputStylesheet library="css" name="table-style.css"  />
    </h:head>
    <h:body>

    	<h1>JSF 2 dataTable example</h1>
    	<h:form>
    		<h:dataTable value="#{order.orderList}" var="o"
    			styleClass="order-table"
    			headerClass="order-table-header"
    			rowClasses="order-table-odd-row,order-table-even-row"
    		>

    		<h:column>

    			<f:facet name="header">Order No</f:facet>
    			#{o.orderNo}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Product Name</f:facet>
    			#{o.productName}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Price</f:facet>
    			#{o.price}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Quantity</f:facet>
    			#{o.qty}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Action</f:facet>

    			<h:commandLink value="Delete" 
                                   action="#{order.deleteAction(o)}" />

    		</h:column>

    		</h:dataTable>

    		<h2>Enter Order</h2>
    		<table>
    		<tr>
    			<td>Order No :</td>
    			<td><h:inputText size="10" value="#{order.orderNo}" /></td>
    		</tr>
    		<tr>
    			<td>Product Name :</td>
    			<td><h:inputText size="20" value="#{order.productName}" /></td>
    		</tr>
    		<tr>
    			<td>Quantity :</td>
    			<td><h:inputText size="5" value="#{order.price}" /></td>
    		</tr>
    		<tr>
    			<td>Price :</td>
    			<td><h:inputText size="10" value="#{order.qty}" /></td>
    		</tr>
    		</table>

    		<h:commandButton value="Add" action="#{order.addAction}" />

    	</h:form>
    </h:body>
</html> 

3.演示

从上到下,显示正在添加的行记录。

jsf2-dataTable-Add-Example-1

jsf2-dataTable-Add-Example-2

jsf2-dataTable-Add-Example-3

下载源代码

Download It – JSF-2-DataTable-Add-Example.zip (10KB)Tags : datatable insert jsf2freestar.config.enabled_slots.push({ placementName: "mkyong_leaderboard_btf", slotId: "mkyong_leaderboard_btf" });

相关文章

如何在 Mac OS X 上运行 Apache Ant

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/ant/how-to-apache-ant-on-mac-os-x/

在本教程中,我们将向您展示如何在 Mac OSX 上安装 Apache Ant。

工具:

  1. Apache Ant 1.9.4
  2. mac os x yosemite 10.10

Preinstalled Apache Ant?
In older version of Mac, Apache Ant may be already installed by default, check if Apache Ant is installed :

 $ ant -v 

1.获取阿帕奇蚂蚁

访问 Apache Ant 网站,获取. tar.gz 文件。

install-apache-ant-on-mac-osx

2.提取它

将下载的 gz 文件复制到您喜欢的位置,提取它。

 $ cp ~/Downloads/apache-ant-1.9.4-bin.tar.gz .

$ cd ~
$ pwd
/Users/mkyong

$ tar vxf apache-ant-1.9.4-bin.tar.gz

x apache-ant-1.9.4/bin/ant
x apache-ant-1.9.4/bin/antRun
x apache-ant-1.9.4/bin/antRun.pl
x apache-ant-1.9.4/bin/complete-ant-cmd.pl
x apache-ant-1.9.4/bin/runant.pl
x apache-ant-1.9.4/bin/runant.py
x apache-ant-1.9.4/
x apache-ant-1.9.4/bin/
......

$ cd ~/apache-ant-1.9.4/bin
$ pwd
/Users/mkyong/apache-ant-1.9.4/bin

$ ant -v
Apache Ant(TM) version 1.9.4 compiled on April 29 2014
Trying the default build file: build.xml
Buildfile: build.xml does not exist!
Build failed 

另外,Apache Ant 命令可以在文件夹$APACHE_ANT_FOLDER/bin中找到。

3.环境变量

将命令ant设置为环境变量,这样您就可以在任何地方“ant”构建您的项目。

 $ vim ~/.bash_profile 

导出$ANT_HOME/bin,保存并重启终端。

~/.bash_profile

 export JAVA_HOME=$(/usr/libexec/java_home)
export GRADLE_HOME=/Users/mkyong/gradle
export M2_HOME=/Users/mkyong/apache-maven-3.1.1

# Apache Ant
export ANT_HOME=/Users/mkyong/apache-ant-1.9.4

# Export to PATH
export PATH=$PATH:$GRADLE_HOME/bin:$M2_HOME/bin:$ANT_HOME/bin 

再测试一次,现在,你可以在任何地方访问ant命令。

 $ cd ~
$ pwd
/Users/mkyong

$ ant -v
Apache Ant(TM) version 1.9.4 compiled on April 29 2014
Trying the default build file: build.xml
Buildfile: build.xml does not exist!
Build failed 

完成了。

参考

  1. Apache Ant 下载页面
  2. 如何在 Mac OSX 上安装 Apache Maven
  3. Linux : gzip 一个文件夹

如何在 Java 中向文件追加文本

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-append-content-to-file-in-java/

本文展示了如何使用以下 Java APIs 将文本追加到文件末尾。

  1. Files.write–向文件追加一行,Java 7。
  2. Files.write–在一个文件中追加多行,Java 7,Java 8。
  3. Files.writeString–Java 11。
  4. FileWriter
  5. FileOutputStream
  6. FileUtils——Apache common io。

在 Java 中,对于像Files.write这样的 NIO APIs,我们可以使用 StandardOpenOption。追加启用追加模式。例如:

 // append a string to the end of the file
	private static void appendToFile(Path path, String content)
		  throws IOException {

			Files.write(path,
							content.getBytes(StandardCharsets.UTF_8),
							StandardOpenOption.CREATE,
							StandardOpenOption.APPEND);

	} 

对于像FileWriterFileOutputStream这样的经典 IO APIs,我们可以向构造函数的第二个参数传递一个true来启用追加模式。例如:

 // append to the file
	try (FileWriter fw = new FileWriter(file, true);
       BufferedWriter bw = new BufferedWriter(fw)) {
      bw.write(content);
      bw.newLine();
  }

	// append to the file
	try (FileOutputStream fos = new FileOutputStream(file, true)) {
      fos.write(content.getBytes(StandardCharsets.UTF_8));
  } 

1.向文件追加一行——files . write

如果文件不存在,API 抛出NoSuchFileException

 Files.write(path, content.getBytes(StandardCharsets.UTF_8),
	                StandardOpenOption.APPEND); 

更好的解决方案总是结合了StandardOpenOption.CREATEStandardOpenOption.APPEND。如果文件不存在,API 将创建文本并写入文件;如果文件存在,将文本追加到文件的末尾。

 Files.write(path, content.getBytes(StandardCharsets.UTF_8),
	                StandardOpenOption.CREATE,
	                StandardOpenOption.APPEND); 

1.1 下面的例子展示了如何在一个文件的末尾添加一行。

FileAppend1.java

 package com.mkyong.io.file;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class FileAppend1 {

    private static final String NEW_LINE = System.lineSeparator();

    public static void main(String[] args) throws IOException {

        Path path = Paths.get("/home/mkyong/test/abc.txt");
        appendToFile(path, "hello world" + NEW_LINE);

    }

    // Java 7
    private static void appendToFile(Path path, String content)
				throws IOException {

        // if file not exists throws java.nio.file.NoSuchFileException
        /* Files.write(path, content.getBytes(StandardCharsets.UTF_8),
						StandardOpenOption.APPEND);*/

        // if file not exists, create and write to it
				// otherwise append to the end of the file
        Files.write(path, content.getBytes(StandardCharsets.UTF_8),
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND);

    }

} 

输出

第一次运行。

/home/mkyong/test/abc.txt

 hello world 

运行第二次。

/home/mkyong/test/abc.txt

 hello world
hello world 

2.将多行追加到文件–files . write

Files.write也支持多行的Iterable接口,我们可以在一个文件中添加一个List

 // append lines of text
    private static void appendToFileJava8(Path path, List<String> list)
			throws IOException {

        // Java 7?
        /*Files.write(path, list, StandardCharsets.UTF_8,
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND);*/

        // Java 8, default utf_8
        Files.write(path, list,
                StandardOpenOption.CREATE,
                StandardOpenOption.APPEND);

    } 

3.Java 11–追加模式下的 Files.writeString。

在 Java 7 中,我们需要将一个String转换成一个byte[],并将其写入或追加到一个文件中。

 String content = "...";

	Files.write(path, content.getBytes(StandardCharsets.UTF_8),
					StandardOpenOption.CREATE,
					StandardOpenOption.APPEND); 

在 Java 11 中,我们可以使用新的Files.writeString API 将字符串直接写入或追加到文件中。追加模式的工作方式相同。

 // Java 11, writeString, append mode
  private static void appendToFileJava11(Path path, String content)
			throws IOException {

      // utf_8
      /*Files.writeString(path, content, StandardCharsets.UTF_8,
              StandardOpenOption.CREATE,
              StandardOpenOption.APPEND);*/

      // default StandardCharsets.UTF_8
      Files.writeString(path, content,
              StandardOpenOption.CREATE,
              StandardOpenOption.APPEND);
  } 

4.文件写入器

对于像FileWriter这样的遗留 IO APIs,构造函数的第二个参数表示追加模式。

 // append
	// if file not exists, create and write
	// if the file exists, append to the end of the file
	try (FileWriter fw = new FileWriter(file, true);
			 BufferedWriter bw = new BufferedWriter(fw)) {

			bw.write(content);
			bw.newLine();   // add new line, System.lineSeparator()

	} 

4.1 下面的例子显示了如何使用FileWriter在文件末尾添加一行。

FileAppend4.java

 package com.mkyong.io.file;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class FileAppend4 {

	public static void main(String[] args) throws IOException {

			File file = new File("/home/mkyong/test/abc.txt");
			appendToFileFileWriter(file, "hello world");
			System.out.println("Done");

	}

	private static void appendToFileFileWriter(File file, String content)
			throws IOException {

			// default - create and write
			// if file not exists, create and write
			// if file exists, truncate and write
			/*try (FileWriter fw = new FileWriter(file);
					 BufferedWriter bw = new BufferedWriter(fw)) {
					bw.write(content);
					bw.newLine();
			}*/

			// append mode
			// if file not exists, create and write
			// if file exists, append to the end of the file
			try (FileWriter fw = new FileWriter(file, true);
					 BufferedWriter bw = new BufferedWriter(fw)) {

					bw.write(content);
					bw.newLine();   // add new line, System.lineSeparator()

			}

	}

} 

4.2 以下示例将一个List或多行追加到文件末尾。

 private static void appendToFileFileWriter(
			File file, List<String> content) throws IOException {

      try (FileWriter fw = new FileWriter(file, true);
           BufferedWriter bw = new BufferedWriter(fw)) {

          for (String s : content) {
              bw.write(s);
              bw.newLine();
          }
      }

  } 

5.文件输出流

FileOutputStream的附加模式与FileWriter的工作方式相同。

 private static void appendToFileFileOutputStream(File file, String content)
			throws IOException {

      // append mode
      try (FileOutputStream fos = new FileOutputStream(file, true)) {
          fos.write(content.getBytes(StandardCharsets.UTF_8));
      }

  } 

6. FileUtils

下面的例子使用流行的 Apache commons-io FileUtils.writeStringToFile在文件末尾添加一个字符串。

pom.xml

 <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.7</version>
  </dependency> 
 import org.apache.commons.io.FileUtils;

    private static void appendToFileFileUtils(File file, String content)
			  throws IOException {

				// append mode
        FileUtils.writeStringToFile(
                file, content, StandardCharsets.UTF_8, true);

    } 

审核FileUtils.writeStringToFile签名;最后一个或第四个参数表示追加模式。

 public static void writeStringToFile(final File file, final String data,
				final Charset charset,final boolean append)
				throws IOException {

      try (OutputStream out = openOutputStream(file, append)) {
          IOUtils.write(data, out, charset);
      }

  } 

7.在过去。

在 Java 7 之前,我们可以使用FileWriter将文本添加到文件中,并手动关闭资源。

ClassicBufferedWriterExample.java

 package com.mkyong;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class ClassicBufferedWriterExample {

    public static void main(String[] args) {

        BufferedWriter bw = null;
        FileWriter fw = null;

        try {

            String content = "Hello";

            fw = new FileWriter("app.log", true);
            bw = new BufferedWriter(fw);
            bw.write(content);

        } catch (IOException e) {
            System.err.format("IOException: %s%n", e);
        } finally {
            try {
                if (bw != null)
                    bw.close();

                if (fw != null)
                    fw.close();
            } catch (IOException ex) {
                System.err.format("IOException: %s%n", ex);
            }
        }
    }
} 

附:以上代码只是为了好玩和遗留目的,始终坚持 try-with-resources 关闭资源。

参考

如何在 Struts 2 中自动选择下拉框值

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts2/how-to-auto-select-drop-down-box-value-in-struts-2/

在 Struts 2 中,HTML 下拉框可以通过 < s:select > 标签呈现。要自动选择下拉框的默认值,只需在 < s:select > 标签中声明一个 value 属性,并相应地设置默认值。

1.Java 列表示例

为下拉框生成选择选项的 Java 列表。

 //...
public class SelectAction extends ActionSupport{

	private List<String> searchEngine;
	private String yourSearchEngine;

	//set default value
	public String getDefaultSearchEngine() {
		return "yahoo.com";
	}
	public SelectAction(){	
		searchEngine = new ArrayList<String>();
		searchEngine.add("google.com");
		searchEngine.add("bing.com");
		searchEngine.add("yahoo.com");
		searchEngine.add("baidu.com");
	}
	//...
} 

< s:选择> 标签渲染 HTML 下拉框。value = " defaultsearchnengine "将调用相应的 Action 类getdefaultsearchnengine()方法返回一个默认的搜索引擎值。

 <s:select label="What's your favor search engine" 
		headerKey="-1" headerValue="Select Search Engines"
		list="searchEngine" 
		name="yourSearchEngine" 
		value="defaultSearchEngine" /> 

在本例中,下拉框将自动选择“yahoo.com”作为默认选项。

2.OGNL 列表示例

通过 OGNL 表达式创建一个下拉框,直接在“属性中设置默认值。

 <s:select label="Select a month" 
		headerKey="-1" headerValue="Select Month"
		list="#{'1':'Jan', '2':'Feb', '3':'Mar', '4':'Apr'}" 
		name="yourMonth" 
		value="2" /> 

在本例中,下拉框将自动选择“2”(Feb)作为默认选项。

Download It – Struts2-Select-DropDown-Box-Example.zipdropdown struts2 (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190304030851/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

如何在 Java web 服务客户机中绕过证书检查

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/webservices/jax-ws/how-to-bypass-certificate-checking-in-a-java-web-service-client/

在 Java web 服务开发环境中,开发人员总是使用keytool来生成测试证书。在进行客户端测试时,web 服务测试客户端经常会遇到以下错误消息:

  1. Java . security . cert . certificate 异常:找不到与本地主机匹配的名称
  2. SunCertPathBuilderException:无法找到请求目标的有效认证路径

这里有一个源代码,是我从马丁·卡琳的书《Java Web Services:启动并运行,第一版》中复制过来的。一个在测试环境中非常有用的代码唯一,推荐学习并收藏备查:)

Warning
Don’t try it at production environment, unless you have a very solid reason to by pass all the certificate checking. Ans if yes, why are you still using SSL connection? 😃

 package com.mkyong.client;

import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.io.*;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class HttpsClient{

  public static void main(String[] args)
  {
     new HttpsClient().testIt();
  }

  private TrustManager[ ] get_trust_mgr() {
     TrustManager[ ] certs = new TrustManager[ ] {
        new X509TrustManager() {
           public X509Certificate[ ] getAcceptedIssuers() { return null; }
           public void checkClientTrusted(X509Certificate[ ] certs, String t) { }
           public void checkServerTrusted(X509Certificate[ ] certs, String t) { }
         }
      };
      return certs;
  }

  private void testIt(){
     String https_url = "https://localhost:8443/HelloWorld/hello?wsdl";
     URL url;
     try {

	    // Create a context that doesn't check certificates.
            SSLContext ssl_ctx = SSLContext.getInstance("TLS");
            TrustManager[ ] trust_mgr = get_trust_mgr();
            ssl_ctx.init(null,                // key manager
                         trust_mgr,           // trust manager
                         new SecureRandom()); // random number generator
            HttpsURLConnection.setDefaultSSLSocketFactory(ssl_ctx.getSocketFactory());

	    url = new URL(https_url);
	    HttpsURLConnection con = (HttpsURLConnection)url.openConnection();

	    // Guard against "bad hostname" errors during handshake.
            con.setHostnameVerifier(new HostnameVerifier() {
                public boolean verify(String host, SSLSession sess) {
                    if (host.equals("localhost")) return true;
                    else return false;
                }
            });

	    //dumpl all cert info
	    print_https_cert(con);

	    //dump all the content
	    print_content(con);

	 } catch (MalformedURLException e) {
		e.printStackTrace();
	 } catch (IOException e) {
		e.printStackTrace();
	 }catch (NoSuchAlgorithmException e) {
		e.printStackTrace();
	 }catch (KeyManagementException e) {
		e.printStackTrace();
      }	
   }

  private void print_https_cert(HttpsURLConnection con){
     if(con!=null){

     try {

	System.out.println("Response Code : " + con.getResponseCode());
	System.out.println("Cipher Suite : " + con.getCipherSuite());
	System.out.println("\n");

	Certificate[] certs = con.getServerCertificates();
	for(Certificate cert : certs){
	  System.out.println("Cert Type : " + cert.getType());
	  System.out.println("Cert Hash Code : " + cert.hashCode());
	  System.out.println("Cert Public Key Algorithm : " + cert.getPublicKey().getAlgorithm());
	  System.out.println("Cert Public Key Format : " + cert.getPublicKey().getFormat());
	  System.out.println("\n");
	}

     } catch (SSLPeerUnverifiedException e) {
	  e.printStackTrace();
     } catch (IOException e){
	  e.printStackTrace();
     }	   
   }		
  }

  private void print_content(HttpsURLConnection con){
    if(con!=null){

    try {

	System.out.println("****** Content of the URL ********");

	BufferedReader br = 
		new BufferedReader(
			new InputStreamReader(con.getInputStream()));

	String input;

	while ((input = br.readLine()) != null){
	   System.out.println(input);
	}
	br.close();

     } catch (IOException e) {
	e.printStackTrace();
     }		
   }
  }
} 

jax-ws web services (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190303105144/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

AJAX 更新后如何调用 Javscript,Wicket

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-to-call-javscript-after-ajax-update-wicket/

问题

根据这个这个检票口的文章。如果我们想在每次 Ajax 更新后运行某段 Javascript 代码,我们可以像这样使用“wicketGlobalPostCallHandler”:

 wicketGlobalPostCallHandler = function {
  alert('successful partial update');
} 

然而,这个解决方案对我来说并不奏效。

解决办法

或者,您可以使用“ajaxRequestTarget”来附加 Javascrip 代码:

 ajaxRequestTarget.appendJavascript("alert('hello');"); 

ajax javascript wicket (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190112014028/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

如何在 Hibernate 中调用存储过程

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/how-to-call-store-procedure-in-hibernate/

在本教程中,您将学习如何在 Hibernate 中调用存储过程。

MySQL 存储过程

这是一个 MySQL 存储过程,它接受股票代码参数并返回相关的股票数据。

 DELIMITER $$

CREATE PROCEDURE `GetStocks`(int_stockcode varchar(20))
BEGIN
   SELECT * FROM stock where stock_code = int_stockcode;
   END $$

DELIMITER ; 

在 MySQL 中,你可以用 call 关键字简单调用它:

 CALL GetStocks('7277'); 

休眠调用存储过程

在 Hibernate 中,有三种方法可以调用数据库存储过程。

1.原生 SQL–createsql query

可以使用 createSQLQuery() 直接调用存储过程。

 Query query = session.createSQLQuery(
	"CALL GetStocks(:stockCode)")
	.addEntity(Stock.class)
	.setParameter("stockCode", "7277");

List result = query.list();
for(int i=0; i<result.size(); i++){
	Stock stock = (Stock)result.get(i);
	System.out.println(stock.getStockCode());
} 

2.批注中的 NamedNativeQuery

@NamedNativeQueries 注释中声明您的存储过程。

 //Stock.java
...
@NamedNativeQueries({
	@NamedNativeQuery(
	name = "callStockStoreProcedure",
	query = "CALL GetStocks(:stockCode)",
	resultClass = Stock.class
	)
})
@Entity
@Table(name = "stock")
public class Stock implements java.io.Serializable {
... 

getNamedQuery() 调用它。

 Query query = session.getNamedQuery("callStockStoreProcedure")
	.setParameter("stockCode", "7277");
List result = query.list();
for(int i=0; i<result.size(); i++){
	Stock stock = (Stock)result.get(i);
	System.out.println(stock.getStockCode());
} 

3.XML 映射文件中的 sql 查询

在" sql-query "标记中声明您的存储过程。

 <!-- Stock.hbm.xml -->
...
<hibernate-mapping>
    <class name="com.mkyong.common.Stock" table="stock" ...>
        <id name="stockId" type="java.lang.Integer">
            <column name="STOCK_ID" />
            <generator class="identity" />
        </id>
        <property name="stockCode" type="string">
            <column name="STOCK_CODE" length="10" not-null="true" unique="true" />
        </property>
        ...
    </class>

    <sql-query name="callStockStoreProcedure">
	<return alias="stock" class="com.mkyong.common.Stock"/>
	<![CDATA[CALL GetStocks(:stockCode)]]>
    </sql-query>

</hibernate-mapping> 

getNamedQuery() 调用它。

 Query query = session.getNamedQuery("callStockStoreProcedure")
	.setParameter("stockCode", "7277");
List result = query.list();
for(int i=0; i<result.size(); i++){
	Stock stock = (Stock)result.get(i);
	System.out.println(stock.getStockCode());
} 

结论

以上三种方法做的是同样的事情,在数据库中调用一个存储过程。这三种方法没有太大的区别,你选择哪种方法取决于你个人的喜好。

hibernate stored procedure

Java–更新文件的上次修改日期

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-change-the-file-last-modified-date-in-java/

在 Java 中,我们可以使用 NIO Files.setLastModifiedTime(path, FileTime)来更新文件的最后修改日期或时间。

 Path path = Paths.get("/path/file");

  LocalDate newLocalDate = LocalDate.of(1997, 12, 31);

  // year, month, dayOfMonth, hour, minute, second
  LocalDateTime newLocalDateTime = LocalDateTime.of(1999, 9, 30, 10, 30, 22);

  // convert LocalDateTime to Instant
  Instant instant = newLocalDateTime.toInstant(ZoneOffset.UTC);

  // convert LocalDate to Instant, need a time zone
  Instant instant = newLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant();

  // convert instant to filetime
  // update last modified time of a file
  Files.setLastModifiedTime(path, FileTime.from(instant)); 

1。Java NIO——更新文件的上次修改日期。

对于 Java NIO java.nio.file.Files,我们可以使用Files.setLastModifiedTime()来更新文件的最后修改日期。

UpdateLastModifiedTime.java

 package com.mkyong.io.howto;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;

public class UpdateLastModifiedTime {

    public static void main(String[] args) throws IOException {

        String fileName = "c:\\test\\google.png";

        Path file = Paths.get(fileName);
        BasicFileAttributes attr = Files.readAttributes(file, BasicFileAttributes.class);
        FileTime lastModifiedTime = attr.lastModifiedTime();

        // print original last modified time
        System.out.println("[original] lastModifiedTime:" + lastModifiedTime);

        LocalDate newLocalDate = LocalDate.of(1998, 9, 30);
        // convert LocalDate to instant, need time zone
        Instant instant = newLocalDate.atStartOfDay(ZoneId.systemDefault()).toInstant();

        // convert instant to filetime
        // update last modified time
        Files.setLastModifiedTime(file, FileTime.from(instant));

        // read last modified time again
        FileTime newLastModifiedTime = Files.readAttributes(file,
                BasicFileAttributes.class).lastModifiedTime();
        System.out.println("[updated] lastModifiedTime:" + newLastModifiedTime);

    }

} 

输出

Terminal

 [original] lastModifiedTime:2020-12-01T07:23:48.405691Z
[updated] lastModifiedTime:1998-09-29T16:00:00Z 

查看Files.setLastModifiedTime方法签名,新的修改时间是一个 FileTime

Files.java

 package java.nio.file;

  //...
  public static Path setLastModifiedTime(Path path, FileTime time)
       throws IOException
   {
       getFileAttributeView(path, BasicFileAttributeView.class)
           .setTimes(Objects.requireNonNull(time), null, null);
       return path;
   } 

我们可以使用FileTime.from(Instant instant)将一个 Java 8 Instant转换成一个FileTime

 Files.setLastModifiedTime(file, FileTime.from(instant)); 

FileTime.fromMillis(long value)将从 epoch 开始的毫秒数转换为FileTime

 FileTime now = FileTime.fromMillis(System.currentTimeMillis());
  Files.setLastModifiedTime(path, now); 

2。传统 IO—更新文件的上次修改时间。

对于遗留 IO java.io.File,我们可以使用File.setLastModified()来更新最后修改日期。

UpdateLastModifiedTime2.java

 package com.mkyong.io.howto;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class UpdateLastModifiedTime2 {

    private static final SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");

    public static void main(String[] args) throws ParseException {

        String fileName = "c:\\test\\google.png";

        File file = new File(fileName);

        // print original last modified time
        System.out.println("[original] lastModifiedTime:" + sdf.format(file.lastModified()));

        //need convert the above date to milliseconds in long value
        Date newLastModified = sdf.parse("31/08/1998");

        // update last modified date
        file.setLastModified(newLastModified.getTime());

        //print the latest last modified date
        System.out.println("[updated] lastModifiedTime:" + sdf.format(file.lastModified()));

    }

} 

输出

Terminal

 [original] lastModifiedTime:30/09/1998
[updated] lastModifiedTime:31/08/1998 

查看File.setLastModified方法签名;自纪元(1970 年 1 月 1 日 00:00:00 GMT)以来,参数long time以毫秒为单位进行测量。

File.java

 package java.io;

  //...
  public boolean setLastModified(long time) {
      if (time < 0) throw new IllegalArgumentException("Negative time");
      SecurityManager security = System.getSecurityManager();
      if (security != null) {
          security.checkWrite(path);
      }
      if (isInvalid()) {
          return false;
      }
      return fs.setLastModifiedTime(this, time);
  } 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考文献

如何将 Wicket 改为部署模式?

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-to-change-wicket-to-deployment-mode/

问题

默认情况下,Wicket 运行在开发模式下。如何切换到生产模式?

 WebApplication:759 - [WicketApplication] Started Wicket version 1.4.17 in development mode
********************************************************************
*** WARNING: Wicket is running in DEVELOPMENT mode.              ***
***                               ^^^^^^^^^^^                    ***
*** Do NOT deploy to your live server(s) without changing this.  ***
*** See Application#getConfigurationType() for more information. ***
******************************************************************** 

解决办法

据我所知,有两种方法可以将 Wicket 更改为在部署(生产)模式下运行:

 WebApplication:759 - [WicketApplication] Started Wicket version 1.4.17 in deployment mode 

1.web.xml

第一种方法是在 web.xml 中添加一个“配置”上下文参数。

文件:web.xml

 <web-app ...>

	<context-param>
		<param-name>configuration</param-name>
		<param-value>deployment</param-value>
	</context-param>

	...
</web-app> 

2.Wicket getConfigurationType()

第二种方法是覆盖 Wicket 应用程序的getConfigurationType()方法。

文件:Wicket 应用程序类

 import org.apache.wicket.Application;
import org.apache.wicket.protocol.http.WebApplication;

public class WicketApplication extends WebApplication {

	@Override
	public String getConfigurationType() {

		return Application.DEPLOYMENT;

	}
} 

Note
The Wicket application always has the highest priority. For example, if web.xml is “development” and Wicket application is “deployment“, Wicket will run in “deployment” mode.deployment wicket

如何在 Java 中检查文件是否存在

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-check-if-a-file-exists-in-java/

在 Java 中,我们可以使用Files.exists(path)来测试一个文件是否存在。path可以是一个文件或一个目录。最好结合!Files.isDirectory(path)来保证现有文件不是一个目录。

 Path path = Paths.get("/home/mkyong/test/test.log");

  // file exists and it is not a directory
  if(Files.exists(path) && !Files.isDirectory(path)) {
      System.out.println("File exists!");
  } 

1.Files.exists(路径)(NIO)

这个例子使用Files.exists(path)来测试一个文件是否存在。

FileExist.java

 package com.mkyong.io.file;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileExist {

    public static void main(String[] args) {

        Path path = Paths.get("/home/mkyong/test/test.log");

        // check exists for file and directory
        if (Files.exists(path)) {

            if (Files.isRegularFile(path)) {
                System.out.println("File exists!");
            }
            if (Files.isDirectory(path)) {
                System.out.println("File exists, but it is a directory.");
            }

        } else {
            System.out.println("File doesn't exist");
        }

    }
} 

1.2 如果文件是一个符号链接或软链接,默认情况下Files.exists将跟随该链接。Files.exists有两个参数,我们可以通过第二个LinkOption.NOFOLLOW_LINKS参数告诉方法停止跟踪符号链接。

 import java.nio.file.Files;
import java.nio.file.LinkOption;

    // do not follow symbolic link
    if(Files.exists(path, LinkOption.NOFOLLOW_LINKS)){
      //...
    } 

1.3 有一个Files.notExists()用来测试不存在的文件。

 if(Files.notExists(path)){
      System.out.println("File doesn't exist");
  } 

2.File.exists(旧 IO)

遗留 IO java.io.File有一个类似的File.exists()来测试文件或目录是否存在,但是它不支持符号链接。

FileExist2.java

 package com.mkyong.io.file;

import java.io.File;

public class FileExist2 {

    public static void main(String[] args) {

        File file = new File("/home/mkyong/test/");

        if(file.exists() && !file.isDirectory()){
            System.out.println("File exists!");
        }else{
            System.out.println("File doesn't exist");
        }

    }

} 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何用 jQuery 检查一个元素是否有某个类名

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-check-if-an-element-has-a-certain-class-name-with-jquery/

jQuery 提供了两种方法来检查元素是否有特定的类名。这两种方法具有相同的功能。

  1. 是('。类名’)
  2. hasClass('。类名’)

例如,检查 div 元素的类名是否为“redColor”。

1.是('。类名’)

 $('div').is('.redColor') 

2.hasClass('。类名’)

 $('div').hasClass('.redColor') 

例子

如果 div 元素的类名为“redColor ”,那么将其类更改为“blueColor”。

 <html>
<head>
<style type="text/css">
  .redColor { 
  	background:red;
  }
  .blueColor { 
  	background:blue;
  }
 </style>
<script type="text/javascript" src="jquery-1.3.2.min.js"></script>
</head>
<body>
  <h1>jQuery check if an element has a certain class</h1>

  <div class="redColor">This is a div tag with class name of "redColor"</div>

  <p>
  <button id="isTest">is('.redColor')</button>
  <button id="hasClassTest">hasClass('.redColor')</button>
  <button id="reset">reset</button>
  </p>
<script type="text/javascript">

    $("#isTest").click(function () {

	  if($('div').is('.redColor')){
	  	$('div').addClass('blueColor');
	  }

    });

    $("#hasClassTest").click(function () {

	  if($('div').hasClass('.redColor')){
	  	$('div').addClass('blueColor');
	  }

    });

	$("#reset").click(function () {
	  location.reload();
    });

</script>
</body>
</html> 

http://web.archive.org/web/20190310101146if_/http://www.mkyong.com/wp-content/uploads/jQuery/jQuery-check-has-css-class.html

Try Demojquery

如何检查 jQuery 中是否存在元素

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-check-if-an-element-is-exists-in-jquery/

在 jQuery 中,可以使用。length 属性来检查元素是否存在。如果元素存在,length 属性将返回匹配元素的总数。

举个例子,

 if($('#div1').length){
	alert("Div1 exists");
}else{
	alert("Div1 does not exists");
} 

检查 id 为“div1”的元素是否存在。

jQuery 长度示例

 <html>
<head>

<script type="text/javascript" src="jquery-1.3.2.min.js"></script>

</head>

<body>

<h1>jQuery check if an element exists</h1>

<script type="text/javascript">

  $(document).ready(function(){

    $("#buttonDiv1").click(function () {

	if($('#div1').length){
		alert("Div1 exists");
	}else{
		alert("Div1 does not exists");
	}

    });

    $("#buttonDiv2").click(function () {

	if($('#div2').length){
		alert("Div2 exists");
	}else{
		alert("Div2 does not exists");
	}

    });

  });
</script>
</head><body>

<div id="div1">
	<b>This is DIV element which has an ide of "div1"</b>
</div>

<br/>
<br/>
<br/>

<input type='button' value='div1 exists?' id='buttonDiv1'>
<input type='button' value='div2 exists?' id='buttonDiv2'>

</body>
</html> 

http://web.archive.org/web/20200210125142if_/http://www.mkyong.com/wp-content/uploads/jQuery/jQuery-check-is-element-exists.html

Try Demojquery

如何用 jQuery 检查是否按下了回车键

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-check-if-an-enter-key-is-pressed-with-jquery/

“输入”键由代码“13”表示,检查这个 ASCII 图表

要检查文本框内是否按下了“enter”键,只需将 keypress()绑定到文本框。

 $('#textbox').keypress(function(event){

	var keycode = (event.keyCode ? event.keyCode : event.which);
	if(keycode == '13'){
		alert('You pressed a "enter" key in textbox');	
	}

}); 

要检查页面上是否按下了回车键,请将按键()绑定到 jQuery $(文档)。

 $(document).keypress(function(event){

	var keycode = (event.keyCode ? event.keyCode : event.which);
	if(keycode == '13'){
		alert('You pressed a "enter" key in somewhere');	
	}

}); 

在 Firefox 中,你必须使用 event.which 来获取键码;而 IE 同时支持事件.键码事件.哪个

你自己试试

 <html>
<head>

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>

</head>
<body>
  <h1>Check if "enter" is pressed with jQuery</h1>

<label>TextBox : </label>
<input id="textbox" type="text" size="50" />

<script type="text/javascript">

$('#textbox').keypress(function(event){

	var keycode = (event.keyCode ? event.keyCode : event.which);
	if(keycode == '13'){
		alert('You pressed a "enter" key in textbox');	
	}
	event.stopPropagation();
});

$(document).keypress(function(event){

	var keycode = (event.keyCode ? event.keyCode : event.which);
	if(keycode == '13'){
		alert('You pressed a "enter" key in somewhere');	
	}

});

</script>
</body>
</html> 

http://web.archive.org/web/20211204045900if_/https://www.mkyong.com/wp-content/uploads/jQuery/jQuery-check-enter-key-is-pressed.html

Try Demo

如何检查图片是否加载了 jQuery

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-check-if-an-image-is-loaded-with-jquery/

要检查图片加载是否成功,可以结合使用 jQuery ' load() 和' error() 事件:

 $('#image1')
	.load(function(){
		$('#result1').text('Image is loaded!');	
	})
	.error(function(){
		$('#result1').text('Image is not loaded!');
	}); 

如果图像加载成功,则调用 load() 函数,否则调用 error() 函数。

你自己试试

 <html>
<head>

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>

 <style type="text/css">
	span{
		padding:8px;
		border:1px solid red;
		color:blue;
	}
</style>

</head>

<body>

<h1>Check if image is loaded jQuery</h1>
<p>
<img id="image1" 
src="http://static.jquery.com/files/rockimg/logo_jquery_215x53.gif"/>
<p>Load image from 
"http://static.jquery.com/files/rockimg/logo_jquery_215x53.gif"</p>

<span id="result1"></span>
</p>

<p>
<img id="image2" src="xxxx.jpg"/>
<p>Load image from "xxxx.jpg"</p>

<span id="result2"></span>
</p>

<script type="text/javascript">

$('#image1')
	.load(function(){
		$('#result1').text('Image is loaded!');	
	})
	.error(function(){
		$('#result1').text('Image is not loaded!');
	});

$('#image2')
	.load(function(){
		$('#result2').text('Image is loaded!');	
	})
	.error(function(){
		$('#result2').text('Image is not loaded!');
	});
</script>

</body>
</html> 

http://web.archive.org/web/20190303234728if_/http://www.mkyong.com/wp-content/uploads/jQuery/jQuery-check-if-image-loaded.html

Try Demoimage jquery (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190303234728/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

如何检查 jQuery 库是否加载?

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-check-if-jquery-library-is-loaded/

要检查 jQuery 库是否已加载,请使用以下 JavaScript 代码:

 if (typeof jQuery != 'undefined') {

    alert("jQuery library is loaded!");

}else{

    alert("jQuery library is not found!");

} 

另类?

在一些博客和论坛中提到了以下替代代码:

 if (jQuery) {  

   alert("jQuery library is loaded!");

} else {

   alert("jQuery library is not found!");

} 

然而,这是行不通的,当没有加载 jQuery 库时,这段代码将失败( jQuery 没有定义),并且警告消息将永远不会执行。

最后,总是推荐第一种 jQuery 检查方法。

如何在 Java 中比较日期

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-compare-dates-in-java/

本文展示了几个用 Java 比较两个日期的例子。用 Java 8 例子更新。

1。比较两个日期

对于遗产java.util.Date,我们可以用compareTobefore()after()equals()来比较两个日期。

1.1 日期比较

下面的例子使用Date.compareTo来比较 Java 中的两个java.util.Date

  • 如果参数日期等于Date,返回值 0。
  • 如果Date在参数日期之后,返回值大于 0 或为正。
  • 如果Date在参数日期之前,返回值小于 0 或负数。

查看Date.compareTo方法签名。

Date.java

 package java.util;

public class Date
  implements java.io.Serializable, Cloneable, Comparable<Date> {

  public int compareTo(Date anotherDate) {
      long thisTime = getMillisOf(this);
      long anotherTime = getMillisOf(anotherDate);
      return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
  }

  //...
} 

例如:

CompareDate1.java

 package com.mkyong.app;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class CompareDate1 {

    public static void main(String[] args) throws ParseException {

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date1 = sdf.parse("2020-01-30");
        Date date2 = sdf.parse("2020-01-31");

        System.out.println("date1 : " + sdf.format(date1));
        System.out.println("date2 : " + sdf.format(date2));

        int result = date1.compareTo(date2);
        System.out.println("result: " + result);

        if (result == 0) {
            System.out.println("Date1 is equal to Date2");
        } else if (result > 0) {
            System.out.println("Date1 is after Date2");
        } else if (result < 0) {
            System.out.println("Date1 is before Date2");
        } else {
            System.out.println("How to get here?");
        }

    }
} 

输出

Terminal

 date1 : 2020-01-30
date2 : 2020-01-31
result: -1
Date1 is before Date2 

date1改为2020-01-31

输出

Terminal

 date1 : 2020-01-31
date2 : 2020-01-31
result: 0
Date1 is equal to Date2 

date1改为2020-02-01

输出

Terminal

 date1 : 2020-02-01
date2 : 2020-01-31
result: 1
Date1 is after Date2 

1.2 Date.before(),Date.after()和 Date.equals()

下面是一个更加用户友好的方法来比较 Java 中的两个java.util.Date

CompareDate2.java

 package com.mkyong.app;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class CompareDate2 {

  public static void main(String[] args) throws ParseException {

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      //Date date1 = sdf.parse("2009-12-31");
      Date date1 = sdf.parse("2020-02-01");
      Date date2 = sdf.parse("2020-01-31");

      System.out.println("date1 : " + sdf.format(date1));
      System.out.println("date2 : " + sdf.format(date2));

      if (date1.equals(date2)) {
          System.out.println("Date1 is equal Date2");
      }

      if (date1.after(date2)) {
          System.out.println("Date1 is after Date2");
      }

      if (date1.before(date2)) {
          System.out.println("Date1 is before Date2");
      }

  }
} 

输出

Terminal

 date1 : 2020-02-01
date2 : 2020-01-31
Date1 is after Date2 

1.3 检查日期是否在一定范围内

下面的例子使用getTime()来检查一个日期是否在两个日期的特定范围内(包括开始日期和结束日期)。

DateRangeValidator.java

 package com.mkyong.app;

import java.util.Date;

public class DateRangeValidator {

    private final Date startDate;
    private final Date endDate;

    public DateRangeValidator(Date startDate, Date endDate) {
        this.startDate = startDate;
        this.endDate = endDate;
    }

    // inclusive startDate and endDate
    // the equals ensure the inclusive of startDate and endDate,
    // if prefer exclusive, just delete the equals
    public boolean isWithinRange(Date testDate) {

        // it works, alternatives
        /*boolean result = false;
        if ((testDate.equals(startDate) || testDate.equals(endDate)) ||
                (testDate.after(startDate) && testDate.before(endDate))) {
            result = true;
        }
        return result;*/

        // compare date and time, inclusive of startDate and endDate
        // getTime() returns number of milliseconds since January 1, 1970, 00:00:00 GMT
        return testDate.getTime() >= startDate.getTime() &&
                testDate.getTime() <= endDate.getTime();
    }

} 

DateWithinRange.java

 package com.mkyong.app;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateWithinRange {

  public static void main(String[] args) throws ParseException {

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

      Date startDate = sdf.parse("2020-01-01");
      Date endDate = sdf.parse("2020-01-31");

      System.out.println("startDate : " + sdf.format(startDate));
      System.out.println("endDate : " + sdf.format(endDate));

      DateRangeValidator checker = new DateRangeValidator(startDate, endDate);

      Date testDate = sdf.parse("2020-01-01");
      System.out.println("testDate : " + sdf.format(testDate));

      if(checker.isWithinRange(testDate)){
          System.out.println("testDate is within the date range.");
      }else{
          System.out.println("testDate is NOT within the date range.");
      }

  }

} 

输出

Terminal

 startDate : 2020-01-01
endDate   : 2020-01-31

testDate  : 2020-01-01
testDate is within the date range. 

如果我们把testDate改成2020-03-01

输出

Terminal

 startDate : 2020-01-01
endDate   : 2020-01-31

testDate  : 2020-03-01
testDate is NOT within the date range. 

2。比较两个日历

对于遗产java.util.CalendarCalendar的工作方式与java.util.Date相同。而Calendar包含了类似的compareTobefore()after()equals()来比较两个日历。

CompareCalendar.java

 package com.mkyong.app;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class CompareCalendar {

  public static void main(String[] args) throws ParseException {

      SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
      Date date1 = sdf.parse("2020-02-01");
      Date date2 = sdf.parse("2020-01-31");

      Calendar cal1 = Calendar.getInstance();
      Calendar cal2 = Calendar.getInstance();
      cal1.setTime(date1);
      cal2.setTime(date2);

      System.out.println("date1 : " + sdf.format(date1));
      System.out.println("date2 : " + sdf.format(date2));

      if (cal1.after(cal2)) {
          System.out.println("Date1 is after Date2");
      }

      if (cal1.before(cal2)) {
          System.out.println("Date1 is before Date2");
      }

      if (cal1.equals(cal2)) {
          System.out.println("Date1 is equal Date2");
      }

  }

} 

输出

Terminal

 date1 : 2020-02-01
date2 : 2020-01-31
Date1 is after Date2 

3。比较两个日期和时间(Java 8)

对于新的 Java 8 java.time.* 类,都包含类似的compareToisBefore()isAfter()isEqual()来比较两个日期,其工作方式相同。

  • java.time.LocalDate–无时间、无时区的日期。
  • java.time.LocalTime–无日期、无时区的时间。
  • java.time.LocalDateTime–日期和时间,无时区。
  • java.time.ZonedDateTime–日期和时间,带时区。
  • java.time.Instant–自 Unix 纪元时间(UTC 1970 年 1 月 1 日午夜)以来经过的秒数。

3.1 比较两个本地日期

下面的例子展示了如何在 Java 中比较两个LocalDate

CompareLocalDate.java

 package com.mkyong.app;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class CompareLocalDate {

    public static void main(String[] args) throws ParseException {

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd");

        LocalDate date1 = LocalDate.parse("2020-02-01", dtf);
        LocalDate date2 = LocalDate.parse("2020-01-31", dtf);

        System.out.println("date1 : " + date1);
        System.out.println("date2 : " + date2);

        if (date1.isEqual(date2)) {
            System.out.println("Date1 is equal Date2");
        }

        if (date1.isBefore(date2)) {
            System.out.println("Date1 is before Date2");
        }

        if (date1.isAfter(date2)) {
            System.out.println("Date1 is after Date2");
        }

        // test compareTo
        if (date1.compareTo(date2) > 0) {
            System.out.println("Date1 is after Date2");
        } else if (date1.compareTo(date2) < 0) {
            System.out.println("Date1 is before Date2");
        } else if (date1.compareTo(date2) == 0) {
            System.out.println("Date1 is equal to Date2");
        } else {
            System.out.println("How to get here?");
        }

    }

} 

输出

Terminal

 date1 : 2020-02-01
date2 : 2020-01-31
Date1 is after Date2
Date1 is after Date2 

我们将date1改为2020-01-31

输出

Terminal

 date1 : 2020-01-31
date2 : 2020-01-31
Date1 is equal Date2
Date1 is equal to Date2 

3.2 比较两个本地日期时间

下面的例子展示了如何在 Java 中比较两个LocalDateTime,其工作方式相同。

CompareLocalDateTime.java

 package com.mkyong.app;

import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class CompareLocalDateTime {

  public static void main(String[] args) throws ParseException {

      DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");

      LocalDateTime date1 = LocalDateTime.parse("2020-01-31 11:44:43", dtf);
      LocalDateTime date2 = LocalDateTime.parse("2020-01-31 11:44:44", dtf);

      System.out.println("date1 : " + date1);
      System.out.println("date2 : " + date2);

      if (date1.isEqual(date2)) {
          System.out.println("Date1 is equal Date2");
      }

      if (date1.isBefore(date2)) {
          System.out.println("Date1 is before Date2");
      }

      if (date1.isAfter(date2)) {
          System.out.println("Date1 is after Date2");
      }

  }

} 

输出

Terminal

 date1 : 2020-01-31T11:44:43
date2 : 2020-01-31T11:44:44
Date1 is before Date2 

3.3 比较两个瞬间

新的 Java 8 java.time.Instant,返回自 Unix 纪元时间(UTC 1970 年 1 月 1 日午夜)以来经过的秒数。

CompareInstant.java

 package com.mkyong.app;

import java.text.ParseException;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

public class CompareInstant {

    public static void main(String[] args) throws ParseException {

        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");

        LocalDateTime date1 = LocalDateTime.parse("2020-01-31 11:44:44", dtf);
        LocalDateTime date2 = LocalDateTime.parse("2020-01-31 11:44:44", dtf);

        // convert LocalDateTime to Instant
        Instant instant1 = date1.toInstant(ZoneOffset.UTC);
        Instant instant2 = date2.toInstant(ZoneOffset.UTC);

        // compare getEpochSecond
        if (instant1.getEpochSecond() == instant2.getEpochSecond()) {
            System.out.println("instant1 is equal instant2");
        }

        if (instant1.getEpochSecond() < instant2.getEpochSecond()) {
            System.out.println("instant1 is before instant2");
        }

        if (instant1.getEpochSecond() > instant2.getEpochSecond()) {
            System.out.println("instant1 is after instant2");
        }

        // compare with APIs
        if (instant1.equals(instant2)) {
            System.out.println("instant1 is equal instant2");
        }

        if (instant1.isBefore(instant2)) {
            System.out.println("instant1 is before instant2");
        }

        if (instant1.isAfter(instant2)) {
            System.out.println("instant1 is after instant2");
        }

    }

} 

输出

Terminal

 instant1 : 2020-01-31T11:44:44Z
instant2 : 2020-01-31T11:44:44Z
instant1 is equal instant2
instant1 is equal instant2 

4 检查日期是否在一定范围内(Java 8)

我们可以用简单的isBeforeisAfterisEqual来检查某个日期是否在某个日期范围内;例如,下面的程序检查LocalDate是否在 2020 年 1 月。

DateRangeValidator.java

 package com.mkyong.app;

import java.time.LocalDate;

public class DateRangeValidator {

  private final LocalDate startDate;
  private final LocalDate endDate;

  public DateRangeValidator(LocalDate startDate, LocalDate endDate) {
      this.startDate = startDate;
      this.endDate = endDate;
  }

  public boolean isWithinRange(LocalDate testDate) {

      // exclusive startDate and endDate
      //return testDate.isBefore(endDate) && testDate.isAfter(startDate);

      // inclusive startDate and endDate
      return (testDate.isEqual(startDate) || testDate.isEqual(endDate))
              || (testDate.isBefore(endDate) && testDate.isAfter(startDate));

  }

} 

LocalDateWithinRange.java

 package com.mkyong.app;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class LocalDateWithinRange {

  public static void main(String[] args) throws ParseException {

      DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd");

      LocalDate startDate = LocalDate.parse("2020-01-01", dtf);
      LocalDate endDate = LocalDate.parse("2020-01-31", dtf);

      System.out.println("startDate : " + startDate);
      System.out.println("endDate : " + endDate);

      DateRangeValidator checker = new DateRangeValidator(startDate, endDate);

      LocalDate testDate = LocalDate.parse("2020-01-01", dtf);
      System.out.println("\ntestDate : " + testDate);

      if (checker.isWithinRange(testDate)) {
          System.out.println("testDate is within the date range.");
      } else {
          System.out.println("testDate is NOT within the date range.");
      }

  }

} 

输出

Terminal

 startDate : 2020-01-01
endDate : 2020-01-31

testDate : 2020-01-01
testDate is within the date range. 

我们可以对其他 Java 8 时间类使用相同的日期范围算法,如LocalDateTimeZonedDateTimeInstant

 public boolean isWithinRange(LocalDateTime testDate) {

      // exclusive startDate and endDate
      //return testDate.isBefore(endDate) && testDate.isAfter(startDate);

      // inclusive startDate and endDate
      return (testDate.isEqual(startDate) || testDate.isEqual(endDate))
              || (testDate.isBefore(endDate) && testDate.isAfter(startDate));

  } 

5 检查日期是否超过 6 个月

下面显示了一个检查日期是否超过 6 个月的示例。

PlusMonthExample.java

 package com.mkyong.app;

import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

public class PlusMonthExample {

  public static void main(String[] args) throws ParseException {

      DateTimeFormatter dtf = DateTimeFormatter.ofPattern("uuuu-MM-dd");

      LocalDate now = LocalDate.parse("2020-01-01", dtf);
      LocalDate date1 = LocalDate.parse("2020-07-01", dtf);

      System.out.println("now: " + now);
      System.out.println("date1: " + date1);

      // add 6 months
      LocalDate nowPlus6Months = now.plusMonths(6);
      System.out.println("nowPlus6Months: " + nowPlus6Months);

      System.out.println("If date1 older than 6 months?");

      // if want to exclude the 2020-07-01, remove the isEqual
      if (date1.isAfter(nowPlus6Months) || date1.isEqual(nowPlus6Months)) {
          System.out.println("Yes");
      } else {
          System.out.println("No");
      }

  }

} 

输出

Terminal

 now: 2020-01-01
date1: 2020-07-01
nowPlus6Months: 2020-07-01
If date1 older than 6 months?
Yes 

注意
更多例子来比较或检查一个日期是否超过 30 天或 6 个月

参考文献

Java——以 Gzip 格式压缩文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-compress-a-file-in-gzip-format/

本文展示了如何使用 Java 压缩 Gzip 格式的文件。

1。什么是 Gzip?
GZip 是 Unix 或 Linux 系统上的标准文件压缩工具,一般都有后缀.gz

2。为什么我们需要用 Gzip 压缩文件?
答:文件更小,节省磁盘空间。

3。tar.gz 怎么样?
Gzip压缩单个文件,而Tar则是将文件收集成一个归档文件,访问本文即可在 Java 中创建 tar.gz

1.压缩 Gzip–GZIPOutputStream 中的文件

我们将FileInputStream复制到 GZIPOutputStream 中,以 Gzip 格式压缩文件。

1.1 以下示例将文件sitemap.xml压缩成 Gzip 文件sitemap.xml.gz

GZipExample.java

 package com.mkyong.io.howto.compress;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.GZIPOutputStream;

public class GZipExample {

    public static void main(String[] args) {

        // compress a file
        Path source = Paths.get("/home/mkyong/test/sitemap.xml");
        Path target = Paths.get("/home/mkyong/test/sitemap.xml.gz");

        if (Files.notExists(source)) {
            System.err.printf("The path %s doesn't exist!", source);
            return;
        }

        try {

            GZipExample.compressGzip(source, target);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    // copy file (FileInputStream) to GZIPOutputStream
    public static void compressGzip(Path source, Path target) throws IOException {

        try (GZIPOutputStream gos = new GZIPOutputStream(
                                      new FileOutputStream(target.toFile()));
             FileInputStream fis = new FileInputStream(source.toFile())) {

            // copy file
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) > 0) {
                gos.write(buffer, 0, len);
            }

        }

    }

} 

输出,XML 文件以 Gzip 格式压缩,文件大小从 388k 降到 40k。

Terminal

 $ ls -lsah sitemap.*
388K -rw-rw-r-- 1 mkyong mkyong 388K Ogos 12 14:02 sitemap.xml
 40K -rw-rw-r-- 1 mkyong mkyong  40K Ogos 12 14:06 sitemap.xml.gz

$ gzip -l sitemap.xml.gz
       compressed        uncompressed  ratio uncompressed_name
            40154              396719  89.9% sitemap.xml 

1.2 本例与例 1.1 相似。相反,我们使用 NIO Files.copy将一个Path直接复制到GZIPOutputStream

 public static void compressGzipNio(Path source, Path target) throws IOException {

      try (GZIPOutputStream gos = new GZIPOutputStream(
                                    new FileOutputStream(target.toFile()))) {

          Files.copy(source, gos);

      }

  } 

2.将字符串压缩到 Gzip

2.1 这个例子将一个字符串压缩成一个 Gzip 文件。

 public static void compressStringToGzip(String data, Path target) throws IOException {

      try (GZIPOutputStream gos = new GZIPOutputStream(
                                    new FileOutputStream(target.toFile()))) {

          gos.write(data.getBytes(StandardCharsets.UTF_8));

      }

  } 

延伸阅读
Java——如何解压一个 Gzip 文件

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何在 Java 中创建 Zip 文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-compress-files-in-zip-format/

本文展示了压缩单个文件和整个目录(包括子文件和子目录)的几个例子。

  1. 压缩文件-java.util.zip
  2. 压缩文件-Files.copyZip FileSystems
  3. 按需压缩文件(不写入磁盘)
  4. 压缩文件夹-文件树和java.util.zip
  5. 压缩文件夹-文件树和Files.copyZip FileSystems
  6. zipj4图书馆

Java 7 引入了 Zip 文件系统提供者,结合Files.copy,我们可以很容易地将文件属性复制到 Zip 文件中(见例 4)。

1.压缩单个文件–Java . util . zip

1.1 这个 Java 示例使用java.util.zip.ZipOutputStream压缩单个文件。

ZipFileExample1.java

 package com.mkyong.io.howto;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipFileExample1 {

    public static void main(String[] args) {

        Path source = Paths.get("/home/mkyong/test/Test.java");
        String zipFileName = "example.zip";

        try {

            ZipExample.zipSingleFile(source, zipFileName);

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    // Zip a single file
    public static void zipSingleFile(Path source, String zipFileName)
        throws IOException {

        if (!Files.isRegularFile(source)) {
            System.err.println("Please provide a file.");
            return;
        }

        try (
            ZipOutputStream zos = new ZipOutputStream(
                new FileOutputStream(zipFileName));
            FileInputStream fis = new FileInputStream(source.toFile());
        ) {

            ZipEntry zipEntry = new ZipEntry(source.getFileName().toString());
            zos.putNextEntry(zipEntry);

            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) > 0) {
                zos.write(buffer, 0, len);
            }
            zos.closeEntry();
        }

    }
} 

输出

Terminal

 $ unzip -l example.zip
Archive:  example.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
       32  2020-08-06 16:19   Test.java
---------                     -------
       32                     1 file

$ unzip example.zip
Archive:  example.zip
  inflating: Test.java 

2.压缩单个文件–文件系统

2.1 这个例子使用 Java 7 NIO FileSystems.newFileSystem创建一个 zip 文件,并使用Files.copy将文件复制到 zip 路径中。

ZipFileExample2.java

 package com.mkyong.io.howto;

import java.io.*;
import java.net.URI;
import java.nio.file.*;
import java.util.HashMap;
import java.util.Map;

public class ZipFileExample2 {

    public static void main(String[] args) {

        Path source = Paths.get("/home/mkyong/test/Test.java");
        String zipFileName = "example.zip";

        try {

            ZipExample.zipSingleFileNio(source, zipFileName);

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    // Zip a single file
    public static void zipSingleFileNio(Path source, String zipFileName)
        throws IOException {

        if (!Files.isRegularFile(source)) {
            System.err.println("Please provide a file.");
            return;
        }

        Map<String, String> env = new HashMap<>();
        // Create the zip file if it doesn't exist
        env.put("create", "true");

        URI uri = URI.create("jar:file:/home/mkyong/" + zipFileName);

        try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {
            Path pathInZipfile = zipfs.getPath(source.getFileName().toString());

            // Copy a file into the zip file path
            Files.copy(source, pathInZipfile, StandardCopyOption.REPLACE_EXISTING);
        }

    }

} 

3.按需压缩单个文件

这个例子使用ByteArrayInputStream直接按需创建一些字节并保存到 zip 文件中,而不保存或写入数据到本地文件系统中。

 // create a file on demand (without save locally) and add to zip
    public static void zipFileWithoutSaveLocal(String zipFileName) throws IOException {

        String data = "Test data \n123\n456";
        String fileNameInZip = "abc.txt";

        try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFileName))) {

            ZipEntry zipEntry = new ZipEntry(fileNameInZip);
            zos.putNextEntry(zipEntry);

            ByteArrayInputStream bais = new ByteArrayInputStream(data.getBytes());
            // one line, able to handle large size?
            //zos.write(bais.readAllBytes());

            // play safe
            byte[] buffer = new byte[1024];
            int len;
            while ((len = bais.read(buffer)) > 0) {
                zos.write(buffer, 0, len);
            }

            zos.closeEntry();
        }

    } 

输出

Terminal

 $ unzip -l example.zip

Archive:  example.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
       18  2020-08-11 18:44   abc.txt
---------                     -------
       18                     1 file 

4.压缩文件夹或目录–Java . util . zip

4.1 查看包含一些子文件和子目录的目录。

Terminal

 $ tree /home/mkyong/test
test
├── data
│   └── db.debug.conf
├── README.md
├── test-a1.log
├── test-a2.log
├── test-b
│   ├── test-b1.txt
│   ├── test-b2.txt
│   ├── test-c
│   │   ├── test-c1.log
│   │   └── test-c2.log
│   └── test-d
│       ├── test-d1.log
│       └── test-d2.log
└── Test.java 

4.2 这个 Java 示例使用FileVisitor遍历文件树,使用ZipOutputStream手动压缩所有内容,包括子文件和子目录,但忽略符号链接和文件属性。

ZipDirectoryExample1.java

 package com.mkyong.io.howto;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ZipDirectoryExample {

    public static void main(String[] args) {

        Path source = Paths.get("/home/mkyong/test/");

        if (!Files.isDirectory(source)) {
            System.out.println("Please provide a folder.");
            return;
        }

        try {

            ZipDirectoryExample.zipFolder(source);

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    // zip a directory, including sub files and sub directories
    public static void zipFolder(Path source) throws IOException {

        // get folder name as zip file name
        String zipFileName = source.getFileName().toString() + ".zip";

        try (
                ZipOutputStream zos = new ZipOutputStream(
                        new FileOutputStream(zipFileName))
        ) {

            Files.walkFileTree(source, new SimpleFileVisitor<>() {
                @Override
                public FileVisitResult visitFile(Path file,
                    BasicFileAttributes attributes) {

                    // only copy files, no symbolic links
                    if (attributes.isSymbolicLink()) {
                        return FileVisitResult.CONTINUE;
                    }

                    try (FileInputStream fis = new FileInputStream(file.toFile())) {

                        Path targetFile = source.relativize(file);
                        zos.putNextEntry(new ZipEntry(targetFile.toString()));

                        byte[] buffer = new byte[1024];
                        int len;
                        while ((len = fis.read(buffer)) > 0) {
                            zos.write(buffer, 0, len);
                        }

                        // if large file, throws out of memory
                        //byte[] bytes = Files.readAllBytes(file);
                        //zos.write(bytes, 0, bytes.length);

                        zos.closeEntry();

                        System.out.printf("Zip file : %s%n", file);

                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) {
                    System.err.printf("Unable to zip : %s%n%s%n", file, exc);
                    return FileVisitResult.CONTINUE;
                }
            });

        }

    }

} 

输出

Terminal

 Zip file : /home/mkyong/test/test-a2.log
Zip file : /home/mkyong/test/test-a1.log
Zip file : /home/mkyong/test/data/db.debug.conf
Zip file : /home/mkyong/test/README.md
Zip file : /home/mkyong/test/Test.java
Zip file : /home/mkyong/test/test-b/test-b1.txt
Zip file : /home/mkyong/test/test-b/test-c/test-c2.log
Zip file : /home/mkyong/test/test-b/test-c/test-c1.log
Zip file : /home/mkyong/test/test-b/test-b2.txt
Zip file : /home/mkyong/test/test-b/test-d/test-d2.log
Zip file : /home/mkyong/test/test-b/test-d/test-d1.log

Done 

上面的例子在当前的工作目录下创建了 zip 文件,我们没有复制文件的属性(查看文件创建的日期和时间)。

Terminal

 $ unzip -l test.zip

Archive:  test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2020-08-06 18:49   test-a2.log
        0  2020-08-06 18:49   test-a1.log
       14  2020-08-06 18:49   data/db.debug.conf
       42  2020-08-06 18:49   README.md
       32  2020-08-06 18:49   Test.java
        0  2020-08-06 18:49   test-b/test-b1.txt
        0  2020-08-06 18:49   test-b/test-c/test-c2.log
        0  2020-08-06 18:49   test-b/test-c/test-c1.log
        0  2020-08-06 18:49   test-b/test-b2.txt
        0  2020-08-06 18:49   test-b/test-d/test-d2.log
        0  2020-08-06 18:49   test-b/test-d/test-d1.log
---------                     -------
       88                     11 files 

5.压缩文件夹或目录——文件系统

5.1 本例使用相同的FileVisitor遍历文件树。不过,这次我们使用FileSystems URI 创建 zip 文件,使用Files.copy将文件复制到 zip 路径,包括文件属性,但是忽略符号链接。

ZipDirectoryExample2.java

 package com.mkyong.io.howto;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;

public class ZipDirectoryExample {

    public static void main(String[] args) {

        Path source = Paths.get("/home/mkyong/test/");

        if (!Files.isDirectory(source)) {
            System.out.println("Please provide a folder.");
            return;
        }

        try {

            ZipDirectoryExample.zipFolderNio(source);

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    public static void zipFolderNio(Path source) throws IOException {

        // get current working directory
        String currentPath = System.getProperty("user.dir") + File.separator;

        // get folder name as zip file name
        // can be other extension, .foo .bar .whatever
        String zipFileName = source.getFileName().toString() + ".zip";
        URI uri = URI.create("jar:file:" + currentPath + zipFileName);

        Files.walkFileTree(source, new SimpleFileVisitor<>() {
            @Override
            public FileVisitResult visitFile(Path file,
                BasicFileAttributes attributes) {

                // Copying of symbolic links not supported
                if (attributes.isSymbolicLink()) {
                    return FileVisitResult.CONTINUE;
                }

                Map<String, String> env = new HashMap<>();
                env.put("create", "true");

                try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {

                    Path targetFile = source.relativize(file);
                    Path pathInZipfile = zipfs.getPath(targetFile.toString());

                    // NoSuchFileException, need create parent directories in zip path
                    if (pathInZipfile.getParent() != null) {
                        Files.createDirectories(pathInZipfile.getParent());
                    }

                    // copy file attributes
                    CopyOption[] options = {
                            StandardCopyOption.REPLACE_EXISTING,
                            StandardCopyOption.COPY_ATTRIBUTES,
                            LinkOption.NOFOLLOW_LINKS
                    };
                    // Copy a file into the zip file path
                    Files.copy(file, pathInZipfile, options);

                } catch (IOException e) {
                    e.printStackTrace();
                }

                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) {
                System.err.printf("Unable to zip : %s%n%s%n", file, exc);
                return FileVisitResult.CONTINUE;
            }

        });

    }

} 

输出

Terminal

 $ unzip -l test.zip
Archive:  test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2020-07-27 15:10   test-a2.log
        0  2020-07-23 14:55   test-a1.log
        0  2020-08-06 18:57   data/
       14  2020-08-04 14:07   data/db.debug.conf
       42  2020-08-05 19:04   README.md
       32  2020-08-05 19:04   Test.java
        0  2020-08-06 18:57   test-b/
        0  2020-07-24 15:49   test-b/test-b1.txt
        0  2020-08-06 18:57   test-b/test-c/
        0  2020-07-27 15:11   test-b/test-c/test-c2.log
        0  2020-07-27 15:11   test-b/test-c/test-c1.log
        0  2020-07-27 15:10   test-b/test-b2.txt
        0  2020-08-06 18:57   test-b/test-d/
        0  2020-07-27 15:11   test-b/test-d/test-d2.log
        0  2020-07-27 15:11   test-b/test-d/test-d1.log
---------                     -------
       88                     15 files

# unzip to an external folder abc
$ unzip test.zip -d abc

Archive:  test.zip
 inflating: abc/test-a2.log         
 inflating: abc/test-a1.log         
  creating: abc/data/
 inflating: abc/data/db.debug.conf  
 inflating: abc/README.md           
 inflating: abc/Test.java           
  creating: abc/test-b/
 inflating: abc/test-b/test-b1.txt  
  creating: abc/test-b/test-c/
 inflating: abc/test-b/test-c/test-c2.log  
 inflating: abc/test-b/test-c/test-c1.log  
 inflating: abc/test-b/test-b2.txt  
  creating: abc/test-b/test-d/
 inflating: abc/test-b/test-d/test-d2.log  
 inflating: abc/test-b/test-d/test-d1.log  

$ tree abc
abc
├── data
│   └── db.debug.conf
├── README.md
├── test-a1.log
├── test-a2.log
├── test-b
│   ├── test-b1.txt
│   ├── test-b2.txt
│   ├── test-c
│   │   ├── test-c1.log
│   │   └── test-c2.log
│   └── test-d
│       ├── test-d1.log
│       └── test-d2.log
└── Test.java 

6.Zip 文件–zip4j

zip4j是 Java 中流行的 zip 库;它有许多先进的功能,如密码保护的压缩文件,分裂压缩文件,AES 加密等。请访问 zip4j github 获取更多文档和用法。

以下是压缩文件和文件夹的一些常见用法。

 import net.lingala.zip4j.ZipFile;

//...
  public static void zip4j() throws IOException {

      // zip file with a single file
      new ZipFile("filename.zip").addFile("file.txt");

      // zip file with multiple files
      List<File> files = Arrays.asList(
              new File("file1.txt"), new File("file2.txt"));
      new ZipFile("filename.zip").addFiles(files);

      // zip file with a folder
      new ZipFile("filename.zip").addFolder(new File("/home/mkyong/folder"));

  } 

延伸阅读
如何用 Java 解压 zip 文件

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何在 Hibernate 中配置 DBCP 连接池

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/how-to-configure-dbcp-connection-pool-in-hibernate/

Note
Due to bugs in the old DBCP code, Hibernate is no longer maintain DBCP-based connection provider, read this Hibernate thread.

现在,阿帕奇 DBCP 回到了积极的开发中,许多 bug 被修复,现在更加稳定了。即使 Hibernate 没有像 C3P0Proxool 这样的连接提供者,但是你仍然可以很容易地配置它。

在本教程中,我们将向您展示如何集成 Apache DBCP 连接池和 Hibernate 框架。

1.去拿 DBCP 罐

要集成 DBCP 和 Hibernate,你需要 commons-dbcp.jarcommons-pool-1.5.4.jar

文件:pom.xml

 <project ...>

	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>

	<dependencies>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>

	</dependencies>
</project> 

2.DBCPConnectionProvider

要将 DBCP 与 Hibernate 集成,需要创建一个" DBCPConnectionProvider "类,参见这篇文章

文件:DBCPConnectionProvider.java

 package com.mkyong.util;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.dbcp.BasicDataSource;
import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.hibernate.HibernateException;
import org.hibernate.cfg.Environment;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.connection.ConnectionProviderFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DBCPConnectionProvider implements ConnectionProvider {

	private static final Logger log = LoggerFactory
			.getLogger(DBCPConnectionProvider.class);
	private static final String PREFIX = "hibernate.dbcp.";
	private BasicDataSource ds;

	// Old Environment property for backward-compatibility (property removed in
	// Hibernate3)
	private static final String DBCP_PS_MAXACTIVE = "hibernate.dbcp.ps.maxActive";

	// Property doesn't exists in Hibernate2
	private static final String AUTOCOMMIT = "hibernate.connection.autocommit";

	public void configure(Properties props) throws HibernateException {
		try {
			log.debug("Configure DBCPConnectionProvider");

			// DBCP properties used to create the BasicDataSource
			Properties dbcpProperties = new Properties();

			// DriverClass & url
			String jdbcDriverClass = props.getProperty(Environment.DRIVER);
			String jdbcUrl = props.getProperty(Environment.URL);
			dbcpProperties.put("driverClassName", jdbcDriverClass);
			dbcpProperties.put("url", jdbcUrl);

			// Username / password
			String username = props.getProperty(Environment.USER);
			String password = props.getProperty(Environment.PASS);
			dbcpProperties.put("username", username);
			dbcpProperties.put("password", password);

			// Isolation level
			String isolationLevel = props.getProperty(Environment.ISOLATION);
			if ((isolationLevel != null)
					&& (isolationLevel.trim().length() > 0)) {
				dbcpProperties.put("defaultTransactionIsolation",
						isolationLevel);
			}

			// Turn off autocommit (unless autocommit property is set)
			String autocommit = props.getProperty(AUTOCOMMIT);
			if ((autocommit != null) && (autocommit.trim().length() > 0)) {
				dbcpProperties.put("defaultAutoCommit", autocommit);
			} else {
				dbcpProperties.put("defaultAutoCommit",
						String.valueOf(Boolean.FALSE));
			}

			// Pool size
			String poolSize = props.getProperty(Environment.POOL_SIZE);
			if ((poolSize != null) && (poolSize.trim().length() > 0)
					&& (Integer.parseInt(poolSize) > 0)) {
				dbcpProperties.put("maxActive", poolSize);
			}

			// Copy all "driver" properties into "connectionProperties"
			Properties driverProps = ConnectionProviderFactory
					.getConnectionProperties(props);
			if (driverProps.size() > 0) {
				StringBuffer connectionProperties = new StringBuffer();
				for (Iterator iter = driverProps.entrySet().iterator(); iter
						.hasNext();) {
					Map.Entry entry = (Map.Entry) iter.next();
					String key = (String) entry.getKey();
					String value = (String) entry.getValue();
					connectionProperties.append(key).append('=').append(value);
					if (iter.hasNext()) {
						connectionProperties.append(';');
					}
				}
				dbcpProperties.put("connectionProperties",
						connectionProperties.toString());
			}

			// Copy all DBCP properties removing the prefix
			for (Iterator iter = props.entrySet().iterator(); iter.hasNext();) {
				Map.Entry entry = (Map.Entry) iter.next();
				String key = (String) entry.getKey();
				if (key.startsWith(PREFIX)) {
					String property = key.substring(PREFIX.length());
					String value = (String) entry.getValue();
					dbcpProperties.put(property, value);
				}
			}

			// Backward-compatibility
			if (props.getProperty(DBCP_PS_MAXACTIVE) != null) {
				dbcpProperties.put("poolPreparedStatements",
						String.valueOf(Boolean.TRUE));
				dbcpProperties.put("maxOpenPreparedStatements",
						props.getProperty(DBCP_PS_MAXACTIVE));
			}

			// Some debug info
			if (log.isDebugEnabled()) {
				StringWriter sw = new StringWriter();
				dbcpProperties.list(new PrintWriter(sw, true));
				log.debug(sw.toString());
			}

			// Let the factory create the pool
			ds = (BasicDataSource) BasicDataSourceFactory
					.createDataSource(dbcpProperties);

			// The BasicDataSource has lazy initialization
			// borrowing a connection will start the DataSource
			// and make sure it is configured correctly.
			Connection conn = ds.getConnection();
			conn.close();

			// Log pool statistics before continuing.
			logStatistics();
		} catch (Exception e) {
			String message = "Could not create a DBCP pool";
			log.error(message, e);
			if (ds != null) {
				try {
					ds.close();
				} catch (Exception e2) {
					// ignore
				}
				ds = null;
			}
			throw new HibernateException(message, e);
		}
		log.debug("Configure DBCPConnectionProvider complete");
	}

	public Connection getConnection() throws SQLException {
		Connection conn = null;
		try {
			conn = ds.getConnection();
		} finally {
			logStatistics();
		}
		return conn;
	}

	public void closeConnection(Connection conn) throws SQLException {
		try {
			conn.close();
		} finally {
			logStatistics();
		}
	}

	public void close() throws HibernateException {
		log.debug("Close DBCPConnectionProvider");
		logStatistics();
		try {
			if (ds != null) {
				ds.close();
				ds = null;
			} else {
				log.warn("Cannot close DBCP pool (not initialized)");
			}
		} catch (Exception e) {
			throw new HibernateException("Could not close DBCP pool", e);
		}
		log.debug("Close DBCPConnectionProvider complete");
	}

	protected void logStatistics() {
		if (log.isInfoEnabled()) {
			log.info("active: " + ds.getNumActive() + " (max: "
					+ ds.getMaxActive() + ")   " + "idle: " + ds.getNumIdle()
					+ "(max: " + ds.getMaxIdle() + ")");
		}
	}

	public boolean supportsAggressiveRelease() {
		return false;
	}
} 

3.在 hibernate.cfg.xml 中配置 DBCP

现在,链接您的“ DBCPConnectionProvider ”并在“ hibernate.cfg.xml ”中定义 DBCP 属性,例如:

文件:hibernate.cfg.xml

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
 <session-factory>
  <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
  <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:MKYONG</property>
  <property name="hibernate.connection.username">mkyong</property>
  <property name="hibernate.connection.password">password</property>
  <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
  <property name="hibernate.default_schema">MKYONG</property>
  <property name="show_sql">true</property>

  <property name="hibernate.connection.provider_class">
	com.mkyong.util.DBCPConnectionProvider
  </property>
  <property name="hibernate.dbcp.initialSize">8</property>
  <property name="hibernate.dbcp.maxActive">20</property>
  <property name="hibernate.dbcp.maxIdle">20</property>
  <property name="hibernate.dbcp.minIdle">0</property>

  <mapping class="com.mkyong.user.DBUser"></mapping>
</session-factory>
</hibernate-configuration> 

Note
The DBCP properties are supported in Hibernate via “hibernate.dbcp.properties-name“.

对于所有 DBCP 属性,请参考此 DBCP 配置页面。

4.运行它,输出

完成后,运行它并查看以下输出:

dbcp connection pool in hibernate

在应用程序启动阶段,连接池中创建了 8 个数据库连接,供您的 web 应用程序使用。

Download it – Hibernate-DBCP-Connection-Pool-Example.zip (10KB)

参考

  1. http://wiki.apache.org/commons/DBCP/Hibernate
  2. https://forum.hibernate.org/viewtopic.php?f=1&t = 947528&查看=下一张
  3. http://commons.apache.org/dbcp/configuration.html
  4. http://wiki.apache.org/commons/DBCP

如何在 Struts 2 中配置全局资源包

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/struts2/how-to-configure-global-resource-bundle-in-struts-2/

通常,您可能需要一个全局资源包(属性文件)来存储应用程序中所有类都可以使用的消息。

Download It – Struts2-global-resource-bundle-Example.zip

在 Struts 2 中,有三种方式来配置全局资源包:

1.struts.properties

在“struts.properties”文件中配置全局资源包,这里定义了一个名为“ global.properties 的属性文件作为全局资源包。

 struts.custom.i18n.resources = global 

对于多个资源包,只需用逗号分隔属性文件。

 struts.custom.i18n.resources = global, another-properties-file 

2.struts.xml

或者,您可以在 struts.xml 配置文件中将全局资源包配置为一个常量值。

 <struts>
   <constant name="struts.custom.i18n.resources" value="global" /> 	
</struts> 

3.听众

最后一种方法是使用 servlet 监听器加载一个属性文件作为全局资源包。

 package com.mkyong.common.listener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.opensymphony.xwork2.util.LocalizedTextUtil;

public class GlobalMessagesListener implements ServletContextListener {

	  private static final String DEFAULT_RESOURCE = "global";

	  public void contextInitialized(ServletContextEvent arg0) {
	    LocalizedTextUtil.addDefaultResourceBundle(DEFAULT_RESOURCE);
	  }

	  public void contextDestroyed(ServletContextEvent arg0) {
	  }
} 

web.xml

 <!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <display-name>Struts 2 Web Application</display-name>

  <filter>
	<filter-name>struts2</filter-name>
	<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  </filter>

  <filter-mapping>
	<filter-name>struts2</filter-name>
	<url-pattern>/*</url-pattern>
  </filter-mapping>

  <listener>
  	<listener-class>
          com.mkyong.common.listener.GlobalMessagesListener
        </listener-class>
  </listener>

</web-app> 

resource bundle struts2

如何在 Hibernate 中配置日志记录–SLF4j+Log4j

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/how-to-configure-log4j-in-hibernate-project/

Try logback
Try logback logging framework, read this article for the “reasons to prefer logback over log4j. To integrate logback with Hibernate, refer this – How to configure logging in Hibernate – Logback

Hibernate 使用简单日志 Facade for Java (SLF4J) 将日志输出重定向到您的首选日志框架 is (log4j,JCL,JDK 日志,lofback…)。在本教程中,我们将向您展示如何使用 SLF4j + Log4j 日志框架在 Hibernate 中进行日志记录。

本文中使用的技术:

  1. Hibernate 3.6.3 .最终版
  2. slf4j-api-1.6.1
  3. slf4j-log4j12-1.6.1
  4. Eclipse 3.6
  5. Maven 3.0.3

1.获取 SLF4j + Log4j

要在 Hibernate 中进行日志记录,您需要“ slf4j-api.jar ”和您的首选绑定,如 log4j“slf4j-log4j 12 . jar”。只需在您的pom.xml中声明依赖关系。

文件:pom.xml

 <project ...>
	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>

	<dependencies>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

		<!-- slf4j-log4j -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.6.1</version>
		</dependency>

	</dependencies>
</project> 

Where is slf4j-api.jar?
The slf4j-api.jar is defined as the dependency of “hibernate-core“, so , you do not need to declare it again.

2.Log4j 属性文件

创建一个" log4j.properties "文件,并将其放入项目的类路径中,见下图:

configure log4j in hibernate

文件:log4.properties

 # Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=C:\\mkyongapp.log
log4j.appender.file.MaxFileSize=1MB
log4j.appender.file.MaxBackupIndex=1
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

# Root logger option
log4j.rootLogger=INFO, file, stdout

# Log everything. Good for troubleshooting
log4j.logger.org.hibernate=INFO

# Log all JDBC parameters
log4j.logger.org.hibernate.type=ALL 

使用这个 log4j 配置,它会将所有日志记录输出重定向到控制台和一个位于“C:\\mkyongapp.log”的文件。

Note
Hibernate provides many settings to let developer to decide what to log. Always refer to this Hibernate Log Categories, choose some and implement it in your log file.

3.输出

试着运行你的 Hibernate web 应用程序,所有的日志输出将被记录在"C:\\mkyongapp.log"文件中。见下图:

log4j outputDownload it – Log4j-Hibernate-Example.zip (7KB)

参考

  1. 【Java 的简单日志门面(SLF4J)
  2. http://logging.apache.org/log4j/1.2/

如何在 Hibernate–log back 中配置日志记录

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/how-to-configure-logging-in-hibernate-logback/

在本教程中,我们将展示如何将日志记录框架与 Hibernate 集成。

本教程中使用的工具和技术:

  1. Hibernate 3.6.3 .最终版
  2. slf4j-api-1.6.1
  3. 回溯-核心-0.9.28
  4. 回溯-经典-0.9.28
  5. Eclipse 3.6
  6. Maven 3.0.3

1.获取 SLF4j + Logback

要在 Hibernate web 应用程序中使用 logback,您需要 3 个库:

  1. slf4j-api.jar
  2. 测井岩心
  3. 回溯-经典

文件:pom.xml

 <project ...>
	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>

	<dependencies>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

		<!-- logback -->
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-core</artifactId>
			<version>0.9.28</version>
		</dependency>

		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>0.9.28</version>
		</dependency>

	</dependencies>
</project> 

Where is slf4j-api.jar?
The slf4j-api.jar is defined as the dependency of “hibernate-core“, so , you do not need to declare it again.

2.logback.xml

创建一个" logback.xml "文件,并将其放入项目的类路径中,见下图:

logback hibernate

一个经典的“logback.xml”例子。

 <?xml version="1.0" encoding="UTF-8"?>
<configuration>

 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
	<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
		<Pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
                </Pattern>
	</encoder>
 </appender>

 <appender name="FILE"
	class="ch.qos.logback.core.rolling.RollingFileAppender">
	<file>c:/mkyongapp.log</file>
	<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
	   <Pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
           </Pattern>
	</encoder>

	<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
		<FileNamePattern>c:/mkyongapp.%i.log.zip</FileNamePattern>
		<MinIndex>1</MinIndex>
		<MaxIndex>10</MaxIndex>
	</rollingPolicy>

	<triggeringPolicy
		class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
		<MaxFileSize>2MB</MaxFileSize>
	</triggeringPolicy>

  </appender>

  <logger name="org.hibernate.type" level="ALL" />
  <logger name="org.hibernate" level="DEBUG" />

  <root level="INFO">
	<appender-ref ref="FILE" />
	<appender-ref ref="STDOUT" />
  </root>

</configuration> 

使用这个logback.xml配置,这意味着将所有 web 应用程序日志记录输出重定向到控制台和一个位于c:/mkyongapp.log的文件。

3.输出

参见下面“C:\\mkyongapp.log”处的日志记录输出:

 //...
2011-04-23_14:34:08.055 [main] DEBUG o.h.transaction.JDBCTransaction - commit
2011-04-23_14:34:08.056 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - processing flush-time cascades
2011-04-23_14:34:08.056 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - dirty checking collections
2011-04-23_14:34:08.058 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 1 insertions, 0 updates, 0 deletions to 1 objects
2011-04-23_14:34:08.058 [main] DEBUG o.h.e.d.AbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
2011-04-23_14:34:08.059 [main] DEBUG org.hibernate.pretty.Printer - listing entities:
2011-04-23_14:34:08.060 [main] DEBUG org.hibernate.pretty.Printer - com.mkyong.user.DBUser{username=Hibernate101, createdBy=system, userId=100, createdDate=Sat Apr 23 14:34:08 SGT 2011}
2011-04-23_14:34:08.064 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0)
2011-04-23_14:34:08.066 [main] DEBUG org.hibernate.SQL - insert into MKYONG.DBUSER (CREATED_BY, CREATED_DATE, USERNAME, USER_ID) values (?, ?, ?, ?)
2011-04-23_14:34:08.150 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - system
2011-04-23_14:34:08.152 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [2] as [DATE] - Sat Apr 23 14:34:08 SGT 2011
2011-04-23_14:34:08.153 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [3] as [VARCHAR] - Hibernate101
2011-04-23_14:34:08.153 [main] TRACE o.h.type.descriptor.sql.BasicBinder - binding parameter [4] as [INTEGER] - 100 

Download it – Logback-Hibernate-Example.zip (8KB)

参考

  1. 【Java 的简单日志门面(SLF4J)
  2. 回退日志框架

如何在 Hibernate 中配置 C3P0 连接池

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/how-to-configure-the-c3p0-connection-pool-in-hibernate/

Connection Pool
Connection pool is good for performance, as it prevents Java application create a connection each time when interact with database and minimizes the cost of opening and closing connections.

参见 wiki 连接池的解释

Hibernate 自带内部连接池,但不适合生产使用。在本教程中,我们将向您展示如何将第三方连接池 C3P0 与 Hibernate 集成。

1.获取 hibernate-c3p0.jar

要集成 c3p0 和 Hibernate,需要 hibernate-c3p0.jar ,从 JBoss repository 获取。

文件:pom.xml

 <project ...>

	<repositories>
		<repository>
			<id>JBoss repository</id>
			<url>http://repository.jboss.org/nexus/content/groups/public/</url>
		</repository>
	</repositories>

	<dependencies>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

		<!-- Hibernate c3p0 connection pool -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-c3p0</artifactId>
			<version>3.6.3.Final</version>
		</dependency>

	</dependencies>
</project> 

2.配置 c3p0 属性

为了配置 c3p0,将 c3p0 配置详细信息放入" hibernate.cfg.xml "中,如下所示:

文件:hibernate.cfg.xml

 <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
 <session-factory>
  <property name="hibernate.connection.driver_class">oracle.jdbc.driver.OracleDriver</property>
  <property name="hibernate.connection.url">jdbc:oracle:thin:@localhost:1521:MKYONG</property>
  <property name="hibernate.connection.username">mkyong</property>
  <property name="hibernate.connection.password">password</property>
  <property name="hibernate.dialect">org.hibernate.dialect.Oracle10gDialect</property>
  <property name="hibernate.default_schema">MKYONG</property>
  <property name="show_sql">true</property>

  <property name="hibernate.c3p0.min_size">5</property>
  <property name="hibernate.c3p0.max_size">20</property>
  <property name="hibernate.c3p0.timeout">300</property>
  <property name="hibernate.c3p0.max_statements">50</property>
  <property name="hibernate.c3p0.idle_test_period">3000</property>

  <mapping class="com.mkyong.user.DBUser"></mapping>
</session-factory>
</hibernate-configuration> 
  1. hibernate . c3p 0 . min _ size–池中 JDBC 连接的最小数量。休眠默认值:1
  2. hibernate . c3p 0 . max _ size–池中 JDBC 连接的最大数量。休眠默认值:100
  3. hibernate . c3p 0 . time out–从池中删除空闲连接的时间(秒)。休眠默认值:0,永不过期。
  4. hibernate . c3p 0 . max _ statements–将缓存准备好的语句数。提高性能。休眠默认值:0,禁用缓存。
  5. hibernate . c3p 0 . idle _ test _ period–自动验证连接之前的空闲时间(秒)。休眠默认值:0

Note
For detail about hibernate-c3p0 configuration settings, please read this article.

运行它,输出

完成后,运行它并查看以下输出:

c3p0 connection pool in hibernate

在连接初始化过程中,连接池中创建了 5 个数据库连接,以备 web 应用程序重用。

Download it – Hibernate-C3P0-Connection-Pool-Example.zip (8KB)

参考

  1. http://docs . JBoss . org/hibernate/core/3.6/reference/en-US/html _ single/# d0e 1748
  2. http://www.mchange.com/projects/c3p0/index.html#appendix_d

Tags : c3p0 connection pool hibernate

用 JDBC 驱动程序连接到 MySQL

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/jdbc/how-to-connect-to-mysql-with-jdbc-driver-java/

一个 JDBC 的例子,展示了如何用 JDBC 驱动程序连接到 MySQL 数据库。

测试者:

  • Java 8
  • MySQL 5.7
  • MySQL JDBC 驱动mysql-connector-java:8.0.16

1.下载 MySQL JDBC 驱动程序

访问 https://dev.mysql.com/downloads/connector/j/下载最新的 MySQL JDBC 驱动程序。

mysql-connector-j

2.JDBC 连接

2.1 建立到 MySQL 数据库的连接。

JDBCExample.java

 import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class JDBCExample {

    public static void main(String[] args) {

        // https://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html#package.description
        // auto java.sql.Driver discovery -- no longer need to load a java.sql.Driver class via Class.forName

        // register JDBC driver, optional since java 1.6
        /*try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }*/

        // auto close connection
        try (Connection conn = DriverManager.getConnection(
                "jdbc:mysql://127.0.0.1:3306/test", "root", "password")) {

            if (conn != null) {
                System.out.println("Connected to the database!");
            } else {
                System.out.println("Failed to make connection!");
            }

        } catch (SQLException e) {
            System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
} 

测试:

 # compile
> javac JDBCExample.java

# run
> java JDBCExample

SQL State: 08001
No suitable driver found for jdbc:mysql://127.0.0.1:3306/test 

要用java命令运行它,我们需要手动加载 MySQL JDBC 驱动程序。假设所有东西都存储在c:\test文件夹中,用这个-cp选项再次运行它。

project layout

 > java -cp "c:\test\mysql-connector-java-8.0.16.jar;c:\test" JDBCExample
Connected to the database! 

3.Maven 项目

3.1 MySQL JDBC 驱动程序可以在 Maven 中央存储库中获得。

pom.xml

 <dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<version>8.0.16</version>
    </dependency> 

3.2 一个简单的 JDBC 选择示例。

JDBCExample2.java

 import com.mkyong.jdbc.model.Employee;

import java.math.BigDecimal;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class JDBCExample2 {

    public static void main(String[] args) {

        System.out.println("MySQL JDBC Connection Testing ~");

        List<Employee> result = new ArrayList<>();

        String SQL_SELECT = "Select * from EMPLOYEE";

        try (Connection conn = DriverManager.getConnection(
                "jdbc:mysql://127.0.0.1:3306/test", "root", "password");
             PreparedStatement preparedStatement = conn.prepareStatement(SQL_SELECT)) {

            ResultSet resultSet = preparedStatement.executeQuery();

            while (resultSet.next()) {

                long id = resultSet.getLong("ID");
                String name = resultSet.getString("NAME");
                BigDecimal salary = resultSet.getBigDecimal("SALARY");
                Timestamp createdDate = resultSet.getTimestamp("CREATED_DATE");

                Employee obj = new Employee();
                obj.setId(id);
                obj.setName(name);
                obj.setSalary(salary);
                // Timestamp -> LocalDateTime
                obj.setCreatedDate(createdDate.toLocalDateTime());

                result.add(obj);

            }
            result.forEach(x -> System.out.println(x));

        } catch (SQLException e) {
            System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

} 

Employee.java

 import java.math.BigDecimal;
import java.time.LocalDateTime;

public class Employee {

    private Long id;
    private String name;
    private BigDecimal salary;
    private LocalDateTime createdDate;

    //...
} 

表定义。

 CREATE TABLE EMPLOYEE
(
    ID INT NOT NULL AUTO_INCREMENT,
    NAME VARCHAR(100) NOT NULL,
    SALARY DECIMAL(15, 2) NOT NULL,
    CREATED_DATE DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (ID)
); 

下载源代码

$ git clone https://github.com/mkyong/java-jdbc.git

参考

Tags : jdbc mysql

Java–如何将 byte[]保存到文件中

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-convert-array-of-bytes-into-file/

本文展示了几种将byte[]保存到文件中的方法。

对于 JDK 1.7 和更高版本,NIO Files.write 是将byte[]保存到文件的最简单的解决方案。

 // bytes = byte[]
  Path path = Paths.get("/path/file");
  Files.write(path, bytes); 

FileOutputStream是最好的选择。

 try (FileOutputStream fos = new FileOutputStream("/path/file")) {
      fos.write(bytes);
      //fos.close // no need, try-with-resources auto close
  } 

如果我们有 Apache Commons IO ,试试FileUtils

 FileUtils.writeByteArrayToFile(new File("/path/file"), bytes); 

pom.xml

 <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.7</version>
  </dependency> 

1.将字节[]保存到文件。

下面的例子读取一个文件并将数据转换成一个byte[]。稍后,我们将byte[]保存到另一个文件中。

ByteToFile.java

 package com.mkyong.io.howto;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class ByteToFile {

    public static void main(String[] args) {

        try {

            // tested with character data and binary data

            // file to bytes[]
            byte[] bytes = Files.readAllBytes(Paths.get("/home/mkyong/test/file.txt"));

            // save byte[] to a file
            writeBytesToFile("/home/mkyong/test/file2.txt", bytes);
            writeBytesToFileNio("/home/mkyong/test/file3.txt", bytes);
            writeBytesToFileApache("/home/mkyong/test/file4.txt", bytes);

            System.out.println("Done");

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //JDK 7 - FileOutputStream + try-with-resources
    private static void writeBytesToFile(String fileOutput, byte[] bytes)
        throws IOException {

        try (FileOutputStream fos = new FileOutputStream(fileOutput)) {
            fos.write(bytes);
        }

    }

    //JDK 7, NIO, Files.write
    private static void writeBytesToFileNio(String fileOutput, byte[] bytes)
        throws IOException {

        Path path = Paths.get(fileOutput);
        Files.write(path, bytes);

    }

    // Apache Commons IO
    private static void writeBytesToFileApache(String fileOutput, byte[] bytes)
        throws IOException {

        FileUtils.writeByteArrayToFile(new File(fileOutput), bytes);

    }

} 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何在 BeanWrapperFieldSetMapper 中转换日期

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/spring-batch/how-to-convert-date-in-beanwrapperfieldsetmapper/

读取下面的 Spring 批处理作业,它从" domain.csv "中读取数据,并将其映射到一个域对象。

job-example.xml

 <bean class="org.springframework.batch.item.file.FlatFileItemReader" >

  <property name="resource" value="file:outputs/csv/domain.csv" />
  <property name="lineMapper">
    <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">

	<property name="lineTokenizer">
	  <bean
		class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
		<property name="names" value="id, domainName, lastModifiedDate" />
	  </bean>
	</property>
	<property name="fieldSetMapper">
	  <bean
		class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
		<property name="prototypeBeanName" value="domain" />
	  </bean>
	</property>

    </bean>
  </property>

</bean>

 <bean id="domain" class="com.mkyong.batch.Domain" scope="prototype" /> 

domain.csv

 1,facebook.com,Mon Jul 15 16:32:21 MYT 2013
2,google.com,Mon Jul 15 16:32:21 MYT 2013
3,youtube.com,Mon Jul 15 16:32:21 MYT 2013
4,yahoo.com,Mon Jul 15 16:32:21 MYT 2013
5,amazon.com,Mon Jul 15 16:32:21 MYT 2013 

Domain.java

 import java.util.Date;

public class DomainRanking {

	private int id;
	private String domainName;
	private Date lastModifiedDate;

	//...
} 

问题

问题是如何将字符串日期Mon Jul 15 16:32:21 MYT 2013映射/转换为java.util.Date?运行上述作业将提示以下错误消息:

 Cannot convert value of type [java.lang.String] to required type [java.util.Date] 
        for property 'lastModifiedDate': 
	no matching editors or conversion strategy found 

解决办法

参考BeanWrapperFieldSetMapper JavaDoc:

要定制将字段集值转换为注入原型所需类型的方式,有几种选择。您可以通过 customEditors 属性直接注入 PropertyEditor 实例…

为了修复它,声明一个CustomDateEditor并通过customEditors属性注入到BeanWrapperFieldSetMapper中。

job-example.xml

 <bean id="dateEditor" 
  class="org.springframework.beans.propertyeditors.CustomDateEditor">
  <constructor-arg>
	<bean class="java.text.SimpleDateFormat">
              <constructor-arg value="EEE MMM dd HH:mm:ss z yyyy" />
	</bean>
  </constructor-arg>
  <constructor-arg value="true" /> 
</bean>

<bean class="org.springframework.batch.item.file.FlatFileItemReader" >

  <property name="resource" value="file:outputs/csv/domain.csv" />
  <property name="lineMapper">
    <bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
	<property name="lineTokenizer">
	  <bean
		class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
		<property name="names" value="id, domainName, lastModifiedDate" />
	  </bean>
	</property>
	<property name="fieldSetMapper">
	  <bean
		class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
		<property name="prototypeBeanName" value="domain" />
		<property name="customEditors">
		  <map>
			<entry key="java.util.Date">
			     <ref local="dateEditor" />
			</entry>
		  </map>
		</property>
	  </bean>
	</property>

    </bean>
  </property>
</bean>

<bean id="domain" class="com.mkyong.batch.Domain" scope="prototype" /> 

字符串日期“MYT 2013 年 7 月 15 日星期一 16:32:21”由“EEE MMM dd HH:mm:ss z yyyy”表示。

Note
Do not injects the CustomDateEditor via CustomEditorConfigurer (globally), it will not works.

 <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
	<property name="customEditors">
	  <map>
		<entry key="java.util.Date">
			<ref local="dateEditor" />
		</entry>
	  </map>
	</property>
    </bean> 

参考

  1. BeanWrapperFieldSetMapper JavaDoc
  2. BeanWrapperFieldSetMapper 不适用于日期
  3. java.text.SimpleDateFormat JavaDoc
  4. Spring Batch–如何将字符串从文件转换为日期
  5. Spring 将日期注入 Bean 属性-custom Date editor
  6. 如何将字符串转换为日期–Java

date conversion spring batch

Java–如何将文件转换成字节[]

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-convert-file-into-an-array-of-bytes/

在 Java 中,我们可以使用Files.readAllBytes(path)将一个File对象转换成一个byte[]

 import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

  String filePath = "/path/to/file";

  // file to byte[], Path
  byte[] bytes = Files.readAllBytes(Paths.get(filePath));

  // file to byte[], File -> Path
  File file = new File(filePath);
  byte[] bytes = Files.readAllBytes(file.toPath()); 

NIO Files类从 Java 7 开始就可用了。

1.文件输入流

在 Java 7 之前,我们可以初始化一个预定义大小的新byte[](与文件长度相同),使用FileInputStream将文件数据读入新的byte[]

 // file to byte[], old and classic way, before Java 7
  private static void readFileToBytes(String filePath) throws IOException {

      File file = new File(filePath);
      byte[] bytes = new byte[(int) file.length()];

      FileInputStream fis = null;
      try {

          fis = new FileInputStream(file);

          //read file into bytes[]
          fis.read(bytes);

      } finally {
          if (fis != null) {
              fis.close();
          }
      }

  } 

还是这个try-with-resources版本。

 private static void readFileToBytes(String filePath) throws IOException {

    File file = new File(filePath);
    byte[] bytes = new byte[(int) file.length()];

    // funny, if can use Java 7, please uses Files.readAllBytes(path)
    try(FileInputStream fis = new FileInputStream(file)){
        fis.read(bytes);
    }

} 

2.Apache commons-Apache 公用程式

如果我们有 Apache Commons IO ,试试FileUtils

 import org.apache.commons.io.FileUtils;

  //...
  File file = new File("/path/file");
  byte[] bytes = FileUtils.readFileToByteArray(file); 

pom.xml

 <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.7</version>
  </dependency> 

3.将文件转换为 byte[],反之亦然。

下面的例子使用 NIO Files.readAllBytes将一个图像文件读入一个byte[],并使用Files.writebyte[]保存到一个新的图像文件中。

FileToBytes.java

 package com.mkyong.io.howto;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileToBytes {

    public static void main(String[] args) {

        try {

            String filePath = "/home/mkyong/test/phone.png";

            // file to bytes[]
            byte[] bytes = Files.readAllBytes(Paths.get(filePath));

            // bytes[] to file
            Path path = Paths.get("/home/mkyong/test/phone2.png");
            Files.write(path, bytes);

            System.out.println("Done");

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

} 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

Java–将文件转换为十六进制

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-convert-file-to-hex-in-java/

这篇文章展示了如何将一个文件转换成一个十六进制(hex) 的代表格式。

例如,下面是一个文本文件。

/path/to/text.txt

 ABCDEFG
12345678
!@#$%^&*()
Testing only 

我们将把上面的文件转换成下面的十六进制格式。

 41 42 43 44 45 46 47 0D 0A 31 32 33 34 35 36                 | ABCDEFG..123456
37 38 0D 0A 21 40 23 24 25 5E 26 2A 28 29 0D                 | 78..!@#$%^&*().
0A 54 65 73 74 69 6E 67 20 6F 6E 6C 79                       | .Testing only 

1.Java 将文件转换为十六进制

想法是将文件读入一个InputStream,并使用 String.format(%X) 将每个字节转换成一个十六进制代码。

FileToHex.java

 package com.mkyong.io.howto;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class FileToHex {

    private static final String NEW_LINE = System.lineSeparator();
    private static final String UNKNOWN_CHARACTER = ".";

    public static void main(String[] args) throws IOException {

        String file = "/path/to/text.txt";

        String s = convertFileToHex(Paths.get(file));
        System.out.println(s);
    }

    public static String convertFileToHex(Path path) throws IOException {

        if (Files.notExists(path)) {
            throw new IllegalArgumentException("File not found! " + path);
        }

        StringBuilder result = new StringBuilder();
        StringBuilder hex = new StringBuilder();
        StringBuilder input = new StringBuilder();

        int count = 0;
        int value;

        // path to inputstream....
        try (InputStream inputStream = Files.newInputStream(path)) {

            while ((value = inputStream.read()) != -1) {

                hex.append(String.format("%02X ", value));

                //If the character is unable to convert, just prints a dot "."
                if (!Character.isISOControl(value)) {
                    input.append((char) value);
                } else {
                    input.append(UNKNOWN_CHARACTER);
                }

                // After 15 bytes, reset everything for formatting purpose
                if (count == 14) {
                    result.append(String.format("%-60s | %s%n", hex, input));
                    hex.setLength(0);
                    input.setLength(0);
                    count = 0;
                } else {
                    count++;
                }

            }

            // if the count>0, meaning there is remaining content
            if (count > 0) {
                result.append(String.format("%-60s | %s%n", hex, input));
            }

        }

        return result.toString();
    }

} 

2.将图像文件转换为十六进制

使用图像文件运行上述程序。

 String file = "/path/to/hello.png"; 

输出

 89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44                 | .PNG........IHD
52 00 00 03 11 00 00 01 1E 08 06 00 00 00 F5                 | R.............õ
AE 98 9A 00 00 00 09 70 48 59 73 00 00 12 74                 | ®......pHYs...t
00 00 12 74 01 DE 66 1F 78 00 00 00 07 74 49                 | ...t.Þf.x....tI

//...

20 08 42 4B 88 13 21 08 82 20 08 82 20 08 42                 |  .BK..!.. .. .B
4B FC 7F 0B 00 ED 81 F6 3A 58 EC 00 00 00 00                 | Kü...í.ö:Xì....
49 45 4E 44 AE 42 60 82                                      | IEND®B`. 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io/howto

参考

如何在 Java 中将 InputStream 转换成文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-convert-inputstream-to-file-in-java/

下面是一些将InputStream转换成File的 Java 例子。

  1. 普通 Java–文件输出流
  2. Apache common io–file utils . copyinputstreamtofile
  3. Java 7–files . copy
  4. Java 9–input stream . transfer

1。普通 Java–文件输出流

这个例子下载了google.com HTML 页面,并将其作为InputStream返回。我们用FileOutputStreamInputStream复制成File,保存在某个地方。

InputStreamToFile1.java

 package com.mkyong.io.howto;

import java.io.*;
import java.net.URI;

public class InputStreamToFile1 {

    /**
     * The default buffer size
     */
    public static final int DEFAULT_BUFFER_SIZE = 8192;

    public static void main(String[] args) throws IOException {

        URI u = URI.create("https://www.google.com/");
        try (InputStream inputStream = u.toURL().openStream()) {

            File file = new File("c:\\test\\google.txt");

            copyInputStreamToFile(inputStream, file);

        }

    }

    private static void copyInputStreamToFile(InputStream inputStream, File file)
            throws IOException {

        // append = false
        try (FileOutputStream outputStream = new FileOutputStream(file, false)) {
            int read;
            byte[] bytes = new byte[DEFAULT_BUFFER_SIZE];
            while ((read = inputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
        }

    }

} 

2 .Apache common me〔t1〕

如果项目中有 Apache Commons IO,我们可以使用FileUtils.copyInputStreamToFileInputStream复制到File中。

pom.xml

 <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.7</version>
  </dependency> 

InputStreamToFile2.java

 package com.mkyong.io.howto;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

public class InputStreamToFile2 {

    public static void main(String[] args) throws IOException {

        URI u = URI.create("https://www.google.com/");
        try (InputStream inputStream = u.toURL().openStream()) {

            File file = new File("c:\\test\\google.txt");

            // commons-io
            FileUtils.copyInputStreamToFile(inputStream, file);

        }

    }

} 

3。Java 1.7–files . copy

在 Java 1.7 中,我们可以使用Files.copyInputStream复制到一个Path

InputStreamToFile3.java

 URI u = URI.create("https://www.google.com/");
  try (InputStream inputStream = u.toURL().openStream()) {

      File file = new File("c:\\test\\google.txt");

      // Java 1.7
      Files.copy(inputStream, file.toPath(), StandardCopyOption.REPLACE_EXISTING);

  } 

4 .Java 9–input stream . transfer

在 Java 9 中,我们可以使用新的 InputStream#transferToInputStream直接复制到OutputStream中。

InputStreamToFile4.java

 package com.mkyong.io.howto;

import java.io.*;
import java.net.URI;

public class InputStreamToFile4 {

    public static void main(String[] args) throws IOException {

        URI u = URI.create("https://www.google.com/");
        try (InputStream inputStream = u.toURL().openStream()) {

            File file = new File("c:\\test\\google.txt");

            // Java 9
            copyInputStreamToFileJava9(inputStream, file);

        }

    }

    // Java 9
    private static void copyInputStreamToFileJava9(InputStream input, File file)
        throws IOException {

        // append = false
        try (OutputStream output = new FileOutputStream(file, false)) {
            input.transferTo(output);
        }

    }

} 

5。将文件转换为输入流

我们可以使用FileInputStream将一个File转换成一个InputStream

 File file = new File("d:\\download\\google.txt");
  InputStream inputStream = new FileInputStream(file); 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ CD Java-io/操作方法

参考文献

Jackson——将 JSON 字符串转换成映射

原文:http://web.archive.org/web/20230101150211/https://www.mkyong.com/java/how-to-convert-java-map-to-from-json-jackson/

在 Jackson 中,我们可以使用mapper.readValue(json, Map.class)将 JSON 字符串转换成Map

用杰克逊 2.9.8 测试的 PS

pom.xml

 <dependency>
		<groupId>com.fasterxml.jackson.core</groupId>
		<artifactId>jackson-databind</artifactId>
		<version>2.9.8</version>
	</dependency> 

1.要映射的 JSON 字符串

JacksonMapExample1.java

 package com.mkyong;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.util.Map;

public class JacksonMapExample1 {

    public static void main(String[] args) {

        ObjectMapper mapper = new ObjectMapper();
        String json = "{\"name\":\"mkyong\", \"age\":\"37\"}";

        try {

            // convert JSON string to Map
            Map<String, String> map = mapper.readValue(json, Map.class);

			// it works
            //Map<String, String> map = mapper.readValue(json, new TypeReference<Map<String, String>>() {});

            System.out.println(map);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
} 

输出

 {name=mkyong, age=37} 

2.映射到 JSON 字符串

JacksonMapExample2.java

 package com.mkyong;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.HashMap;
import java.util.Map;

public class JacksonMapExample2 {

    public static void main(String[] args) {

        ObjectMapper mapper = new ObjectMapper();

        Map<String, String> map = new HashMap<>();
        map.put("name", "mkyong");
        map.put("age", "37");

        try {

            // convert map to JSON string
            String json = mapper.writeValueAsString(map);

            System.out.println(json);   // compact-print

            json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);

            System.out.println(json);   // pretty-print

        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

    }
} 

输出

 {"name":"mkyong","age":"37"}
{
  "name" : "mkyong",
  "age" : "37"
} 

3.要映射的 JSON 数组?

3.1 JSON 数组字符串是这样的

 [{"age":29,"name":"mkyong"}, {"age":30,"name":"fong"}] 

它应该转换成一个List,而不是一个Map,例如:

 // convert JSON array to List
	List<Person> list = Arrays.asList(mapper.readValue(json, Person[].class)); 

Note
Read this Jackson – Convert JSON array string to List

参考

如何在 Java 对象和 JSON 之间转换(Jackson)

原文:http://web.archive.org/web/20230101150211/https://www.mkyong.com/java/how-to-convert-java-object-to-from-json-jackson/

在本教程中,我们将向您展示如何使用 Jackson 1.x 数据绑定在 Java 对象和 JSON 之间进行转换。

Note
Jackson 1.x is a maintenance project, please use Jackson 2.x instead.Note
This tutorial is obsolete, no more update, please refer to the latest Jackson 2 tutorial – Object to / from JSON.

1.快速参考

1.1 将 Java 对象转换成 JSON,writeValue(...)

 ObjectMapper mapper = new ObjectMapper();
User user = new User();

//Object to JSON in file
mapper.writeValue(new File("c:\\user.json"), user);

//Object to JSON in String
String jsonInString = mapper.writeValueAsString(user); 

1.2 将 JSON 转换成 Java 对象,readValue(...)

 ObjectMapper mapper = new ObjectMapper();
String jsonInString = "{'name' : 'mkyong'}";

//JSON from file to Object
User user = mapper.readValue(new File("c:\\user.json"), User.class);

//JSON from String to Object
User user = mapper.readValue(jsonInString, User.class); 

所有的例子都用杰克逊 1.9.13 进行了测试

2.杰克逊依赖

对于 Jackson 1.x,它包含 6 个不同用途的独立 jar,在大多数情况下,你只需要jackson-mapper-asl

pom.xml

 <dependency>
		<groupId>org.codehaus.jackson</groupId>
		<artifactId>jackson-mapper-asl</artifactId>
		<version>1.9.13</version>
	</dependency> 

3.POJO(普通旧 Java 对象)

用于测试的用户对象。

User.java

 package com.mkyong.json;

import java.util.List;

public class User {

	private String name;
	private int age;
	private List<String> messages;

	//getters and setters
} 

4.JSON 的 Java 对象

将一个user对象转换成 JSON 格式的字符串。

JacksonExample.java

 package com.mkyong.json;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

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

		ObjectMapper mapper = new ObjectMapper();

		//For testing
		User user = createDummyUser();

		try {
			//Convert object to JSON string and save into file directly 
			mapper.writeValue(new File("D:\\user.json"), user);

			//Convert object to JSON string
			String jsonInString = mapper.writeValueAsString(user);
			System.out.println(jsonInString);

			//Convert object to JSON string and pretty print
			jsonInString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(user);
			System.out.println(jsonInString);

		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private static User createDummyUser(){

		User user = new User();

		user.setName("mkyong");
		user.setAge(33);

		List<String> msg = new ArrayList<>();
		msg.add("hello jackson 1");
		msg.add("hello jackson 2");
		msg.add("hello jackson 3");

		user.setMessages(msg);

		return user;

	}
} 

输出

 //new json file is created in D:\\user.json"

{"name":"mkyong","age":33,"messages":["hello jackson 1","hello jackson 2","hello jackson 3"]}

{
  "name" : "mkyong",
  "age" : 33,
  "messages" : [ "hello jackson 1", "hello jackson 2", "hello jackson 3" ]
} 

5.JSON 到 Java 对象

读取 JSON 字符串并将其转换回 Java 对象。

JacksonExample.java

 package com.mkyong.json;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;

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

		ObjectMapper mapper = new ObjectMapper();

		try {

			// Convert JSON string from file to Object
			User user = mapper.readValue(new File("G:\\user.json"), User.class);
			System.out.println(user);

			// Convert JSON string to Object
			String jsonInString = "{\"age\":33,\"messages\":[\"msg 1\",\"msg 2\"],\"name\":\"mkyong\"}";
			User user1 = mapper.readValue(jsonInString, User.class);
			System.out.println(user1);

		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

} 

输出

 User [name=mkyong, age=33, messages=[hello jackson 1, hello jackson 2, hello jackson 3]]

User [name=mkyong, age=33, messages=[msg 1, msg 2]] 

6.@JsonView

从 1.4 版本开始,Jackson 就支持这个功能,它可以让你控制显示哪些字段。

6.1 一个简单的类。

Views.java

 package com.mkyong.json;

public class Views {

	public static class NameOnly{};
	public static class AgeAndName extends NameOnly{};

} 

6.2 在您想要显示的字段上进行注释。

User.java

 package com.mkyong.json;

import java.util.List;
import org.codehaus.jackson.map.annotate.JsonView;

public class User {

	@JsonView(Views.NameOnly.class)
	private String name;

	@JsonView(Views.AgeAndName.class)
	private int age;

	private List<String> messages;

	//getter and setters
} 

6.3 通过writerWithView()使能@JsonView

JacksonExample.java

 package com.mkyong.json;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;

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

		ObjectMapper mapper = new ObjectMapper();
		//By default all fields without explicit view definition are included, disable this
		mapper.configure(SerializationConfig.Feature.DEFAULT_VIEW_INCLUSION, false);

		//For testing
		User user = createDummyUser();

		try {
			//display name only
			String jsonInString = mapper.writerWithView(Views.NameOnly.class).writeValueAsString(user);
			System.out.println(jsonInString);

			//display namd ana age
			jsonInString = mapper.writerWithView(Views.AgeAndName.class).writeValueAsString(user);
			System.out.println(jsonInString);

		} catch (JsonGenerationException e) {
			e.printStackTrace();
		} catch (JsonMappingException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private static User createDummyUser(){

		User user = new User();

		user.setName("mkyong");
		user.setAge(33);

		List<String> msg = new ArrayList<>();
		msg.add("hello jackson 1");
		msg.add("hello jackson 2");
		msg.add("hello jackson 3");

		user.setMessages(msg);

		return user;

	}
} 

输出

 {"name":"mkyong"}
{"name":"mkyong","age":33} 

参考

  1. 杰克逊项目主页@github
  2. Gson–将 Java 对象转换成 JSON 从 JSON 转换过来

如何将字符串转换为日期–Java

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-convert-string-to-date-java/

在本教程中,我们将向您展示如何将字符串转换为java.util.Date。许多 Java 初学者被日期转换所困扰,希望这篇总结指南能在某些方面帮助你。

 // String -> Date
    SimpleDateFormat.parse(String);

    // Date -> String
    SimpleDateFormat.format(date); 

关于java.text.SimpleDateFormat中使用的一些常见的日期和时间模式,请参考下表 JavaDoc

| 信 | 描述 | 例子 |
| y | 年 | Two thousand and thirteen |
| M | 一年中的月份 | 2007 年 7 月 |
| d | 一个月中的第几天 | 1-31 |
| E | 周中的日名称 | 星期五,星期天 |
| a | Am/pm 标记 | 上午,下午 |
| H | 一天中的小时 | 0-23 |
| h | 上午/下午的小时 | 1-12 |
| m | 小时中的分钟 | 0-60 |
| s | 分钟秒 | 0-60 |

Note
You may interest at this Java 8 example – How to convert String to LocalDate

1.string = 2013 年 6 月 7 日

如果是 3 'M ',则月份被解释为文本(周一至十二月),否则为数字(01-12)。

TestDateExample1.java

 package com.mkyong.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestDateExample1 {

    public static void main(String[] argv) {

        SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy");
        String dateInString = "7-Jun-2013";

        try {

            Date date = formatter.parse(dateInString);
            System.out.println(date);
            System.out.println(formatter.format(date));

        } catch (ParseException e) {
            e.printStackTrace();
        }

    }

} 

输出

 Fri Jun 07 00:00:00 MYT 2013
07-Jun-2013 

2.string = 2013 年 7 月 6 日

TestDateExample2.java

 package com.mkyong.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestDateExample2 {

    public static void main(String[] argv) {

        SimpleDateFormat formatter = new SimpleDateFormat("dd/MM/yyyy");
        String dateInString = "07/06/2013";

        try {

            Date date = formatter.parse(dateInString);
            System.out.println(date);
            System.out.println(formatter.format(date));

        } catch (ParseException e) {
            e.printStackTrace();
        }

    }

} 

输出

 Fri Jun 07 00:00:00 MYT 2013
07/06/2013 

3.String = Fri,2013 年 6 月 7 日

TestDateExample3.java

 package com.mkyong.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestDateExample3 {

    public static void main(String[] argv) {

        SimpleDateFormat formatter = new SimpleDateFormat("E, MMM dd yyyy");
        String dateInString = "Fri, June 7 2013";

        try {

            Date date = formatter.parse(dateInString);
            System.out.println(date);
            System.out.println(formatter.format(date));

        } catch (ParseException e) {
            e.printStackTrace();
        }

    }

} 

输出

 Fri Jun 07 00:00:00 MYT 2013
Fri, Jun 07 2013 

4.String =年 6 月 7 日星期五下午 12 点 10 分 56 秒

TestDateExample4.java

 package com.mkyong.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestDateExample4 {

    public static void main(String[] argv) {

        SimpleDateFormat formatter = new SimpleDateFormat("EEEE, MMM dd, yyyy HH:mm:ss a");
        String dateInString = "Friday, Jun 7, 2013 12:10:56 PM";

        try {

            Date date = formatter.parse(dateInString);
            System.out.println(date);
            System.out.println(formatter.format(date));

        } catch (ParseException e) {
            e.printStackTrace();
        }

    }

} 

输出

 Fri Jun 07 12:10:56 MYT 2013
Friday, Jun 07, 2013 12:10:56 PM 

5.String = 2014-10-05T15:23:01Z

Z 后缀表示 UTC,java.util.SimpleDateFormat解析不正确,需要用'+0000 '替换后缀 Z。

TestDateExample5.java

 package com.mkyong.date;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestDateExample5 {

    public static void main(String[] argv) {

        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ");
        String dateInString = "2014-10-05T15:23:01Z";

        try {

            Date date = formatter.parse(dateInString.replaceAll("Z$", "+0000"));
            System.out.println(date);

            System.out.println("time zone : " + TimeZone.getDefault().getID());
            System.out.println(formatter.format(date));

        } catch (ParseException e) {
            e.printStackTrace();
        }

    }

} 

输出

 Sun Oct 05 23:23:01 MYT 2014
time zone : Asia/Kuala_Lumpur
2014-10-05T23:23:01+0800 

在 Java 8 中,你可以把它转换成一个java.time.Instant对象,用指定的时区显示。

TestDateExample6.java

 package com.mkyong.date;

import java.time.*;

public class TestDateExample6 {

    public static void main(String[] argv) {

        String dateInString = "2014-10-05T15:23:01Z";

        Instant instant = Instant.parse(dateInString);

        System.out.println(instant);

        //get date time only
        LocalDateTime result = LocalDateTime.ofInstant(instant, ZoneId.of(ZoneOffset.UTC.getId()));

        System.out.println(result);

        //get date time + timezone
        ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of("Africa/Tripoli"));
        System.out.println(zonedDateTime);

        //get date time + timezone
        ZonedDateTime zonedDateTime2 = instant.atZone(ZoneId.of("Europe/Athens"));
        System.out.println(zonedDateTime2);

    }

} 

输出

 2014-10-05T15:23:01Z
2014-10-05T15:23:01
2014-10-05T17:23:01+02:00[Africa/Tripoli]
2014-10-05T18:23:01+03:00[Europe/Athens] 

参考

  1. 简单日期格式 JavaDoc
  2. Java 8–如何将字符串转换为本地日期
  3. stack overflow:simple date format 用“Z”字面值解析日期
  4. 维基百科:ISO 8601
  5. 时区和时差等级
  6. 格林威治时间 VS 世界协调时
  7. 什么是时区?
  8. Joda 时间

如何在 Java 中将字符串转换成 InputStream

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-convert-string-to-inputstream-in-java/

在 Java 中,我们可以使用ByteArrayInputStream将一个String转换成一个InputStream

 String str = "mkyong.com";
InputStream is = new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)); 

目录

1. ByteArrayInputStream

这个例子使用ByteArrayInputStream将一个String转换成一个InputStream,并保存到一个文件中。

StringToInputStream.java

 package com.mkyong.string;

import java.io.*;
import java.nio.charset.StandardCharsets;

public class ConvertStringToInputStream {

    public static final int DEFAULT_BUFFER_SIZE = 8192;

    public static void main(String[] args) throws IOException {

        String name = "mkyong.com";

        // String to InputStream
        InputStream is = new ByteArrayInputStream(name.getBytes(StandardCharsets.UTF_8));

        // save to a file
        save(is, "c:\\test\\file.txt");

    }

    // save the InputStream to a File
    private static void save(final InputStream is, final String fileName)
            throws IOException {

        // read bytes from InputStream and write it to FileOutputStream
        try (FileOutputStream outputStream =
                     new FileOutputStream(new File(fileName), false)) {
            int read;
            byte[] bytes = new byte[DEFAULT_BUFFER_SIZE];
            while ((read = is.read(bytes)) != -1) {
                outputStream.write(bytes, 0, read);
            }
        }

    }

} 

2 .Apache common io–ioutils

这个例子使用commons-io库、IOUtils.toInputStream API 将String转换为InputStream

pom.xml

 <dependency>
	<groupId>commons-io</groupId>
	<artifactId>commons-io</artifactId>
	<version>2.11.0</version>
</dependency> 
 import org.apache.commons.io.IOUtils;

// String to InputStream
InputStream result = IOUtils.toInputStream(str, StandardCharsets.UTF_8); 

回顾一下IOUtils.toInputStream的源代码,它是用同一个ByteArrayInputStream把一个String转换成一个InputStream

 package org.apache.commons.io;

public class IOUtils {

	public static InputStream toInputStream(final String input, final Charset charset) {
		return new ByteArrayInputStream(input.getBytes(Charsets.toCharset(charset)));
	}

	//...
} 

延伸阅读
Java 中把 InputStream 转换成 String

3。下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-string

或者

$ cd java-io

4。参考文献

如何在 Java 中复制目录

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-copy-directory-in-java/

在 Java 中,我们可以使用 Java 1.7 FileVisitor或 Apache Commons IO FileUtils.copyDirectory来复制一个目录,其中包括它的子目录和文件。

本文展示了用 Java 复制目录的几种常用方法。

  1. FileVisitor (Java 7+)
  2. FileUtils.copyDirectory(Apache common-io)
  3. 使用 Java 7 NIO 和 Java 8 Stream 进行自定义复制。
  4. 自定义副本,传统 IO。

注意NIOFiles.copy不支持复制目录内容,我们需要创建一个定制的方法来复制目录内容。

1.FileVisitor (Java 7)

这个例子展示了如何使用 FileVisitor 将目录及其内容从/home/mkyong/test/复制到/home/mkyong/test2/

Terminal

 $ tree /home/mkyong/test
test
├── test-a1.log
├── test-a2.log
└── test-b
    ├── test-b1.txt
    ├── test-b2.txt
    ├── test-c
    │   ├── test-c1.log
    │   └── test-c2.log
    └── test-d
        ├── test-d1.log
        └── test-d2.log 

1.1 这个类扩展了SimpleFileVisitor来提供默认值,并且只覆盖必要的方法。

TreeCopyFileVisitor.java

 package com.mkyong.io.utils;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class TreeCopyFileVisitor extends SimpleFileVisitor<Path> {

    private Path source;
    private final Path target;

    public TreeCopyFileVisitor(String source, String target) {
        this.source = Paths.get(source);
        this.target = Paths.get(target);
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir,
        BasicFileAttributes attrs) throws IOException {

        Path resolve = target.resolve(source.relativize(dir));
        if (Files.notExists(resolve)) {
            Files.createDirectories(resolve);
            System.out.println("Create directories : " + resolve);
        }
        return FileVisitResult.CONTINUE;

    }

    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
        throws IOException {

        Path resolve = target.resolve(source.relativize(file));
        Files.copy(file, resolve, StandardCopyOption.REPLACE_EXISTING);
        System.out.println(
                String.format("Copy File from \t'%s' to \t'%s'", file, resolve)
        );

        return FileVisitResult.CONTINUE;

    }

    @Override
    public FileVisitResult visitFileFailed(Path file, IOException exc) {
        System.err.format("Unable to copy: %s: %s%n", file, exc);
        return FileVisitResult.CONTINUE;
    }

} 

1.2 这个例子使用Files.walkFileTree遍历文件树。

CopyDirectory1.java

 package com.mkyong.io.howto;

import com.mkyong.io.utils.TreeCopyFileVisitor;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class CopyDirectory1 {

    public static void main(String[] args) {

        String fromDirectory = "/home/mkyong/test/";
        String toToDirectory = "/home/mkyong/test2/";

        try {

            copyDirectoryFileVisitor(fromDirectory, toToDirectory);

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    public static void copyDirectoryFileVisitor(String source, String target)
        throws IOException {

        TreeCopyFileVisitor fileVisitor = new TreeCopyFileVisitor(source, target);
        Files.walkFileTree(Paths.get(source), fileVisitor);

    }

} 

输出

Terminal

 Create directories : /home/mkyong/test2
Copy File from 	'/home/mkyong/test/test-a2.log' to 	'/home/mkyong/test2/test-a2.log'
Copy File from 	'/home/mkyong/test/test-a1.log' to 	'/home/mkyong/test2/test-a1.log'
Create directories : /home/mkyong/test2/test-b
Copy File from 	'/home/mkyong/test/test-b/test-b1.txt' to 	'/home/mkyong/test2/test-b/test-b1.txt'
Create directories : /home/mkyong/test2/test-b/test-c
Copy File from 	'/home/mkyong/test/test-b/test-c/test-c2.log' to 	'/home/mkyong/test2/test-b/test-c/test-c2.log'
Copy File from 	'/home/mkyong/test/test-b/test-c/test-c1.log' to 	'/home/mkyong/test2/test-b/test-c/test-c1.log'
Copy File from 	'/home/mkyong/test/test-b/test-b2.txt' to 	'/home/mkyong/test2/test-b/test-b2.txt'
Create directories : /home/mkyong/test2/test-b/test-d
Copy File from 	'/home/mkyong/test/test-b/test-d/test-d2.log' to 	'/home/mkyong/test2/test-b/test-d/test-d2.log'
Copy File from 	'/home/mkyong/test/test-b/test-d/test-d1.log' to 	'/home/mkyong/test2/test-b/test-d/test-d1.log'
Done 

检查新目录。

Terminal

 $ tree /home/mkyong/test2
test2
├── test-a1.log
├── test-a2.log
└── test-b
    ├── test-b1.txt
    ├── test-b2.txt
    ├── test-c
    │   ├── test-c1.log
    │   └── test-c2.log
    └── test-d
        ├── test-d1.log
        └── test-d2.log 

注意
官方 Java 复制示例复制一切,包括文件属性。然而,它更难阅读;此示例将方法简化为仅复制文件和目录,并排除属性。

2.fileutils . copy directory(Apache commons-io)

这个例子使用了FileUtils.copyDirectory来复制一个目录及其内容,这是一个友好而简单的 API。

pom.xml

 <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.7</version>
  </dependency> 
 import org.apache.commons.io.FileUtils;

  //...

  public static void copyFileCommonIO(String from, String to)
      throws IOException {

      File fromDir = new File(from);
      File toDir = new File(to);

      FileUtils.copyDirectory(fromDir, toDir);

  } 

3.Java NIO 和 Stream。

这个例子使用 Java 8 Files.list来模拟文件遍历器,使用 Java 7 NIO Files来检查、创建和复制目录及其内容。

CopyDirectory3.java

 package com.mkyong.io.howto;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

public class CopyDirectory3 {

    public static void main(String[] args) {

        String fromDirectory = "/home/mkyong/test/";
        String toToDirectory = "/home/mkyong/test2/";

        try {

            copyDirectoryJavaNIO(Paths.get(fromDirectory),
                Paths.get(toToDirectory));

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    public static void copyDirectoryJavaNIO(Path source, Path target)
            throws IOException {

        // is this a directory?
        if (Files.isDirectory(source)) {

            //if target directory exist?
            if (Files.notExists(target)) {
                // create it
                Files.createDirectories(target);
                System.out.println("Directory created : " + target);
            }

            // list all files or folders from the source, Java 1.8, returns a stream
            // doc said need try-with-resources, auto-close stream
            try (Stream<Path> paths = Files.list(source)) {

                // recursive loop
                paths.forEach(p ->
                        copyDirectoryJavaNIOWrapper(
                          p, target.resolve(source.relativize(p)))
                );

            }

        } else {
            // if file exists, replace it
            Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
            System.out.println(
                    String.format("Copy File from \t'%s' to \t'%s'", source, target)
            );
        }
    }

    // extract method to handle exception in lambda
    public static void copyDirectoryJavaNIOWrapper(Path source, Path target) {

        try {
            copyDirectoryJavaNIO(source, target);
        } catch (IOException e) {
            System.err.println("IO errors : " + e.getMessage());
        }

    }

} 

4.传统 IO

这个例子类似于方法 3。相反,它坚持使用传统的 IO java.io.*,仅供参考。

CopyDirectory4.java

 package com.mkyong.io.howto;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;

public class CopyDirectory4 {

    public static void main(String[] args) {

        String fromDirectory = "/home/mkyong/test/";
        String toToDirectory = "/home/mkyong/test2/";

        try {

            copyDirectoryLegacyIO(
                new File(fromDirectory),
                new File(toToDirectory));

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    public static void copyDirectoryLegacyIO(File source, File target)
        throws IOException {

        if (source.isDirectory()) {

            //if directory not exists, create it
            if (!target.exists()) {
                if (target.mkdir()) {
                    System.out.println("Directory copied from "
                            + source + "  to " + target);
                } else {
                    System.err.println("Unable to create directory : " + target);
                }
            }

            // list all the directory contents, file walker
            String[] files = source.list();
            if (files == null) {
                return;
            }

            for (String file : files) {
                //construct the src and dest file structure
                File srcFile = new File(source, file);
                File destFile = new File(target, file);
                //recursive copy
                copyDirectoryLegacyIO(srcFile, destFile);
            }

        } else {

            //if file, then copy it
            //Use bytes stream to support all file types
            InputStream in = null;
            OutputStream out = null;

            try {

                in = new FileInputStream(source);
                out = new FileOutputStream(target);

                byte[] buffer = new byte[1024];

                int length;
                //copy the file content in bytes
                while ((length = in.read(buffer)) > 0) {
                    out.write(buffer, 0, length);
                }

                System.out.println("File copied from " + source + " to " + target);

            } catch (IOException e) {

                System.err.println("IO errors : " + e.getMessage());

            } finally {
                if (in != null) {
                    in.close();
                }

                if (out != null) {
                    out.close();
                }
            }
        }

    }

} 


Java,在 20+年之后,仍然没有官方 API 来复制一个目录,创建一个Files.copyDirectory()有多难?

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何在 Java 中复制文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-copy-file-in-java/

本文展示了用 Java 复制文件的四种方法。

  1. Files.copy
  2. Apache commons me(Apache 公用程式)
  3. 番石榴
  4. 普通爪哇咖啡

在 Java 7+中,NIO Files.copy 是复制文件最简单的方法,因为它是一个内置的 API。在 Java 7 之前,Apache Commons IO FileUtils.copyFile是一个更好的解决方案,因为遗留 IO java.io.*没有 API 可以复制。

1.文件.副本(NIO)

1.1 这个代码片段使用 NIO 来复制文件。默认情况下,如果目标文件存在,复制会失败并抛出FileAlreadyExistsException

 Path fromFile = Paths.get(from);
  Path toFile = Paths.get(to);

  Files.copy(fromFile, toFile); 

1.2Files.copy接受一个 varargs 参数:

  • StandardCopyOption.REPLACE_EXISTING–如果目标文件存在,替换它。
  • StandardCopyOption.COPY_ATTRIBUTES–将文件属性复制到目标文件。
 import java.nio.file.StandardCopyOption;

  // if target or destination file exists, replace it.
  Files.copy(fromFile, toFile, StandardCopyOption.REPLACE_EXISTING);

  // multiple StandardCopyOption
  CopyOption[] options = { StandardCopyOption.REPLACE_EXISTING,
                StandardCopyOption.COPY_ATTRIBUTES,
                LinkOption.NOFOLLOW_LINKS };

  Files.copy(fromFile, toFile, options); 

下面 1.3 是一个完整的 Java NIO 复制文件的例子。

CopyFile1.java

 package com.mkyong.io.howto;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class CopyFile1 {

    public static void main(String[] args) {

        String fromFile = "/home/mkyong/dev/db.debug.conf";
        String toFile = "/home/mkyong/live/db.conf";

        try {
            copyFileNIO(fromFile, toFile);

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Copy file is done.");
    }

    public static void copyFileNIO(String from, String to) throws IOException {

        Path fromFile = Paths.get(from);
        Path toFile = Paths.get(to);

        // if fromFile doesn't exist, Files.copy throws NoSuchFileException
        if (Files.notExists(fromFile)) {
            System.out.println("File doesn't exist? " + fromFile);
            return;
        }

        // if toFile folder doesn't exist, Files.copy throws NoSuchFileException
        // if toFile parent folder doesn't exist, create it.
        Path parent = toFile.getParent();
        if(parent!=null){
            if(Files.notExists(parent)){
                Files.createDirectories(parent);
            }
        }

        // default - if toFile exist, throws FileAlreadyExistsException
        Files.copy(fromFile, toFile);

        // if toFile exist, replace it.
        // Files.copy(fromFile, toFile, StandardCopyOption.REPLACE_EXISTING);

        // multiple StandardCopyOption
        /*CopyOption[] options = { StandardCopyOption.REPLACE_EXISTING,
                StandardCopyOption.COPY_ATTRIBUTES,
                LinkOption.NOFOLLOW_LINKS };

        Files.copy(fromFile, toFile, options);*/

    }
} 

2.Apache commons me(Apache 公用程式)

在过去(Java 7 之前), commons-io 是用 Java 复制文件的最简单的解决方案。

pom.xml

 <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.7</version>
  </dependency> 
 import org.apache.commons.io.FileUtils;

  public static void copyFileCommonIO(String from, String to)
        throws IOException {

        File fromFile = new File(from);
        File toFile = new File(to);

        FileUtils.copyFile(fromFile, toFile);

  } 

3.谷歌番石榴

很多项目开始采用的番石榴,而更喜欢番石榴的Files.copy,与 Java NIO 同名,确保导入正确的包com.google.common.io.Files

pom.xml

 <dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>29.0-jre</version>
  </dependency> 
 public static void copyFileGuava(String from, String to) throws IOException {

      File fromFile = new File(from);
      File toFile = new File(to);

      // @Beta?
      com.google.common.io.Files.copy(fromFile, toFile);

  } 

番石榴的Files类从 1.0 版本开始就有了,不知道为什么Files.copy仍然有一个@Beta注释在上面?

Files.java

 package com.google.common.io;

public final class Files {

  //..
  @Beta
  public static void copy(File from, File to) throws IOException {
    checkArgument(!from.equals(to),
      "Source %s and destination %s must be different", from, to);
    asByteSource(from).copyTo(asByteSink(to));
  } 

4.普通爪哇咖啡

这个例子使用普通的 Java InputStreamOutputStream来复制一个文件。

 public static void copyFilePlainJava(String from, String to) throws IOException {

      InputStream inStream = null;
      OutputStream outStream = null;

      try {

          File fromFile = new File(from);
          File toFile = new File(to);

          inStream = new FileInputStream(fromFile);
          outStream = new FileOutputStream(toFile);

          byte[] buffer = new byte[1024];

          int length;
          while ((length = inStream.read(buffer)) > 0) {
              outStream.write(buffer, 0, length);
              outStream.flush();
          }

      } finally {
          if (inStream != null)
              inStream.close();

          if (outStream != null)
              outStream.close();
      }

  } 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何计算 XML 文档的深度(DOM 解析器)

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-count-the-depth-of-xml-document-dom-example/

在 DOM 解析器中,我们可以使用子节点的递归循环来查找或计算 XML 文档的深度。

目录

用 Java 11 测试。

1。一个 XML 文件

下面的 XML 文件包含 3 个深度级别。

src/main/resources/staff.xml

 <?xml version="1.0" encoding="utf-8"?>
<company>                     <!-- Level 1 -->
    <staff id="1001">         <!-- Level 2 -->
        <name>mkyong</name>   <!-- Level 3 -->
        <role>support</role>  <!-- Level 3 -->
        <salary currency="USD">5000</salary>
        <!-- for special characters like < &, need CDATA -->
        <bio><![CDATA[HTML tag <code>testing</code>]]></bio>
    </staff>
    <staff id="1002">
        <name>yflow</name>
        <role>admin</role>
        <salary currency="EUR">8000</salary>
        <bio><![CDATA[a & b]]></bio>
    </staff>
</company> 

2。DOM 解析器+递归循环

下面的例子使用了一个 DOM 解析器和递归循环来查找 XML 级别的深度。

CountDepthXmlDom.java

 package com.mkyong.xml.dom;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class CountDepthXmlDom {

  private static final String FILENAME = "src/main/resources/staff.xml";
  private static int DEPTH_XML = 0;

  public static void main(String[] args) {

      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

      try (InputStream is = new FileInputStream(FILENAME)) {

          DocumentBuilder db = dbf.newDocumentBuilder();

          Document doc = db.parse(is);

          // get all elements
          NodeList childNodes = doc.getChildNodes();

          printNode(childNodes, 0);

          System.out.println("Depth of XML : " + DEPTH_XML);

      } catch (ParserConfigurationException | SAXException | IOException e) {
          e.printStackTrace();
      }

  }

  // loop recursive
  private static void printNode(NodeList nodeList, int level) {
      level++;

      if (nodeList != null && nodeList.getLength() > 0) {
          for (int i = 0; i < nodeList.getLength(); i++) {

              Node node = nodeList.item(i);
              if (node.getNodeType() == Node.ELEMENT_NODE) {

                  String result = String.format(
                          "%" + level * 5 + "s : [%s]%n", node.getNodeName(), level);
                  System.out.print(result);

                  printNode(node.getChildNodes(), level);

                  // how depth is it?
                  if (level > DEPTH_XML) {
                      DEPTH_XML = level;
                  }

              }

          }
      }

  }

} 

输出

Terminal

 company : [1]
   staff : [2]
         name : [3]
         role : [3]
       salary : [3]
          bio : [3]
   staff : [2]
         name : [3]
         role : [3]
       salary : [3]
          bio : [3]
Depth of XML : 3 

3。DOM 解析器+树遍历器

下面的例子使用 DOM 的TreeWalker遍历节点并找到 XML 级别的深度。

CountDepthXmlDomTreeWalker.java

 package com.mkyong.xml.dom;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.TreeWalker;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class CountDepthXmlDomTreeWalker {

    private static final String FILENAME = "src/main/resources/staff.xml";
    private static int DEPTH_XML = 0;

    public static void main(String[] args) {

        int depth = countDepthXml(FILENAME);
        System.out.println("Depth of XML : " + depth);
    }

    private static int countDepthXml(String filename) {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

        try (InputStream is = new FileInputStream(filename)) {

            DocumentBuilder db = dbf.newDocumentBuilder();

            Document doc = db.parse(is);

            DocumentTraversal traversal = (DocumentTraversal) doc;

            // DOM tree walker
            TreeWalker walker = traversal.createTreeWalker(
                    doc.getDocumentElement(),
                    NodeFilter.SHOW_ELEMENT,
                    null,
                    true);

            traverseXmlElements(walker, 0);

        } catch (ParserConfigurationException | SAXException | IOException e) {
            e.printStackTrace();
        }

        return DEPTH_XML;

    }

    private static void traverseXmlElements(TreeWalker walker, int level) {

        level++;

        Node node = walker.getCurrentNode();

        String result = String.format(
                "%" + level * 5 + "s : [%s]%n", node.getNodeName(), level);
        System.out.print(result);

        for (Node n = walker.firstChild();
             n != null;
             n = walker.nextSibling()) {
            traverseXmlElements(walker, level);
        }

        walker.setCurrentNode(node);

        // how depth is it?
        if (level > DEPTH_XML) {
            DEPTH_XML = level;
        }

    }

} 

输出

Terminal

 company : [1]
   staff : [2]
         name : [3]
         role : [3]
       salary : [3]
          bio : [3]
   staff : [2]
         name : [3]
         role : [3]
       salary : [3]
          bio : [3]
Depth of XML : 3 

4。下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-xml

$ CD src/main/Java/com/mkyong/XML/DOM/

5。参考文献

如何在 Java 中计算 XML 元素

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-count-xml-elements-in-java-dom-parser/

本文展示了如何使用 DOM 解析器、 SAX 解析器和 XPath 来计算 Java 中指定 XML 元素的数量。

目录

用 Java 11 测试。

1。一个 XML 文件

下面是一个测试用的 XML 文件,后面所有的例子都会统计称为“staff”的 XML 元素的个数,输出是 2。

src/main/resources/staff.xml

 <?xml version="1.0" encoding="utf-8"?>
<company>
    <staff id="1001">
        <name>mkyong</name>
        <role>support</role>
    </staff>
    <staff id="1002">
        <name>yflow</name>
        <role>admin</role>
    </staff>
</company> 

2。统计 XML 元素(DOM 解析器)

 // DOM APIs
  NodeList list = doc.getElementsByTagName("staff");
  System.out.println(list.getLength());   // 2 

下面是一个完整的 DOM 解析器示例,用来计算 XML 文件中“staff”元素的数量。

CountElementXmlDomParser.java

 package com.mkyong.xml.dom;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class CountElementXmlDomParser {

  private static final String FILENAME = "src/main/resources/staff.xml";

  public static void main(String[] args) {

      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

      try (InputStream is = new FileInputStream(FILENAME)) {

          DocumentBuilder db = dbf.newDocumentBuilder();

          Document doc = db.parse(is);

          // get all elements known as "staff"
          NodeList list = doc.getElementsByTagName("staff");

          System.out.println("Number of staff elements : " + list.getLength());

      } catch (ParserConfigurationException | SAXException | IOException e) {
          e.printStackTrace();
      }

  }

} 

输出

Terminal

 Number of staff elements : 2 

3。计数 XML 元素(XPath)

 XPath xpath = XPathFactory.newInstance().newXPath();
  NodeList nodes = (NodeList)
          xpath.evaluate("//staff", doc, XPathConstants.NODESET);
  int count = nodes.getLength(); 

下面是一个完整的 DOM 和 XPath 示例,用来计算 XML 文件中“staff”元素的数量。

CountElementXmlDomXPath.java

 package com.mkyong.xml.dom;

import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class CountElementXmlDomXPath {

  private static final String FILENAME = "src/main/resources/staff.xml";

  public static void main(String[] args) {

      DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

      try (InputStream is = new FileInputStream(FILENAME)) {

          DocumentBuilder db = dbf.newDocumentBuilder();

          Document doc = db.parse(is);

          // get all elements known as "staff"
          // xpath
          XPath xpath = XPathFactory.newInstance().newXPath();
          NodeList nodes = (NodeList)
                  xpath.evaluate("//staff", doc, XPathConstants.NODESET);
          int count = nodes.getLength();

          System.out.println("Number of staff elements : " + count);

      } catch (ParserConfigurationException | SAXException |
                IOException | XPathExpressionException e) {
          e.printStackTrace();
      }

  }

} 

输出

Terminal

 Number of staff elements : 2 

4。统计 XML 元素(SAX 解析器)

下面是一个完整的 SAX 解析器示例,用来计算 XML 文件中“staff”元素的数量。

4.1 我们可以创建一个 SAX 处理程序,并计算startElement()方法中的元素。

CountElementHandlerSax.java

 package com.mkyong.xml.sax.handler;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class CountElementHandlerSax extends DefaultHandler {

  private final String elementName;
  private Integer count = 0;

  public String getElementName() {
      return elementName;
  }

  public Integer getCount() {
      return count;
  }

  public CountElementHandlerSax(String elementName) {
      this.elementName = elementName;
  }

  @Override
  public void startElement(String uri, String localName,
          String qName, Attributes attributes)
          throws SAXException {
      if (qName.equalsIgnoreCase(getElementName())) {
          count++;
      }
  }

} 

4.2 用上面的CountElementHandlerSax运行 SAX 解析器。

ReadXmlSaxParser.java

 package com.mkyong.xml.sax;

import com.mkyong.xml.sax.handler.CountElementHandlerSax;
import org.xml.sax.SAXException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.IOException;

public class ReadXmlSaxParser {

  private static final String FILENAME = "src/main/resources/staff.xml";

  public static void main(String[] args) {

      SAXParserFactory factory = SAXParserFactory.newInstance();

      try {

          SAXParser saxParser = factory.newSAXParser();

          // count elements name known as "staff"
          CountElementHandlerSax countStaffHandler =
                              new CountElementHandlerSax("staff");
          saxParser.parse(FILENAME, countStaffHandler);

          System.out.println("Number of staff elements : "
              + countStaffHandler.getCount());

      } catch (ParserConfigurationException | SAXException | IOException e) {
          e.printStackTrace();
      }

  }

} 

输出

Terminal

 Number of staff elements : 2 

5。下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-xml

$ CD src/main/Java/com/mkyong/XML/DOM/

$ CD src/main/Java/com/mkyong/XML/sax/

6。参考文献

maven——如何创建 Java 项目

原文:http://web.archive.org/web/20230101150211/https://www.mkyong.com/maven/how-to-create-a-java-project-with-maven/

在本教程中,我们将向您展示如何使用 Maven 来管理 Java 项目——创建、添加依赖项并将 Java 项目打包到可执行的 jar 文件中。最后,我们将创建一个可执行的 jar 文件,用 SHA-256 算法散列一个给定的字符串。

使用的技术:

  1. Maven 3.5.3
  2. JDK 8
  3. Apache Commons 编解码器 1.11

1.从 Maven 模板创建项目

在终端(*uix 或 Mac)或命令提示符(Windows)中,导航到要创建 Java 项目的文件夹。键入以下命令:

 mvn archetype:generate 
	-DgroupId={project-packaging}
	-DartifactId={project-name}
	-DarchetypeArtifactId={maven-template} 
	-DinteractiveMode=false 

这告诉 Maven 从一个 Maven 模板生成一个 Java 项目。举个例子,

 D:\>mvn archetype:generate -DgroupId=com.mkyong.hashing -DartifactId=java-project -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.992 s
[INFO] Finished at: 2018-09-27T17:15:57+08:00
[INFO] ------------------------------------------------------------------------ 

上面的命令将从maven-archetype-quickstart模板生成一个 Java 项目。

2.Maven 目录布局

将创建以下项目目录结构。简而言之,源代码放在文件夹/src/main/java/,单元测试代码放在/src/test/java/

P.S 上图是从 IntelliJ IDEA 抓取的,只需忽略那些.idea文件夹。

Note
Read this Maven standard directory layout.

3.POM 文件

查看生成的pom.xml。它很空,只有一个 jUnit 依赖项。

pom.xml

 <project  
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
		 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mkyong.hashing</groupId>
    <artifactId>java-project3</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>java-project</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project> 

这个 POM 文件就像 Ant build.xml文件,它描述了整个项目的信息,从目录结构,项目插件,项目依赖,如何构建这个项目等等,阅读这个官方 POM 指南

4.更新 POM

4.1 添加编译器属性,告诉 Maven 使用指定的 JDK 版本来编译源代码。

 <properties>
		<!-- https://maven.apache.org/general.html#encoding-warning -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties> 

4.2 将 jUnit 更新到 4.12

 <dependency>
		<groupId>junit</groupId>
		<artifactId>junit</artifactId>
		<version>4.12</version>
		<scope>test</scope>
	</dependency> 

4.3 为 SHA 哈希增加commons-codec

 <!-- Dependency for hashing -->
	<!-- https://search.maven.org/artifact/commons-codec/commons-codec/1.11/jar -->
	<dependency>
		<groupId>commons-codec</groupId>
		<artifactId>commons-codec</artifactId>
		<version>1.11</version>
	</dependency> 

4.4 完整的更新版本。

pom.xml

 <project  
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
		 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mkyong.hashing</groupId>
    <artifactId>java-project</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>java-project</name>
    <url>http://maven.apache.org</url>

    <properties>
        <!-- https://maven.apache.org/general.html#encoding-warning -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.11</version>
        </dependency>
    </dependencies>

</project> 

5.写代码

5.1 更新App.java以接受一个输入,并用阿沙-256 算法对其进行哈希运算。

App.java

 package com.mkyong.hashing;

import org.apache.commons.codec.digest.DigestUtils;

public class App {

    public static void main(String[] args) {

        if (args.length < 1) {
            System.err.println("Please provide an input!");
            System.exit(0);
        }
        System.out.println(sha256hex(args[0]));

    }

    public static String sha256hex(String input) {
        return DigestUtils.sha256Hex(input);
    }

} 

5.2 单元测试。

AppTest.java

 package com.mkyong.hashing;

import org.junit.Assert;
import org.junit.Test;

public class AppTest {

    private String INPUT = "123456";

    @Test
    public void testLength() {
        Assert.assertEquals(64, App.sha256hex(INPUT).length());
    }

    @Test
    public void testHex() {
        String expected = "8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92";
        Assert.assertEquals(expected, App.sha256hex(INPUT));
    }

} 

完成了。

6.Maven 构建

6.1 让我们用mvn package来建造它

 D:\java-project>mvn package
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< com.mkyong.hashing:java-project >-------------------
[INFO] Building java-project 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
......

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.mkyong.hashing.AppTest
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.067 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ java-project ---
[INFO] Building jar: D:\java-project\target\java-project-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.956 s
[INFO] Finished at: 2018-09-28T12:40:18+08:00
[INFO] ------------------------------------------------------------------------ 

它编译、运行单元测试并将项目打包成一个jar文件,并将其放入project/target文件夹。

7.运行#1

7.1 运行它。哎呀…默认情况下,Maven 没有将项目依赖关系commons-codec添加到 jar 文件中。

 D:\java-project>java -cp target/java-project-1.0-SNAPSHOT.jar com.mkyong.hashing.App 123456

Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/codec/digest/DigestUtils
        at com.mkyong.hashing.App.sha256hex(App.java:18)
        at com.mkyong.hashing.App.main(App.java:13)
Caused by: java.lang.ClassNotFoundException: org.apache.commons.codec.digest.DigestUtils
        at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(Unknown Source)
        at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(Unknown Source)
        at java.base/java.lang.ClassLoader.loadClass(Unknown Source)
        ... 2 more 

7.2 为了解决这个问题,我们可以使用这个maven-shade-plugin来创建一个 Uber/fat-jar——将所有的东西组合到一个 jar 文件中。

pom.xml

 <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <!-- Attach the shade goal into the package phase -->
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build> 

7.3 再包一次!

 D:\java-project>mvn clean package
[INFO] Scanning for projects...
[...

[INFO] --- maven-shade-plugin:3.2.0:shade (default) @ java-project ---
[INFO] Including commons-codec:commons-codec:jar:1.11 in the shaded jar.
[INFO] Replacing original artifact with shaded artifact.

[INFO] Replacing D:\java-project\target\java-project-1.0-SNAPSHOT.jar 
	with D:\java-project\target\java-project-1.0-SNAPSHOT-shaded.jar
... 

将生成两个 jar,检查文件大小:

 D:\java-project>dir target
 Volume in drive D is Samsung970
 Volume Serial Number is 10DF-E63D

 Directory of D:\java-project\target

28/09/2018  12:57 PM           335,643 java-project-1.0-SNAPSHOT.jar
28/09/2018  12:57 PM             3,053 original-java-project-1.0-SNAPSHOT.jar
... 

8.运行#2

8.1 再运行一次。好,结果在意料之中。

 D:\java-project>java -cp target/java-project-1.0-SNAPSHOT.jar com.mkyong.hashing.App 123456
8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 

8.2 我们能像 Jar 一样运行它吗?不,没有主类。

 D:\java-project>java -jar target/java-project-1.0-SNAPSHOT.jar 123456
no main manifest attribute, in target/java-project-1.0-SNAPSHOT.jar 

8.3 要解决它,像这样在maven-shade-plugin中添加主类。

pom.xml

 <plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-shade-plugin</artifactId>
	<version>3.2.0</version>
	<executions>
		<!-- Attach the shade into the package phase -->
		<execution>
			<phase>package</phase>
			<goals>
				<goal>shade</goal>
			</goals>
			<configuration>
				<transformers>
					<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
						<mainClass>com.mkyong.hashing.App</mainClass>
					</transformer>
				</transformers>
			</configuration>
		</execution>
	</executions>
</plugin> 

9.运行#3

9.1 再包一次!

 D:\java-project>mvn clean package 

9.2 将它作为 Jar 运行。

 D:\java-project>java -jar target/java-project-1.0-SNAPSHOT.jar 123456
8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 

完成了。

10.砰的一声

最终 POM 文件。

pom.xml

 <project  
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
		 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mkyong.hashing</groupId>
    <artifactId>java-project</artifactId>
    <packaging>jar</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>java-project</name>
    <url>http://maven.apache.org</url>

    <properties>
        <!-- https://maven.apache.org/general.html#encoding-warning -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.11</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.0</version>
                <executions>
                    <!-- Attach the shade into the package phase -->
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.mkyong.hashing.App</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>
</project> 

下载源代码

$ git clone https://github.com/mkyong/maven-examples.git
$ cd java-project
$ mvn package
$ java -jar target/java-project-1.0-SNAPSHOT.jar 123456

参考

  1. Maven–如何创建 Java web 应用项目
  2. 阿帕奇 Maven 项目
  3. 一个简单的 Maven 项目
  4. Java SHA 哈希示例
  5. 如何创建一个 Jar 文件,thin-jar 示例
  6. 创建一个 fat Jar 文件——一个 Jar 示例
  7. 创建一个胖罐子文件——Maven Shade 插件示例

如何用 jQuery 创建表格斑马条纹效果

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-create-a-table-zebra-stripes-effect-jquery/

下面是一个简单的例子,展示了如何用 jQuery 创建一个表格斑马条纹效果。

 <html>
<head>
<title>jQuery Zebra Stripes</title>
</head>
<script type="text/javascript" src="jquery-1.2.6.min.js"></script>

	<script type="text/javascript">
      $(function() {
        $("table tr:nth-child(even)").addClass("striped");
      });
    </script>

    <style type="text/css">
      body,td {
        font-size: 10pt;
      }
      table {
        background-color: black;
        border: 1px black solid;
        border-collapse: collapse;
      }
      th {
        border: 1px outset silver;
        background-color: maroon;
        color: white;
      }
      tr {
        background-color: white;
        margin: 1px;
      }
      tr.striped {
        background-color: coral;
      }
      td {
        padding: 1px 8px;
      }
    </style>

<body>
    <table>
      <tr>
        <th>ID</th>
        <th>Fruit</th>
        <th>Price</th>
      </tr>
      <tr>
        <td>1</td>
        <td>Apple</td>
        <td>0.60</td>
      </tr>
      <tr>
        <td>2</td>
        <td>Orange</td>
        <td>0.50</td>
      </tr>
      <tr>
        <td>3</td>
        <td>Banana</td>
        <td>0.10</td>
      </tr>
      <tr>
        <td>4</td>
        <td>strawberry</td>
        <td>0.05</td>
      </tr>
      <tr>
        <td>5</td>
        <td>carrot</td>
        <td>0.10</td>
      </tr>
    </table>
</body>
</html> 

jquery-zebra-stripes

在 jQuery 中,表格斑马条纹效果是通过一条语句实现的。

 $(function() {
        $("table tr:nth-child(even)").addClass("striped");
}); 

P.S 第 n 个孩子(偶数)”)。addClass("striped") =每个偶数行动态添加" striped" CSS 类。

Try Demo

如何用 jQuery 创建工具提示

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-create-a-tooltips-with-jquery/

这里有一个用 jQuery 创建工具提示消息的简单想法。

想法…

  1. 确定需要显示工具提示信息的“目标”。
  2. 为它创建一个工具提示消息和 CSS 样式。
  3. 三个功能,-显示工具提示隐藏工具提示改变工具提示位置
  4. 当鼠标进入目标时,使用显示工具提示显示工具提示信息,并用改变工具提示位置初始化位置。
  5. 当鼠标在“目标周围移动时,用changetooltipposition不断改变工具提示信息的位置。
  6. 当鼠标离开“目标”时,使用 hideTooltips 隐藏工具提示消息。
  7. 使用 jQuery 来完成🙂

1.目标

“提示”和“用户名标签”是显示工具提示消息的目标。

 <label id="username">Username : </label><input type="text" / size="50"> 
<span id="hint">hint (mouseover me)</span> 

2.工具提示 CSS

为工具提示消息创建 CSS 样式。

 .tooltip{
	margin:8px;
	padding:8px;
	border:1px solid blue;
	background-color:yellow;
	position: absolute;
	z-index: 2;
} 

3.显示工具提示

将工具提示消息附加到“body”标签上,并在工具提示消息的位置上签名。

 var showTooltip = function(event) {
   $('div.tooltip').remove();
   $('<div class="tooltip">I\' am tooltips! tooltips! tooltips! :)</div>')
     .appendTo('body');
   changeTooltipPosition(event);
}; 

5.更改工具提示

更改工具提示消息的位置。

 var changeTooltipPosition = function(event) {
	var tooltipX = event.pageX - 8;
	var tooltipY = event.pageY + 8;
	$('div.tooltip').css({top: tooltipY, left: tooltipX});
}; 

6.隐藏工具提示

隐藏工具提示消息。

 var hideTooltip = function() {
	$('div.tooltip').remove();
}; 

7.捆绑它

将鼠标事件绑定到目标。

 $("span#hint,label#username'").bind({
	mousemove : changeTooltipPosition,
	mouseenter : showTooltip,
	mouseleave: hideTooltip
}); 

你自己试试

在本例中,将鼠标放在提示或标签上以显示工具提示效果。

 <html>
<head>

<script type="text/javascript" src="jquery-1.4.2.min.js"></script>

<style type="text/css">
	#hint{
		cursor:pointer;
	}
	.tooltip{
		margin:8px;
		padding:8px;
		border:1px solid blue;
		background-color:yellow;
		position: absolute;
		z-index: 2;
	}
</style>

</head>

<body>

<h1>jQuery tooltips example</h1>

<label id="username">Username : </label><input type="text" / size="50"> 
<span id="hint">hint (mouseover me)</span>

<script type="text/javascript">

$(document).ready(function() {

	var changeTooltipPosition = function(event) {
	  var tooltipX = event.pageX - 8;
	  var tooltipY = event.pageY + 8;
	  $('div.tooltip').css({top: tooltipY, left: tooltipX});
	};

	var showTooltip = function(event) {
	  $('div.tooltip').remove();
	  $('<div class="tooltip">I\' am tooltips! tooltips! tooltips! :)</div>')
            .appendTo('body');
	  changeTooltipPosition(event);
	};

	var hideTooltip = function() {
	   $('div.tooltip').remove();
	};

	$("span#hint,label#username'").bind({
	   mousemove : changeTooltipPosition,
	   mouseenter : showTooltip,
	   mouseleave: hideTooltip
	});
});

</script>

</body>
</html> 

http://web.archive.org/web/20220118011614if_/https://www.mkyong.com/wp-content/uploads/jQuery/jQuery-tooltips-example.html

Try Demo

maven——如何创建 Java web 应用程序项目

原文:http://web.archive.org/web/20230101150211/https://www.mkyong.com/maven/how-to-create-a-web-application-project-with-maven/

在本教程中,我们将向您展示如何使用 Maven 来管理 Java web 项目。最后,我们将创建一个 Spring MVC web 应用程序,在 JSP 页面上显示当前日期。

使用的技术:

  1. Maven 3.5.3
  2. JDK 8
  3. 释放弹簧 5.1.0
  4. JUnit 5
  5. 回溯 1.2.3
  6. Jetty 9.4.x 或 Tomcat 8.5

1.从 Maven 模板创建一个 web 项目

从 Maven 模板创建一个 web 项目maven-archetype-webapp

 mvn archetype:generate 
	-DgroupId={project-packaging}
	-DartifactId={project-name}
	-DarchetypeArtifactId={maven-template} 
	-DinteractiveMode=false 

举个例子,

 D:\>mvn archetype:generate -DgroupId=com.mkyong.web -DartifactId=java-web-project -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------< org.apache.maven:standalone-pom >-------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] --------------------------------[ pom ]---------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:3.0.1:generate (default-cli) > generate-sources @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:3.0.1:generate (default-cli) < generate-sources @ standalone-pom <<<
[INFO]
[INFO]
[INFO] --- maven-archetype-plugin:3.0.1:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Batch mode
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-webapp:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: D:\
[INFO] Parameter: package, Value: com.mkyong.web
[INFO] Parameter: groupId, Value: com.mkyong.web
[INFO] Parameter: artifactId, Value: java-web-project
[INFO] Parameter: packageName, Value: com.mkyong.web
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: D:\java-web-project
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.509 s
[INFO] Finished at: 2018-10-04T15:25:16+08:00
[INFO] ------------------------------------------------------------------------ 

Note
Actually, this is optional to generate a web project from a Maven web template. You can always generate those folders with the classic mkdir command manually.

2.Maven 模板

2.1 将创建以下项目目录结构。

注:上图来自 IntelliJ IDEA,忽略掉那些 IDE 文件夹,比如.idea``java-web-project.iml

2.2 审核生成的pom.xml

pom.xml

 <project  
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
	http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.mkyong.web</groupId>
  <artifactId>java-web-project</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>java-web-project Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <build>
    <finalName>java-web-project</finalName>
  </build>
</project> 

P.S 生成的文件没什么价值,我们以后会全部更新。首先删除web.xml,我们不需要这个。

3.更新 POM

3.1 更新pom.xml文件,添加 Spring MVC for web framework、JUnit for unit test、Jetty server to test the web project 的依赖项,还有一些 Maven 配置。

pom.xml

 <project 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
		 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.mkyong.web</groupId>
    <artifactId>java-web-project</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>java-web-project Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <properties>
        <!-- https://maven.apache.org/general.html#encoding-warning -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <spring.version>5.1.0.RELEASE</spring.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!-- logging , spring 5 no more bridge, thanks spring-jcl -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

        <!-- junit 5, unit test -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.3.1</version>
            <scope>test</scope>
        </dependency>

        <!-- unit test -->
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-library</artifactId>
            <version>1.3</version>
            <scope>test</scope>
        </dependency>

        <!-- for web servlet -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

        <!-- Some containers like Tomcat don't have jstl library -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
            <scope>provided</scope>
        </dependency>

    </dependencies>
    <build>
        <finalName>java-web-project</finalName>
        <plugins>
            <!-- http://www.eclipse.org/jetty/documentation/current/jetty-maven-plugin.html -->
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.12.v20180830</version>
            </plugin>

            <!-- Default is too old, update to latest to run the latest Spring 5 + jUnit 5 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.0</version>
            </plugin>

            <!-- Default 2.2 is too old, update to latest -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.2.2</version>
            </plugin>

        </plugins>
    </build>

</project> 

3.2 显示项目依赖关系。

 D:\> mvn dependency:tree

...
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ java-web-project ---
[INFO] com.mkyong.web:java-web-project:war:1.0-SNAPSHOT
[INFO] +- org.springframework:spring-webmvc:jar:5.1.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-aop:jar:5.1.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-beans:jar:5.1.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-context:jar:5.1.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-core:jar:5.1.0.RELEASE:compile
[INFO] |  |  \- org.springframework:spring-jcl:jar:5.1.0.RELEASE:compile
[INFO] |  +- org.springframework:spring-expression:jar:5.1.0.RELEASE:compile
[INFO] |  \- org.springframework:spring-web:jar:5.1.0.RELEASE:compile
[INFO] +- org.springframework:spring-test:jar:5.1.0.RELEASE:compile
[INFO] +- ch.qos.logback:logback-classic:jar:1.2.3:compile
[INFO] |  +- ch.qos.logback:logback-core:jar:1.2.3:compile
[INFO] |  \- org.slf4j:slf4j-api:jar:1.7.25:compile
[INFO] +- org.junit.jupiter:junit-jupiter-engine:jar:5.3.1:test
[INFO] |  +- org.apiguardian:apiguardian-api:jar:1.0.0:test
[INFO] |  +- org.junit.platform:junit-platform-engine:jar:1.3.1:test
[INFO] |  |  +- org.junit.platform:junit-platform-commons:jar:1.3.1:test
[INFO] |  |  \- org.opentest4j:opentest4j:jar:1.1.1:test
[INFO] |  \- org.junit.jupiter:junit-jupiter-api:jar:5.3.1:test
[INFO] +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:provided
[INFO] \- javax.servlet:jstl:jar:1.2:provided
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.931 s
[INFO] Finished at: 2018-10-08T15:55:08+08:00
[INFO] ------------------------------------------------------------------------ 

4.Spring MVC + JSP + LogBack

4.1 创建几个文件来引导 Spring MVC web 项目。

SpringConfig.java

 package com.mkyong.web.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@EnableWebMvc
@Configuration
@ComponentScan({"com.mkyong.web"})
public class SpringConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/resources/");
    }

    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver
                = new InternalResourceViewResolver();
        viewResolver.setViewClass(JstlView.class);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
} 

WebInitializer.java

 package com.mkyong.web;

import com.mkyong.web.config.SpringConfig;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class WebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

} 

WelcomeController.java

 package com.mkyong.web.controller;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.Date;

@Controller
public class WelcomeController {

    private final Logger logger = LoggerFactory.getLogger(WelcomeController.class);

    @GetMapping("/")
    public String index(Model model) {
        logger.debug("Welcome to mkyong.com...");
        model.addAttribute("msg", getMessage());
        model.addAttribute("today", new Date());
        return "index";

    }

    private String getMessage() {
        return "Hello World";
    }

} 

4.2 将index.jsp文件移动到WEB-INF文件夹中,并进行更新

index.jsp

 <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<body>
<h1>${msg}</h1>
<h2>Today is <fmt:formatDate value="${today}" pattern="yyy-MM-dd" /></h2>
</body>
</html> 

4.3 记录到控制台。

logbacl.xml

 <?xml version="1.0" encoding="UTF-8"?>
<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <layout class="ch.qos.logback.classic.PatternLayout">

            <Pattern>
                %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
            </Pattern>

        </layout>
    </appender>

    <logger name="com.mkyong.web" level="debug"
            additivity="false">
        <appender-ref ref="STDOUT"/>
    </logger>

    <root level="error">
        <appender-ref ref="STDOUT"/>
    </root>

</configuration> 

5.单元测试

一个简单的 Spring MVC 5 + JUnit 5 例子。

TestWelcome.java

 package com.mkyong.web;

import com.mkyong.web.config.SpringConfig;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@SpringJUnitWebConfig(SpringConfig.class)
public class TestWelcome {

    private MockMvc mockMvc;

    @Autowired
    private WebApplicationContext webAppContext;

    @BeforeEach
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webAppContext).build();
    }

    @Test
    public void testWelcome() throws Exception {

        this.mockMvc.perform(
                get("/"))
                .andDo(print())
                .andExpect(status().isOk())
                .andExpect(view().name("index"))
                .andExpect(forwardedUrl("/WEB-INF/views/index.jsp"))
                .andExpect(model().attribute("msg", "Hello World"));
    }

} 

6.目录结构

查看最终文件和目录结构。

看这个 Maven 标准目录布局

7.演示

7.1 使用 Jetty web 服务器测试 web 项目—mvn jetty:run

 D:\> mvn jetty:run

[INFO] webAppSourceDirectory not set. Trying src\main\webapp
[INFO] Reload Mechanic: automatic
[INFO] nonBlocking:false
[INFO] Classes = D:\java-web-project\target\classes
[INFO] Configuring Jetty for project: java-web-project Maven Webapp
[INFO] Logging initialized @4821ms to org.eclipse.jetty.util.log.Slf4jLog
[INFO] Context path = /
[INFO] Tmp directory = D:\java-web-project\target\tmp
[INFO] Web defaults = org/eclipse/jetty/webapp/webdefault.xml
[INFO] Web overrides =  none
[INFO] web.xml file = null
[INFO] Webapp directory = D:\java-web-project\src\main\webapp
[INFO] jetty-9.4.12.v20180830; built: 2018-08-30T13:59:14.071Z; git: 27208684755d94a92186989f695db2d7b21ebc51; jvm 10.0.1+10

...

[INFO] 1 Spring WebApplicationInitializers detected on classpath
2018-10-08 15:11:50 [main] DEBUG com.mkyong.web.WebInitializer - No ContextLoaderListener registered, as createRootApplicationContext() did not return an application context
[INFO] DefaultSessionIdManager workerName=node0
[INFO] No SessionScavenger set, using defaults
[INFO] node0 Scavenging every 660000ms
[INFO] Initializing Spring DispatcherServlet 'dispatcher'
[INFO] Started o.e.j.m.p.JettyWebAppContext@68a78f3c{/,file:///D:/java-web-project/src/main/webapp/,AVAILABLE}{file:///D:/java-web-project/src/main/webapp/}
[INFO] Started ServerConnector@3355168{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
[INFO] Started @6271ms
[INFO] Started Jetty Server
2018-10-08 15:12:01 [qtp1373051324-19] DEBUG c.m.web.controller.WelcomeController - Welcome to mkyong.com... 

7.2 通过 http://localhost:8080/ 访问

P.S CTRL + C 停止 Jetty web 服务器。

8.部署

8.1 mvn package生成 WAR 文件进行部署。

 D:\> mvn package

...

[INFO] Packaging webapp
[INFO] Assembling webapp [java-web-project] in [D:\java-web-project\target\java-web-project]
[INFO] Processing war project
[INFO] Copying webapp resources [D:\java-web-project\src\main\webapp]
[INFO] Webapp assembled in [89 msecs]
[INFO] Building war: D:\java-web-project\target\java-web-project.war
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.844 s
[INFO] Finished at: 2018-10-08T15:31:12+08:00
[INFO] ------------------------------------------------------------------------ 

生成的 WAR 的默认目录是target/finalName。完成了。

下载源代码

$ git clone https://github.com/mkyong/maven-examples.git
$ cd java-web-project
$ mvn jetty:run

http://localhost:8080

参考

  1. Maven Surefire 插件——使用 JUnit 5 平台
  2. Maven–列出项目的所有插件
  3. Maven–标准目录布局简介
  4. Apache Tomcat Maven 插件
  5. Maven——如何创建一个 Java 项目
  6. 春季 MVC 教程

Tags : jetty junit 5 maven spring mvc web project

如何用 Java 创建目录

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-create-directory-in-java/

在 Java 中,我们可以使用 NIO Files.createDirectory创建一个目录,或者使用Files.createDirectories创建一个包含所有不存在的父目录的目录。

 try {

    Path path = Paths.get("/home/mkyong/a/b/c/");

    //java.nio.file.Files;
    Files.createDirectories(path);

    System.out.println("Directory is created!");

  } catch (IOException e) {

    System.err.println("Failed to create directory!" + e.getMessage());

  } 

1.创建目录–Java NIO

1.1 我们可以使用Files.createDirectory来创建一个目录。

  • 如果父目录不存在,抛出NoSuchFileException
  • 如果目录存在,抛出FileAlreadyExistsException
  • 如果 IO 出错,抛出IOException
 Path path = Paths.get("/home/mkyong/test2/");
  Files.createDirectory(path); 

1.2 我们可以使用Files.createDirectories创建一个包含所有不存在的父目录的目录。

  • 如果父目录不存在,请先创建它。
  • 如果目录存在,不会引发异常。
  • 如果 IO 出错,抛出IOException
 Path path = Paths.get("/home/mkyong/test2/test3/test4/");
  Files.createDirectories(path); 

1.3 这个例子使用Files.createDirectories创建一个目录/test4/,其中包含所有不存在的父目录/test2/test3/

DirectoryCreate1.java

 package com.mkyong.io.directory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class DirectoryCreate1 {

    public static void main(String[] args) {

        String dir = "/home/mkyong/test2/test3/test4/";

        try {

            Path path = Paths.get(file);

            Files.createDirectories(path);

            System.out.println("Directory is created!");

            //Files.createDirectory(path);

        } catch (IOException e) {
            System.err.println("Failed to create directory!" + e.getMessage());
        }

    }
} 

2.创建目录-传统 IO

对于遗留 IO java.io.File,类似的方法是file.mkdir()创建一个目录,file.mkdirs()创建一个包含所有不存在的父目录的目录。

file.mkdir()file.mkdirs()都返回一个布尔值,如果成功创建目录,则为真,否则为假,不抛出异常。

DirectoryCreate2.java

 package com.mkyong.io.directory;

import java.io.File;

public class DirectoryCreate2 {

    public static void main(String[] args) {

        String dir = "/home/mkyong/test2/test3/test4/";

        File file = new File(dir);

        // true if the directory was created, false otherwise
        if (file.mkdirs()) {
            System.out.println("Directory is created!");
        } else {
            System.out.println("Failed to create directory!");
        }

    }

} 

在传统 IO 中,创建目录时缺少抛出异常,这使得开发人员很难调试或理解为什么我们不能创建目录,这也是 Java 发布新的java.nio.Files来抛出适当异常的原因之一。

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何用 Java 创建 tar.gz

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-create-tar-gz-in-java/

在 Java 中,我们有ZipOutputStream创建一个 zip 文件,还有GZIPOutputStream使用 Gzip 压缩一个文件,但是没有官方 API 创建一个tar.gz文件。

在 Java 中,我们可以使用 Apache Commons Compress (仍在开发中)来创建一个.tar.gz文件。

pom.xml

 <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-compress</artifactId>
      <version>1.20</version>
  </dependency> 

注释

  1. tar 用于将文件收集到一个归档文件中,即 tarball,通常带有后缀.tar
  2. Gzip 用于压缩文件以节省空间,通常带有后缀.gz
  3. tar.gz.tgz的意思是将所有文件组合成一个归档文件,并用 Gzip 压缩。

下面的代码片段将创建一个tar.gz文件。

 import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;

//...
try (OutputStream fOut = Files.newOutputStream(Paths.get("output.tar.gz"));
     BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
     GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
     TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {

       TarArchiveEntry tarEntry = new TarArchiveEntry(file,fileName);

       tOut.putArchiveEntry(tarEntry);

       // copy file to TarArchiveOutputStream
       Files.copy(path, tOut);

       tOut.closeArchiveEntry();

       tOut.finish();

     } 

下面的代码片段将解压一个.tar.gz文件。

 import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;

//...
try (InputStream fi = Files.newInputStream(Paths.get("input.tar.gz"));
     BufferedInputStream bi = new BufferedInputStream(fi);
     GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi);
     TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) {

    ArchiveEntry entry;
    while ((entry = ti.getNextEntry()) != null) {

        // create a new path, remember check zip slip attack
        Path newPath = filename(entry, targetDir);

        //checking

        // copy TarArchiveInputStream to newPath
        Files.copy(ti, newPath);

    }
} 

1.向 tar.gz 添加两个文件

这个例子展示了如何将两个文件添加到一个tar.gz文件中。

TarGzipExample1.java

 package com.mkyong.io.howto.compress;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;

public class TarGzipExample1 {

    public static void main(String[] args) {

        try {

            Path path1 = Paths.get("/home/mkyong/test/sitemap.xml");
            Path path2 = Paths.get("/home/mkyong/test/file.txt");
            Path output = Paths.get("/home/mkyong/test/output.tar.gz");

            List<Path> paths = Arrays.asList(path1, path2);
            createTarGzipFiles(paths, output);

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    // tar.gz few files
    public static void createTarGzipFiles(List<Path> paths, Path output)
        throws IOException {

        try (OutputStream fOut = Files.newOutputStream(output);
             BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
             GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
             TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {

            for (Path path : paths) {

                if (!Files.isRegularFile(path)) {
                    throw new IOException("Support only file!");
                }

                TarArchiveEntry tarEntry = new TarArchiveEntry(
                                                  path.toFile(),
                                                  path.getFileName().toString());

                tOut.putArchiveEntry(tarEntry);

                // copy file to TarArchiveOutputStream
                Files.copy(path, tOut);

                tOut.closeArchiveEntry();

            }

            tOut.finish();

        }

    }

} 

输出——它将sitemap.xmlfile.txt添加到一个归档文件output.tar中,并用 Gzip 压缩,结果是一个output.tar.gz

Terminal

 $ tar -tvf /home/mkyong/test/output.tar.gz
-rw-r--r-- 0/0          396719 2020-08-12 14:02 sitemap.xml
-rw-r--r-- 0/0              15 2020-08-11 17:56 file.txt 

2.向 tar.gz 添加一个目录

这个例子将一个目录,包括它的子文件和子目录添加到一个归档文件中,Gzip 将它压缩到一个.tar.gz

这个想法是用Files.walkFileTree遍历一个文件树,将文件一个一个的添加到TarArchiveOutputStream中。

TarGzipExample2.java

 package com.mkyong.io.howto.compress;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class TarGzipExample2 {

    public static void main(String[] args) {

        try {

            // tar.gz a folder
            Path source = Paths.get("/home/mkyong/test");
            createTarGzipFolder(source);

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    // generate .tar.gz file at the current working directory
    // tar.gz a folder
    public static void createTarGzipFolder(Path source) throws IOException {

        if (!Files.isDirectory(source)) {
            throw new IOException("Please provide a directory.");
        }

        // get folder name as zip file name
        String tarFileName = source.getFileName().toString() + ".tar.gz";

        try (OutputStream fOut = Files.newOutputStream(Paths.get(tarFileName));
             BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
             GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
             TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {

            Files.walkFileTree(source, new SimpleFileVisitor<>() {

                @Override
                public FileVisitResult visitFile(Path file,
                                            BasicFileAttributes attributes) {

                    // only copy files, no symbolic links
                    if (attributes.isSymbolicLink()) {
                        return FileVisitResult.CONTINUE;
                    }

                    // get filename
                    Path targetFile = source.relativize(file);

                    try {
                        TarArchiveEntry tarEntry = new TarArchiveEntry(
                                file.toFile(), targetFile.toString());

                        tOut.putArchiveEntry(tarEntry);

                        Files.copy(file, tOut);

                        tOut.closeArchiveEntry();

                        System.out.printf("file : %s%n", file);

                    } catch (IOException e) {
                        System.err.printf("Unable to tar.gz : %s%n%s%n", file, e);
                    }

                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult visitFileFailed(Path file, IOException exc) {
                    System.err.printf("Unable to tar.gz : %s%n%s%n", file, exc);
                    return FileVisitResult.CONTINUE;
                }

            });

            tOut.finish();
        }

    }

} 

3.向 tar.gz 添加字符串

这个例子将String添加到一个ByteArrayInputStream中,并将其直接放入TarArchiveOutputStream中。意思是创建一个文件,不保存到本地磁盘,直接放入tar.gz中。

 public static void createTarGzipFilesOnDemand() throws IOException {

        String data1 = "Test data 1";
        String fileName1 = "111.txt";

        String data2 = "Test data 2 3 4";
        String fileName2 = "folder/222.txt";

        String outputTarGzip = "/home/mkyong/output.tar.gz";

        try (OutputStream fOut = Files.newOutputStream(Paths.get(outputTarGzip));
             BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
             GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
             TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {

            createTarArchiveEntry(fileName1, data1, tOut);
            createTarArchiveEntry(fileName2, data2, tOut);

            tOut.finish();
        }

    }

    private static void createTarArchiveEntry(String fileName,
                                              String data,
                                              TarArchiveOutputStream tOut)
                                              throws IOException {

        byte[] dataInBytes = data.getBytes();

        // create a byte[] input stream
        ByteArrayInputStream baOut1 = new ByteArrayInputStream(dataInBytes);

        TarArchiveEntry tarEntry = new TarArchiveEntry(fileName);

        // need defined the file size, else error
        tarEntry.setSize(dataInBytes.length);
        // tarEntry.setSize(baOut1.available()); alternative

        tOut.putArchiveEntry(tarEntry);

        // copy ByteArrayInputStream to TarArchiveOutputStream
        byte[] buffer = new byte[1024];
        int len;
        while ((len = baOut1.read(buffer)) > 0) {
            tOut.write(buffer, 0, len);
        }

        tOut.closeArchiveEntry();

    } 

4.解压缩文件-tar.gz

这个例子展示了如何解压缩和提取一个tar.gz文件,它还检查了 zip slip 漏洞

TarGzipExample4.java

 package com.mkyong.io.howto.compress;

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class TarGzipExample4 {

    public static void main(String[] args) {

        try {

            // decompress .tar.gz
            Path source = Paths.get("/home/mkyong/test/output.tar.gz");
            Path target = Paths.get("/home/mkyong/test2");
            decompressTarGzipFile(source, target);

        } catch (IOException e) {
            e.printStackTrace();
        }

        System.out.println("Done");

    }

    public static void decompressTarGzipFile(Path source, Path target)
        throws IOException {

        if (Files.notExists(source)) {
            throw new IOException("File doesn't exists!");
        }

        try (InputStream fi = Files.newInputStream(source);
             BufferedInputStream bi = new BufferedInputStream(fi);
             GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi);
             TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) {

            ArchiveEntry entry;
            while ((entry = ti.getNextEntry()) != null) {

                // create a new path, zip slip validate
                Path newPath = zipSlipProtect(entry, target);

                if (entry.isDirectory()) {
                    Files.createDirectories(newPath);
                } else {

                    // check parent folder again
                    Path parent = newPath.getParent();
                    if (parent != null) {
                        if (Files.notExists(parent)) {
                            Files.createDirectories(parent);
                        }
                    }

                    // copy TarArchiveInputStream to Path newPath
                    Files.copy(ti, newPath, StandardCopyOption.REPLACE_EXISTING);

                }
            }
        }
    }

    private static Path zipSlipProtect(ArchiveEntry entry, Path targetDir)
        throws IOException {

        Path targetDirResolved = targetDir.resolve(entry.getName());

        // make sure normalized file still has targetDir as its prefix,
        // else throws exception
        Path normalizePath = targetDirResolved.normalize();

        if (!normalizePath.startsWith(targetDir)) {
            throw new IOException("Bad entry: " + entry.getName());
        }

        return normalizePath;
    }

} 

延伸阅读
请查看官方 Apache Commons 压缩示例

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何用 Java 编写 XML 文件—(DOM 解析器)

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-create-xml-file-in-java-dom/

本教程展示了如何使用 Java 内置的DOM API将数据写入 XML 文件。

目录

PS 用 Java 11 测试过

读这个
如何用 Java 读取 XML 文件——(DOM 解析器)

1。将 XML 写入文件

创建 XML 并将其写入文件的步骤。

  1. 创建一个Document doc
  2. 创建 XML 元素、属性等。,并追加到Document doc
  3. 创建一个Transformer来将Document doc写入一个OutputStream

WriteXmlDom1.java

 package com.mkyong.xml.dom;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.OutputStream;

public class WriteXmlDom1 {

    public static void main(String[] args)
            throws ParserConfigurationException, TransformerException {

        DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

        // root elements
        Document doc = docBuilder.newDocument();
        Element rootElement = doc.createElement("company");
        doc.appendChild(rootElement);

        doc.createElement("staff");
        rootElement.appendChild(doc.createElement("staff"));

        //...create XML elements, and others...

        // write dom document to a file
        try (FileOutputStream output =
                     new FileOutputStream("c:\\test\\staff-dom.xml")) {
            writeXml(doc, output);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    // write doc to output stream
    private static void writeXml(Document doc,
                                 OutputStream output)
            throws TransformerException {

        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = transformerFactory.newTransformer();
        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(output);

        transformer.transform(source, result);

    }
} 

输出

Terminal

 <?xml version="1.0" encoding="UTF-8" standalone="no"?><company><staff>mkyong</staff></company> 

2。漂亮的打印 XML

2.1 默认情况下,Transformer以紧凑格式输出 XML。

Terminal

 <?xml version="1.0" encoding="UTF-8" standalone="no"?><company><staff>mkyong</staff></company> 

2.2 我们可以设置Transformer中的OutputKeys.INDENT来启用漂亮的打印格式。

 TransformerFactory transformerFactory = TransformerFactory.newInstance();
  Transformer transformer = transformerFactory.newTransformer();

  // pretty print XML
  transformer.setOutputProperty(OutputKeys.INDENT, "yes");

  DOMSource source = new DOMSource(doc);
  StreamResult result = new StreamResult(output);

  transformer.transform(source, result); 

输出

Terminal

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<company>
  <staff>mkyong</staff>
</company> 

3。编写 XML 元素、属性、注释 CDATA 等

下面的例子使用一个 DOM 解析器来创建 XML 并将其写入一个OutputStream

WriteXmlDom3.java

 package com.mkyong.xml.dom;

import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class WriteXmlDom3 {

  public static void main(String[] args)
          throws ParserConfigurationException, TransformerException {

      DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
      DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

      // root elements
      Document doc = docBuilder.newDocument();
      Element rootElement = doc.createElement("company");
      doc.appendChild(rootElement);

      // staff 1001

      // add xml elements
      Element staff = doc.createElement("staff");
      // add staff to root
      rootElement.appendChild(staff);
      // add xml attribute
      staff.setAttribute("id", "1001");

      // alternative
      // Attr attr = doc.createAttribute("id");
      // attr.setValue("1001");
      // staff.setAttributeNode(attr);

      Element name = doc.createElement("name");
      // JDK 1.4
      //name.appendChild(doc.createTextNode("mkyong"));
      // JDK 1.5
      name.setTextContent("mkyong");
      staff.appendChild(name);

      Element role = doc.createElement("role");
      role.setTextContent("support");
      staff.appendChild(role);

      Element salary = doc.createElement("salary");
      salary.setAttribute("currency", "USD");
      salary.setTextContent("5000");
      staff.appendChild(salary);

      // add xml comment
      Comment comment = doc.createComment(
              "for special characters like < &, need CDATA");
      staff.appendChild(comment);

      Element bio = doc.createElement("bio");
      // add xml CDATA
      CDATASection cdataSection =
              doc.createCDATASection("HTML tag <code>testing</code>");
      bio.appendChild(cdataSection);
      staff.appendChild(bio);

      // staff 1002
      Element staff2 = doc.createElement("staff");
      // add staff to root
      rootElement.appendChild(staff2);
      staff2.setAttribute("id", "1002");

      Element name2 = doc.createElement("name");
      name2.setTextContent("yflow");
      staff2.appendChild(name2);

      Element role2 = doc.createElement("role");
      role2.setTextContent("admin");
      staff2.appendChild(role2);

      Element salary2 = doc.createElement("salary");
      salary2.setAttribute("currency", "EUD");
      salary2.setTextContent("8000");
      staff2.appendChild(salary2);

      Element bio2 = doc.createElement("bio");
      // add xml CDATA
      bio2.appendChild(doc.createCDATASection("a & b"));
      staff2.appendChild(bio2);

      // print XML to system console
      writeXml(doc, System.out);

  }

  // write doc to output stream
  private static void writeXml(Document doc,
                               OutputStream output)
          throws TransformerException {

      TransformerFactory transformerFactory = TransformerFactory.newInstance();
      Transformer transformer = transformerFactory.newTransformer();

      // pretty print
      transformer.setOutputProperty(OutputKeys.INDENT, "yes");

      DOMSource source = new DOMSource(doc);
      StreamResult result = new StreamResult(output);

      transformer.transform(source, result);

  }
} 

输出

Terminal

 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<company>
  <staff id="1001">
      <name>mkyong</name>
      <role>support</role>
      <salary currency="USD">5000</salary>
      <!--for special characters like < &, need CDATA-->
      <bio>
          <![CDATA[HTML tag <code>testing</code>]]>
      </bio>
  </staff>
  <staff id="1002">
      <name>yflow</name>
      <role>admin</role>
      <salary currency="EUD">8000</salary>
      <bio>
          <![CDATA[a & b]]>
      </bio>
  </staff>
</company> 

4。DOM 常见问题解答

一些 DOM 解析器常见问题。

4.1 如何禁用 XML 声明?

我们可以配置OutputKeys.OMIT_XML_DECLARATION来显示 XML 声明。

 // hide the xml declaration
  // hide <?xml version="1.0" encoding="UTF-8" standalone="no"?>
  transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); 

4.2 如何改变 XML 编码?

我们可以配置OutputKeys.ENCODING来改变 XML 编码。

 // set xml encoding
  // <?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
  transformer.setOutputProperty(OutputKeys.ENCODING,"ISO-8859-1"); 

4.3 如何美化 XML?

我们可以配置OutputKeys.INDENT来启用漂亮的打印 XML。

 // pretty print
  transformer.setOutputProperty(OutputKeys.INDENT, "yes"); 

如何隐藏 XML 声明standalone="no "

我们可以配置document.setXmlStandalone(true)来隐藏 XML 声明standalone="no"

 TransformerFactory transformerFactory = TransformerFactory.newInstance();
  Transformer transformer = transformerFactory.newTransformer();

  // pretty print
  transformer.setOutputProperty(OutputKeys.INDENT, "yes");

  // hide the standalone="no"
  doc.setXmlStandalone(true);

  DOMSource source = new DOMSource(doc);
  StreamResult result = new StreamResult(output);

  transformer.transform(source, result); 

4.5 如何更改 XML 声明版本?

我们可以配置document.setXmlVersion("version")来改变 XML 声明版本。

 // set xml version
  // <?xml version="1.1" encoding="ISO-8859-1" standalone="no"?>
  doc.setXmlVersion("1.1"); 

5。下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-xml

$ CD src/main/Java/com/mkyong/XML/DOM/

6。参考文献

如何用 Java 编写 XML 文件—(JDOM)

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-create-xml-file-in-java-jdom-parser/

本教程展示了如何使用 JDOM 将数据写入 XML 文件。

目录

用 JDOM 2.0.6 测试的 PS

1。下载 JDOM 2.x

Maven for JDOM。

pom.xml

 <dependency>
      <groupId>org.jdom</groupId>
      <artifactId>jdom2</artifactId>
      <version>2.0.6</version>
  </dependency> 

2。将 XML 字符串写入控制台

以下示例使用 JDOM XMLOutputter将 XML 字符串写入系统控制台输出流。

WriteXmlJDom1.java

 package com.mkyong.xml.jdom;

import org.jdom2.Document;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import java.io.IOException;
import java.io.StringReader;

public class WriteXmlJDom1 {

    public static void main(String[] args)
        throws JDOMException, IOException {

        writeSimpleXml();

    }

    private static void writeSimpleXml() throws JDOMException, IOException {

        String xml = "<root><child id=\"100\">mkyong</child></root>";
        SAXBuilder sb = new SAXBuilder();
        Document doc = sb.build(new StringReader(xml));

        // default in compact mode
        // XMLOutputter xmlOutputter = new XMLOutputter();

        // pretty print format
        XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());

        // output to console
        xmlOutputter.output(doc, System.out);

    }

} 

输出

Terminal

 <?xml version="1.0" encoding="UTF-8"?>
<root>
  <child id="100">mkyong</child>
</root> 

3。将 XML 写入文件

JDOM XMLOutputter输出支持写入器输出流

3.1 以下示例通过FileOutputStream将 XML 写入文件。

 XMLOutputter xmlOutputter = new XMLOutputter();

  // output to any OutputStream
  try(FileOutputStream fileOutputStream =
              new FileOutputStream("c:\\test\\file.xml")){
      xmlOutputter.output(new Document(), fileOutputStream);
  } 

3.2 以下示例通过FileWriter将 XML 写入文件。

 XMLOutputter xmlOutputter = new XMLOutputter();

  // output to any Writer
  try(FileWriter fileWriter =
              new FileWriter("c:\\test\\file.xml")){
      xmlOutputter.output(new Document(), fileWriter);
  } 

3.3 审查XMLOutputter.output重载方法:

JDOM XMLOutputter

4。编写 XML 属性、注释、CDATA 等

以下示例使用 JDOM 将 XML 元素、属性、注释和 CDATA 写入输出流。

WriteXmlJDom3.java

 package com.mkyong.xml.jdom;

import org.jdom2.CDATA;
import org.jdom2.Comment;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;

import java.io.IOException;
import java.io.OutputStream;

public class WriteXmlJDom3 {

  public static void main(String[] args) throws IOException {

      writeXml(System.out);

  }

  private static void writeXml(OutputStream output) throws IOException {

      Document doc = new Document();
      doc.setRootElement(new Element("company"));

      Element staff = new Element("staff");
      // add xml attribute
      staff.setAttribute("id", "1001");

      staff.addContent(new Element("name").setText("mkyong"));
      staff.addContent(new Element("role").setText("support"));
      staff.addContent(new Element("salary")
              .setAttribute("curreny", "USD").setText("5000"));

      // add xml comments
      staff.addContent(new Comment("for special characters like < &, need CDATA"));

      // add xml CDATA
      staff.addContent(new Element("bio")
              .setContent(new CDATA("HTML tag <code>testing</code>")));

      // append child to root
      doc.getRootElement().addContent(staff);

      Element staff2 = new Element("staff");
      staff2.setAttribute("id", "1002");
      staff2.addContent(new Element("name").setText("yflow"));
      staff2.addContent(new Element("role").setText("admin"));
      staff2.addContent(new Element("salary")
              .setAttribute("curreny", "EUD").setText("8000"));
      // add xml CDATA
      staff2.addContent(new Element("bio")
              .setContent(new CDATA("a & b")));

      // append child to root
      doc.getRootElement().addContent(staff2);

      XMLOutputter xmlOutputter = new XMLOutputter();

      // pretty print
      xmlOutputter.setFormat(Format.getPrettyFormat());
      xmlOutputter.output(doc, output);

  }

} 

输出

Terminal

 <?xml version="1.0" encoding="UTF-8"?>
<company>
  <staff id="1001">
    <name>mkyong</name>
    <role>support</role>
    <salary curreny="USD">5000</salary>
    <!--for special characters like < &, need CDATA-->
    <bio><![CDATA[HTML tag <code>testing</code>]]></bio>
  </staff>
  <staff id="1002">
    <name>yflow</name>
    <role>admin</role>
    <salary curreny="EUD">8000</salary>
    <bio><![CDATA[a & b]]></bio>
  </staff>
</company> 

5。更改 XML 编码

这段代码片段将 XML 编码改为ISO-8859-1

.java

 XMLOutputter xmlOutputter = new XMLOutputter();
  // change xml encoding
  xmlOutputter.setFormat(Format.getPrettyFormat().setEncoding("ISO-8859-1"));
  xmlOutputter.output(doc, output); 

输出

Terminal

 <?xml version="1.0" encoding="ISO-8859-1"?>
<root></root> 


更多 JDOM2 示例——JDOM 2 入门

6。下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-xml

$ CD src/main/Java/com/mkyong/XML/JDOM/

7 .。参考文献

如何在 Eclipse IDE 中调试 Ant Ivy 项目

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/ant/how-to-debug-ant-ivy-project-in-eclipse-ide/

在本教程中,我们将向您展示如何在 Eclipse IDE 中调试 Ant-Ivy web 项目。

使用的技术:

  1. Eclipse 4.2
  2. Eclipse Tomcat 插件
  3. Ant 1.9.4
  4. 阿帕奇 IvyDE
  5. 弹簧 4.1.3 .释放

Note
Previous Ant Spring MVC web project will be reused.

1.安装 Apache IvyDE

安装 Apache IvyDE,它将 Ivy 集成到 Eclipse IDE 中。重启 Eclipse 以完成安装。

ivy-site

2.向项目添加 Ivy 支持

将之前的 Ant Spring MVC 项目作为 Java 项目导入到 Eclipse IDE 中(我们稍后会转换)。

Note
There are some error icons on Java source, ignore it, the error will be gone after integrated with Ivy dependency management.

2.1 右击项目文件夹,选择"配置->添加 Ivy 依赖管理"

Debug-ant-Ivy-in-eclipse-1

2.2 右键单击ivy.xmlbuild.xml,选择“添加 Ivy 库…”会出现一个 Ivy 对话框,单击完成按钮接受默认设置。

Debug-ant-Ivy-in-eclipse-2

2.3 再次右键点击项目文件夹,选择“Ivy”,现在有很多选项可用。

Debug-ant-Ivy-in-eclipse-3

3.转换为 Eclipse Web 项目

要在 Eclipse 中将导入的 Java 项目转换为 web 项目(WTP ):

3.1 右击项目,选择"属性->项目刻面" :

勾选Java,选择 1.7,勾选Dynamic Web Module,选择 2.5,点击“进一步配置可用”,点击“下一步”接受 Java 应用的默认设置。

Debug-ant-Ivy-in-eclipse-4

将“内容目录”更新到war文件夹(包含 WEB-INF 文件夹的文件夹),取消选中生成 web.xml 选项。

Debug-ant-Ivy-in-eclipse-5

3.2 在项目属性上,选择“部署程序集”,点击“添加…”按钮。选择“Java 构建路径条目-> Ivy”。

Debug-ant-Ivy-in-eclipse-6

确保文件夹是正确的,并且添加了 Ivy 库。

Debug-ant-Ivy-in-eclipse-7

4.调试它

以上项目与 Eclipse 集成,只需像往常一样调试项目。在服务器视图中,只需创建一个 Tomcat 服务器并附加 web 项目。

Debug-ant-Ivy-in-eclipse-8

完成了。

参考

  1. IvyDE 官网
  2. 蚂蚁春天 MVC web 项目

Java——解压缩 Gzip 文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-decompress-file-from-gzip-file/

在上一篇文章中,我们展示了如何用 Gzip 格式压缩文件。这篇文章展示了如何解压缩一个 Gzip 文件。

1.解压缩 Gzip 文件–GZIPInputStream

1.1 这个例子将一个 Gzip 文件sitemap.xml.gz解压回它的原始文件sitemap.xml。我们将 GZIPInputStream 复制到一个FileOutputStream中来解压一个 Gzip 文件。

GZipExample.java

 package com.mkyong.io.howto.compress;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.GZIPInputStream;

public class GZipExample {

    public static void main(String[] args) {

        Path source = Paths.get("/home/mkyong/test/sitemap.xml.gz");
        Path target = Paths.get("/home/mkyong/test/sitemap.xml");

        if (Files.notExists(source)) {
            System.err.printf("The path %s doesn't exist!", source);
            return;
        }

        try {

            GZipExample.decompressGzip(source, target);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void decompressGzip(Path source, Path target) throws IOException {

        try (GZIPInputStream gis = new GZIPInputStream(
                                      new FileInputStream(source.toFile()));
             FileOutputStream fos = new FileOutputStream(target.toFile())) {

            // copy GZIPInputStream to FileOutputStream
            byte[] buffer = new byte[1024];
            int len;
            while ((len = gis.read(buffer)) > 0) {
                fos.write(buffer, 0, len);
            }

        }

    }

} 

2.再次解压缩 Gzip 文件。

2.1 该实例类似于实例 1;相反,我们使用 NIO File.copy来复制文件。

 import java.nio.file.Files;
import java.util.zip.GZIPInputStream;

  //...
  public static void decompressGzipNio(Path source, Path target) throws IOException {

      try (GZIPInputStream gis = new GZIPInputStream(
                                    new FileInputStream(source.toFile()))) {

          Files.copy(gis, target);
      }

  } 

3.将 Gzip 文件解压缩为字节数组

3.1 这个例子将一个 Gzip 文件直接解压到一个byte[]文件中,而不是保存到本地文件中。

GZipExample3.java

 package com.mkyong.io.howto.compress;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.GZIPInputStream;

public class GZipExample3 {

    public static void main(String[] args) {

        // decompress
        Path source = Paths.get("/home/mkyong/test/sitemap.xml.gz");

        if (Files.notExists(source)) {
            System.err.printf("The path %s doesn't exist!", source);
            return;
        }

        try {

            byte[] bytes = GZipExample.decompressGzipToBytes(source);
            // do task for the byte[]

            //convert byte[] to string for display
            System.out.println(new String(bytes, StandardCharsets.UTF_8));

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    // decompress a Gzip file into a byte arrays
    public static byte[] decompressGzipToBytes(Path source) throws IOException {

        ByteArrayOutputStream output = new ByteArrayOutputStream();

        try (GZIPInputStream gis = new GZIPInputStream(
                                      new FileInputStream(source.toFile()))) {

            // copy GZIPInputStream to ByteArrayOutputStream
            byte[] buffer = new byte[1024];
            int len;
            while ((len = gis.read(buffer)) > 0) {
                output.write(buffer, 0, len);
            }

        }

        return output.toByteArray();

    }

} 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何用 Java 解压一个 zip 文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-decompress-files-from-a-zip-file/

这篇文章展示了如何使用ZipInputStreamzip4j库在 Java 中解压一个 zip 文件。

要手动解压缩文件,记得添加对 zip slip 漏洞的验证。

 Path targetDirResolved = targetDir.resolve(zipEntry.getName());

  // make sure normalized file still has targetDir as its prefix
  Path normalizePath = targetDirResolved.normalize();

  if (!normalizePath.startsWith(targetDir)) {
      // may be zip slip, better stop and throws exception
      throw new IOException("Bad zip entry: " + zipEntry.getName());
  } 

1.压缩文件

下面这个 zip 文件结构就是本文创建的——用 Java 创建 zip 文件。稍后我们将展示如何将它解压到一个新的文件夹中。

1.1 zip 文件只包含文件。

/home/mkyong/zip/test.zip

 $ unzip -l test.zip

Archive:  test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2020-08-06 18:49   test-a2.log
        0  2020-08-06 18:49   test-a1.log
       14  2020-08-06 18:49   data/db.debug.conf
       42  2020-08-06 18:49   README.md
       32  2020-08-06 18:49   Test.java
        0  2020-08-06 18:49   test-b/test-b1.txt
        0  2020-08-06 18:49   test-b/test-c/test-c2.log
        0  2020-08-06 18:49   test-b/test-c/test-c1.log
        0  2020-08-06 18:49   test-b/test-b2.txt
        0  2020-08-06 18:49   test-b/test-d/test-d2.log
        0  2020-08-06 18:49   test-b/test-d/test-d1.log
---------                     -------
       88                     11 files 

1.2 zip 文件包含文件夹和文件。

/home/mkyong/zip/test.zip

 $ unzip -l test.zip

Archive:  test.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2020-07-27 15:10   test-a2.log
        0  2020-07-23 14:55   test-a1.log
        0  2020-08-06 18:57   data/
       14  2020-08-04 14:07   data/db.debug.conf
       42  2020-08-05 19:04   README.md
       32  2020-08-05 19:04   Test.java
        0  2020-08-06 18:57   test-b/
        0  2020-07-24 15:49   test-b/test-b1.txt
        0  2020-08-06 18:57   test-b/test-c/
        0  2020-07-27 15:11   test-b/test-c/test-c2.log
        0  2020-07-27 15:11   test-b/test-c/test-c1.log
        0  2020-07-27 15:10   test-b/test-b2.txt
        0  2020-08-06 18:57   test-b/test-d/
        0  2020-07-27 15:11   test-b/test-d/test-d2.log
        0  2020-07-27 15:11   test-b/test-d/test-d1.log
---------                     -------
       88                     15 files 

2. Unzip file – ZipInputStream

要解压缩一个 zip 文件,我们使用ZipInputStream来读取 zip 文件,并将文件从 zip 文件复制到一个新的文件夹中(zip 文件之外)。

下面这个例子将一个 zip 文件/home/mkyong/zip/test.zip解压到一个文件夹/home/mkyong/zip/,也提供了一个验证来防止 zip slip 漏洞

ZipFileUnZipExample.java

 package com.mkyong.io.howto;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public class ZipFileUnZipExample {

    public static void main(String[] args) {

        Path source = Paths.get("/home/mkyong/zip/test.zip");
        Path target = Paths.get("/home/mkyong/zip/");

        try {

            unzipFolder(source, target);
            System.out.println("Done");

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static void unzipFolder(Path source, Path target) throws IOException {

        try (ZipInputStream zis = new ZipInputStream(new FileInputStream(source.toFile()))) {

            // list files in zip
            ZipEntry zipEntry = zis.getNextEntry();

            while (zipEntry != null) {

                boolean isDirectory = false;
                // example 1.1
                // some zip stored files and folders separately
                // e.g data/
                //     data/folder/
                //     data/folder/file.txt
                if (zipEntry.getName().endsWith(File.separator)) {
                    isDirectory = true;
                }

                Path newPath = zipSlipProtect(zipEntry, target);

                if (isDirectory) {
                    Files.createDirectories(newPath);
                } else {

                    // example 1.2
                    // some zip stored file path only, need create parent directories
                    // e.g data/folder/file.txt
                    if (newPath.getParent() != null) {
                        if (Files.notExists(newPath.getParent())) {
                            Files.createDirectories(newPath.getParent());
                        }
                    }

                    // copy files, nio
                    Files.copy(zis, newPath, StandardCopyOption.REPLACE_EXISTING);

                    // copy files, classic
                    /*try (FileOutputStream fos = new FileOutputStream(newPath.toFile())) {
                        byte[] buffer = new byte[1024];
                        int len;
                        while ((len = zis.read(buffer)) > 0) {
                            fos.write(buffer, 0, len);
                        }
                    }*/
                }

                zipEntry = zis.getNextEntry();

            }
            zis.closeEntry();

        }

    }

    // protect zip slip attack
    public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir)
        throws IOException {

        // test zip slip vulnerability
        // Path targetDirResolved = targetDir.resolve("../../" + zipEntry.getName());

        Path targetDirResolved = targetDir.resolve(zipEntry.getName());

        // make sure normalized file still has targetDir as its prefix
        // else throws exception
        Path normalizePath = targetDirResolved.normalize();
        if (!normalizePath.startsWith(targetDir)) {
            throw new IOException("Bad zip entry: " + zipEntry.getName());
        }

        return normalizePath;
    }

} 

输出

Terminal

 $ pwd
/home/mkyong/zip/

$ tree
.
├── data
│   └── db.debug.conf
├── README.md
├── test-a1.log
├── test-a2.log
├── test-b
│   ├── test-b1.txt
│   ├── test-b2.txt
│   ├── test-c
│   │   ├── test-c1.log
│   │   └── test-c2.log
│   └── test-d
│       ├── test-d1.log
│       └── test-d2.log
├── Test.java 

3.解压缩文件–zip4j

这个例子使用了 zip4j 库来解压一个 zip 文件。

pom.xml

 <dependency>
      <groupId>net.lingala.zip4j</groupId>
      <artifactId>zip4j</artifactId>
      <version>2.6.1</version>
  </dependency> 
 // it takes `File` as arguments
  public static void unzipFolderZip4j(Path source, Path target)
        throws IOException {

        new ZipFile(source.toFile())
                .extractAll(target.toString());

  } 

4.ZipException:无效的条目大小

如果我们遇到下面的invalid entry size异常,这意味着 zip 文件在复制、传输或创建过程中被破坏。没有办法修复损坏的文件大小,再次获得一个新的 zip 文件。

Terminal

 java.util.zip.ZipException: invalid entry size (expected 0 but got 1282 bytes)
	at java.base/java.util.zip.ZipInputStream.readEnd(ZipInputStream.java:398)
	at java.base/java.util.zip.ZipInputStream.read(ZipInputStream.java:197)
	at java.base/java.io.FilterInputStream.read(FilterInputStream.java:107)
	at com.mkyong.io.howto.ZipFileUnZipExample.unzipFolder(ZipFileUnZipExample.java:63)
	at com.mkyong.io.howto.ZipFileUnZipExample.main(ZipFileUnZipExample.java:22) 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io/howto

参考

如何在 Spring 中为 bean 属性注入值

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/spring/how-to-define-bean-properties-in-spring/

在 Spring 中,有三种方法可以向 bean 属性注入值。

  • 普通方法
  • 捷径
  • “p”模式

请看一个简单的 Java 类,它包含两个属性——name 和 type。稍后,您将使用 Spring 向 bean 属性中注入值。

 package com.mkyong.common;

public class FileNameGenerator 
{
	private String name;
	private String type;

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
} 

1.普通方法

在“value”标记内注入值,并用“property”标记括起来。

 <beans 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean id="FileNameGenerator" class="com.mkyong.common.FileNameGenerator">
		<property name="name">
			<value>mkyong</value>
		</property>
		<property name="type">
			<value>txt</value>
		</property>
	</bean>
</beans> 

2.捷径

用“值”属性注入值。

 <beans 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean id="FileNameGenerator" class="com.mkyong.common.FileNameGenerator">
		<property name="name" value="mkyong" />
		<property name="type" value="txt" />
	</bean>

</beans> 

3.“p”模式

通过使用“p”模式作为属性来注入值。

 <beans 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean id="FileNameGenerator" class="com.mkyong.common.FileNameGenerator" 
             p:name="mkyong" p:type="txt" />

</beans> 

记得在 Spring XML bean 配置文件中声明了xmlns:p = " http://www . Spring framework . org/schema/p

结论

使用哪种方法完全取决于个人偏好,这不会影响注入到 bean 属性中的值。

spring

如何在 Java 中删除目录

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-delete-directory-in-java/

如果我们在 Java 中使用 NIO Files.delete删除一个非空目录,它会抛出DirectoryNotEmptyException;对于遗留 IO File.delete删除非空目录,它返回 false。标准的解决方案是递归地循环目录,首先删除所有子目录的内容(子文件或子目录),然后删除父目录。

这个例子展示了在 Java 中删除目录的一些常用方法。

  1. Files.walkFileTree + FileVisitor (Java 7)
  2. Files.walk (Java 8)
  3. FileUtils.deleteDirectory(阿帕奇通用 IO)
  4. 目录中的递归删除(普通 Java 代码)

目录结构。

我们使用Files来创建用于测试的目录和文件。

 public static void createDummyFiles() throws IOException {
      Files.createDirectories(Paths.get("/home/mkyong/test2/test3/test4/test5/"));
      Files.write(Paths.get("/home/mkyong/test2/test2.log"), "hello".getBytes());
      Files.write(Paths.get("/home/mkyong/test2/test3/test3.log"), "hello".getBytes());
  } 

查看目录结构。

Terminal

 $ tree test2
test2
├── test2.log
└── test3
    ├── test3.log
    └── test4
        └── test5 

稍后我们将删除test2目录,包括它的所有内容(子目录或子文件)

1.删除目录–files . walk filetree(Java 7)

这个例子使用 NIO Files.walkFileTree + SimpleFileVisitor删除一个目录。FileVisitor将访问指定路径的所有子目录和子文件。

DirectoryDelete1.java

 package com.mkyong.io.directory;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class DirectoryDelete1 {

    public static void createDummyFiles() throws IOException {
        Files.createDirectories(Paths.get("/home/mkyong/test2/test3/test4/test5/"));
        Files.write(Paths.get("/home/mkyong/test2/test2.log"), "hello".getBytes());
        Files.write(Paths.get("/home/mkyong/test2/test3/test3.log"), "hello".getBytes());
    }

    public static void main(String[] args) {

        String dir = "/home/mkyong/test2/";

        try {

            createDummyFiles();

            deleteDirectoryJava7(dir);

            System.out.println("Done");

        } catch (IOException e) {
            System.err.printf("Failed to delete the directory %n%s%n", e);
        }

    }

    public static void deleteDirectoryJava7(String filePath)
        throws IOException {

        Path path = Paths.get(filePath);

        Files.walkFileTree(path,
            new SimpleFileVisitor<>() {

                // delete directories or folders
                @Override
                public FileVisitResult postVisitDirectory(Path dir,
                                                          IOException exc)
                                                          throws IOException {
                    Files.delete(dir);
                    System.out.printf("Directory is deleted : %s%n", dir);
                    return FileVisitResult.CONTINUE;
                }

                // delete files
                @Override
                public FileVisitResult visitFile(Path file,
                                                 BasicFileAttributes attrs)
                                                 throws IOException {
                    Files.delete(file);
                    System.out.printf("File is deleted : %s%n", file);
                    return FileVisitResult.CONTINUE;
                }
            }
        );

    }

} 

输出

Terminal

 File is deleted : /home/mkyong/test2/test2.log
Directory is deleted : /home/mkyong/test2/test3/test4/test5
Directory is deleted : /home/mkyong/test2/test3/test4
File is deleted : /home/mkyong/test2/test3/test3.log
Directory is deleted : /home/mkyong/test2/test3
Directory is deleted : /home/mkyong/test2

Done 

2.删除目录–files . walk(Java 8)

这个例子使用Files.walk为目录返回一个Stream<Path>Stream<Path>包含指定路径的所有子目录和子文件。

 public static void deleteDirectoryJava8(String dir) throws IOException {

      Path path = Paths.get(dir);

      // read java doc, Files.walk need close the resources.
      // try-with-resources to ensure that the stream's open directories are closed
      try (Stream<Path> walk = Files.walk(path)) {
          walk
                  .sorted(Comparator.reverseOrder())
                  .forEach(DirectoryDelete::deleteDirectoryJava8Extract);
      }

  }

  // extract method to handle exception in lambda
  public static void deleteDirectoryJava8Extract(Path path) {
      try {
          Files.delete(path);
      } catch (IOException e) {
          System.err.printf("Unable to delete this path : %s%n%s", path, e);
      }
  } 

3.删除目录–FileUtils(通用 IO)

这个例子使用 Apache Common IO FileUtils.deleteDirectory删除一个目录。

pom.xml

 <dependency>
      <groupId>commons-io</groupId>
      <artifactId>commons-io</artifactId>
      <version>2.7</version>
  </dependency> 
 import org.apache.commons.io.FileUtils;

  FileUtils.deleteDirectory(new File(dir)); 

4.删除目录–普通 Java 代码

这个例子使用遗留 IO java.io.*和一个简单的递归循环算法来删除一个目录。

 public static void deleteDirectoryLegacyIO(File file) {

    File[] list = file.listFiles();
    if (list != null) {
        for (File temp : list) {
            //recursive delete
            System.out.println("Visit " + temp);
            deleteDirectoryLegacyIO(temp);
        }
    }

    if (file.delete()) {
        System.out.printf("Delete : %s%n", file);
    } else {
        System.err.printf("Unable to delete file or directory : %s%n", file);
    }

} 

输出

Terminal

 Visit /home/mkyong/test2/test2.log
Delete : /home/mkyong/test2/test2.log
Visit /home/mkyong/test2/test3
Visit /home/mkyong/test2/test3/test4
Visit /home/mkyong/test2/test3/test4/test5
Delete : /home/mkyong/test2/test3/test4/test5
Delete : /home/mkyong/test2/test3/test4
Visit /home/mkyong/test2/test3/test3.log
Delete : /home/mkyong/test2/test3/test3.log
Delete : /home/mkyong/test2/test3
Delete : /home/mkyong/test2

Done 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何在 Java 中删除文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-delete-file-in-java/

在 Java 中,我们可以使用 NIO Files.delete(Path)Files.deleteIfExists(Path)来删除文件。

1.用 Java NIO 删除文件

1.1Files.delete(Path)删除一个文件,不返回任何内容,或者失败时抛出一个异常。

DeleteFile1.java

 package com.mkyong.io.file;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class DeleteFile1 {

    public static void main(String[] args) {

        String fileName = "/home/mkyong/app1.log";
        try {
            Files.delete(Paths.get(fileName));
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

} 

如果文件不存在,它抛出NoSuchFileException

 java.nio.file.NoSuchFileException: /home/mkyong/app1.log 

1.2Files.deleteIfExists(Path)也删除一个文件,但它返回一个布尔值,如果文件删除成功则为真;如果文件不存在,不抛出异常。

DeleteFile2.java

 package com.mkyong.io.file;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class DeleteFile2 {

    public static void main(String[] args) {

        String fileName = "/home/mkyong/app.log";

        try {
            boolean result = Files.deleteIfExists(Paths.get(fileName));
            if (result) {
                System.out.println("File is deleted!");
            } else {
                System.out.println("Sorry, unable to delete the file.");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

} 

2.用 Java IO 删除文件

2.1 对于遗留文件 IO java.io.*,我们可以使用File.delete()删除一个文件,如果文件删除成功,它将返回 boolean,true,否则返回 false。

DeleteFile3.java

 package com.mkyong.io.file;

import java.io.File;

public class DeleteFile3 {

    public static void main(String[] args) {

        String fileName = "/home/mkyong/app1.log";

        try {
            File file = new File(fileName);
            if (file.delete()) {
                System.out.println(file.getName() + " is deleted!");
            } else {
                System.out.println("Sorry, unable to delete the file.");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

} 

2.2File.deleteOnExit()有点特殊,它会在 JVM 正常终止时删除文件。但是,不能保证文件会被删除,请小心使用,或者避免使用这种方法。

 File file = new File(fileName);
  file.deleteOnExit(); 

注意
遗留文件 IO java.io.*几个弊端,总是挑 Java 文件 NIO,java.nio.*

参考

如何删除 JSF 数据表中的行

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/how-to-delete-row-in-jsf-datatable/

这个例子是对前面的 JSF 2 数据表例子的增强,通过添加一个“删除”函数来删除数据表中的行。

删除概念

整体概念非常简单:

1.在每一行的末尾分配一个“删除”链接。

 //...
<h:dataTable value="#{order.orderList}" var="o">

<h:column>

    <f:facet name="header">Action</f:facet>

    <h:commandLink value="Delete" action="#{order.deleteAction(o)}" />

</h:column> 

2.如果单击了“删除”链接,则将当前行对象传递给 deleteAction()。在 deleteAction()方法中,只是从“列表”中删除当前行对象并返回到当前页面。

 public String deleteAction(Order order) {

	orderList.remove(order);
	return null;
} 

freestar.config.enabled_slots.push({ placementName: "mkyong_incontent_1", slotId: "mkyong_incontent_1" });

例子

一个 JSF 2.0 的例子来实现上述概念,删除数据表中的行。

1.受管 Bean

一个名为“order”的托管 bean,不言自明。

 package com.mkyong;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean(name="order")
@SessionScoped
public class OrderBean implements Serializable{

	private static final long serialVersionUID = 1L;

	private static final ArrayList<Order> orderList = 
		new ArrayList<Order>(Arrays.asList(

		new Order("A0001", "Intel CPU", 
				new BigDecimal("700.00"), 1),
		new Order("A0002", "Harddisk 10TB", 
				new BigDecimal("500.00"), 2),
		new Order("A0003", "Dell Laptop", 
				new BigDecimal("11600.00"), 8),
		new Order("A0004", "Samsung LCD", 
				new BigDecimal("5200.00"), 3),
		new Order("A0005", "A4Tech Mouse", 
				new BigDecimal("100.00"), 10)
	));

	public ArrayList<Order> getOrderList() {

		return orderList;

	}

	public String deleteAction(Order order) {

		orderList.remove(order);
		return null;
	}

	public static class Order{

		String orderNo;
		String productName;
		BigDecimal price;
		int qty;

		public Order(String orderNo, String productName, 
				BigDecimal price, int qty) {
			this.orderNo = orderNo;
			this.productName = productName;
			this.price = price;
			this.qty = qty;
		}

		//getter and setter methods
	}
} 

2.JSF·佩奇

JSF 页面显示带有 dataTable 标记的数据,并创建一个“删除”链接来删除行记录。

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html    
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      >
    <h:head>
    	<h:outputStylesheet library="css" name="table-style.css"  />
    </h:head>
    <h:body>

    	<h1>JSF 2 dataTable example</h1>
    	<h:form>
    		<h:dataTable value="#{order.orderList}" var="o"
    			styleClass="order-table"
    			headerClass="order-table-header"
    			rowClasses="order-table-odd-row,order-table-even-row"
    		>

    		<h:column>

    			<f:facet name="header">Order No</f:facet>
    			#{o.orderNo}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Product Name</f:facet>
    			#{o.productName}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Price</f:facet>
    			#{o.price}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Quantity</f:facet>
    			#{o.qty}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Action</f:facet>

    			<h:commandLink value="Delete" 
                                action="#{order.deleteAction(o)}" />

    		</h:column>

    		</h:dataTable>

    	</h:form>
    </h:body>
</html> 

3.演示

从上到下,显示被删除的行记录。

jsf2-dataTable-Delete-Example-1

jsf2-dataTable-Delete-Example-2

下载源代码

Download It – JSF-2-DataTable-Delete-Example.zip (10KB)Tags : datatable delete jsf2freestar.config.enabled_slots.push({ placementName: "mkyong_leaderboard_btf", slotId: "mkyong_leaderboard_btf" });

如何在 Java 中删除临时文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-delete-temporary-file-in-java/

在 Java 中,我们可以使用 NIO Files.delete()Files.deleteIfExists()来删除一个临时文件,它的工作原理就像删除一个普通的文本文件一样。

 // create a temporary file
  Path path = Files.createTempFile(null, ".log");
  // delete
  Files.delete(path);

  // or
  if(Files.deleteIfExists(path)){
    // success
  }else{
    // file does not exist
  } 

1.删除临时文件–Java NIO

这个例子使用java.nio创建一个临时文件,写一行,然后删除它。

TempFileDelete1.java

 package com.mkyong.io.temp;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

public class TempFileDelete1 {

    public static void main(String[] args) {

        try {

            Path temp = Files.createTempFile(null, ".log");
            System.out.println(temp);

            // if file doesn't exist throws NoSuchFileException
            // Files.delete(temp);

            // write a line
            Files.write(temp, "Hello World".getBytes(StandardCharsets.UTF_8));

            // check if file exists before delete
            boolean result = Files.deleteIfExists(temp);
            if (result) {
                System.out.println("File is success delete.");
            } else {
                System.out.println("File doesn't exist.");
            }

        } catch (IOException e) {
            System.err.println("Unable to delete the file!");
            e.printStackTrace();
        }

    }

} 

输出

 /tmp/16821893865655843803.log
File is success delete. 

2.删除临时文件 Java IO

这个例子使用遗留 IO java.io创建一个临时文件,并在以后删除它。

TempFileDelete2.java

 package com.mkyong.io.temp;

import java.io.File;
import java.io.IOException;

public class TempFileDelete2 {

    public static void main(String[] args) {

        try {

            File tempFile = File.createTempFile("abc_", ".log");
            System.out.println(tempFile);

            boolean result = tempFile.delete();
            if (result) {
                System.out.println(tempFile.getName() + " is deleted!");
            } else {
                System.out.println("Sorry, unable to delete the file.");
            }

            // delete when JVM exit normally.
            // tempFile.deleteOnExit();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

} 

只有当“JVM 正常退出”小心使用临时文件时,file.deleteOnExit()才会删除它。

注意
遗留 IO 有一些缺点,如果可能的话,总是挑选新的 Java NIO java.nio.*来做文件 IO 的东西。

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考

如何将基于 Maven 的 war 文件部署到 Tomcat

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/maven/how-to-deploy-maven-based-war-file-to-tomcat/

在本教程中,我们将向您展示如何在 Tomcat 6 和 7 中使用 Maven-Tomcat 插件将 WAR 文件打包并部署到 Tomcat。

使用的库:

  1. maven3
  2. Tomcat 6.0.37
  3. Tomcat 7.0.53

Tomcat 7
Deploy URL = http://localhost:8080/manager/text
Command = mvn tomcat7:deploy

Tomcat 6
部署 URL =http://localhost:8080/manager/
命令= mvn tomcat6:deploy

1.Tomcat 7 示例

这个例子向您展示了如何在 Tomcat 7 上打包和部署 WAR 文件。

1.1 Tomcat 认证
添加一个角色为manager-guimanager-script的用户。

%TOMCAT7_PATH%/conf/tomcat-users.xml

 <?xml version='1.0' encoding='utf-8'?>
<tomcat-users>

	<role rolename="manager-gui"/>
	<role rolename="manager-script"/>
	<user username="admin" password="password" roles="manager-gui,manager-script" />

</tomcat-users> 

1.2 Maven 认证
在 Maven 设置文件中添加上面 Tomcat 的用户,以后 Maven 将使用这个用户登录 Tomcat 服务器。

%MAVEN_PATH%/conf/settings.xml

 <?xml version="1.0" encoding="UTF-8"?>
<settings ...>
	<servers>

		<server>
			<id>TomcatServer</id>
			<username>admin</username>
			<password>password</password>
		</server>

	</servers>
</settings> 

1.3 Tomcat7 Maven 插件
声明一个 Maven Tomcat 插件。

pom.xml

 <plugin>
		<groupId>org.apache.tomcat.maven</groupId>
		<artifactId>tomcat7-maven-plugin</artifactId>
		<version>2.2</version>
		<configuration>
			<url>http://localhost:8080/manager/text</url>
			<server>TomcatServer</server>
			<path>/mkyongWebApp</path>
		</configuration>
	</plugin> 

它是如何工作的?
在部署过程中,它告诉 Maven 通过“http://localhost:8080/manager/text”,在路径“/mkyongWebApp”上,使用“Tomcat server”(在 settings.xml 中)用户名和密码进行认证,将 WAR 文件部署到 Tomcat 服务器上。

1.4 部署到 Tomcat
命令来操作 Tomcat 上的 WAR 文件。

 mvn tomcat7:deploy 
mvn tomcat7:undeploy 
mvn tomcat7:redeploy 

例子

 > mvn tomcat7:deploy

...
[INFO] Deploying war to http://localhost:8080/mkyongWebApp
Uploading: http://localhost:8080/manager/text/deploy?path=%2FmkyongWebApp&update=true
Uploaded: http://localhost:8080/manager/text/deploy?path=%2FmkyongWebApp&update=true (13925 KB at 35250.9 KB/sec)

[INFO] tomcatManager status code:200, ReasonPhrase:OK
[INFO] OK - Deployed application at context path /mkyongWebApp
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.507 s
[INFO] Finished at: 2014-08-05T11:35:25+08:00
[INFO] Final Memory: 28M/308M
[INFO] ------------------------------------------------------------------------ 

maven-war-file-deploy-tomcat7

2.Tomcat 6 示例

这个例子向您展示了如何在 Tomcat 6 上打包和部署 WAR 文件。步骤与 Tomcat 7 相同,只是部署 url 和命令名不同。

2.1 Tomcat 身份验证

%TOMCAT6_PATH%/conf/tomcat-users.xml

 <?xml version='1.0' encoding='utf-8'?>
<tomcat-users>

	<role rolename="manager-gui"/>
	<role rolename="manager-script"/>
	<user username="admin" password="password" roles="manager-gui,manager-script" />

</tomcat-users> 

2.2 Maven 认证

%MAVEN_PATH%/conf/settings.xml

 <?xml version="1.0" encoding="UTF-8"?>
<settings ...>
	<servers>

		<server>
			<id>TomcatServer</id>
			<username>admin</username>
			<password>password</password>
		</server>

	</servers>
</settings> 

2.3 Tomcat6 Maven 插件

pom.xml

 <plugin>
		<groupId>org.apache.tomcat.maven</groupId>
		<artifactId>tomcat6-maven-plugin</artifactId>
		<version>2.2</version>
		<configuration>
			<url>http://localhost:8080/manager</url>
			<server>TomcatServer</server>
			<path>/mkyongWebApp</path>
		</configuration>
	</plugin> 

2.4 部署到 Tomcat

 mvn tomcat6:deploy 
mvn tomcat6:undeploy 
mvn tomcat6:redeploy 

例子

 > mvn tomcat6:deploy

...
[INFO] Deploying war to http://localhost:8080/mkyongWebApp
Uploading: http://localhost:8080/manager/deploy?path=%2FmkyongWebApp
Uploaded: http://localhost:8080/manager/deploy?path=%2FmkyongWebApp (13925 KB at 32995.5 KB/sec)

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 22.652 s
[INFO] Finished at: 2014-08-05T12:18:54+08:00
[INFO] Final Memory: 30M/308M
[INFO] ------------------------------------------------------------------------ 

参考

  1. Apache Tomcat 7 管理器应用指南
  2. Apache Tomcat 6 管理器应用指南
  3. Tomcat Maven 插件
  4. Tomcat Maven 插件–上下文目标

如何用 jQuery 检测复制、粘贴和剪切行为

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-detect-copy-paste-and-cut-behavior-with-jquery/

要检测复制、粘贴和剪切行为,只需要绑定相应的事件类型。

 $("#textA").bind('copy', function() {
    $('span').text('copy behaviour detected!')
});	
$("#textA").bind('paste', function() {
    $('span').text('paste behaviour detected!')
});	
$("#textA").bind('cut', function() {
    $('span').text('cut behaviour detected!')
}); 

如果您使用的是 jQuery 1.4x,它支持如下的多事件声明:

 $("#textA").bind({
	copy : function(){
		$('span').text('copy behaviour detected!');
	},
	paste : function(){
		$('span').text('paste behaviour detected!');
	},
	cut : function(){
		$('span').text('cut behaviour detected!');
	}
}); 

你自己试试

 <html>
<head>
<script type="text/javascript" src="jquery-1.4.2.min.js"></script>

<style type="text/css">
	span{
		color:blue;
	}
</style>

</head>
<body>
  <h1>jQuery copy, paste and cut example</h1>
  <form action="#">
  	<label>TextBox : </label>
	<input id="textA" type="text" size="50" 
          value="Copy, paste or cut message here" />
  </form>

  <span></span>

<script type="text/javascript">

$(document).ready(function() {

	$("#textA").bind({
		copy : function(){
			$('span').text('copy behaviour detected!');
		},
		paste : function(){
			$('span').text('paste behaviour detected!');
		},
		cut : function(){
			$('span').text('cut behaviour detected!');
		}
	});

});	
</script>
</body>
</html> 

http://web.archive.org/web/20190304001155if_/http://www.mkyong.com/wp-content/uploads/jQuery/jQuery-copy-paste-cut-textbox.html

Try Demojquery keyboard event (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190304001155/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

jQuery–如何禁用点击后的提交按钮

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jquery/how-to-disable-submit-button-after-clicked-with-jquery/

通常,用户喜欢在提交按钮上按几次以确保按钮被点击,这导致了双重表单提交问题。常见的解决方案是在用户点击提交按钮后禁用它。

1.启用/禁用提交按钮

1.1 要禁用一个提交按钮,你只需要给提交按钮添加一个disabled属性。

 $("#btnSubmit").attr("disabled", true); 

1.2 要启用禁用的按钮,请将disabled属性设置为 false,或者移除disabled属性。

 $('#btnSubmit').attr("disabled", false);	
or
$('#btnSubmit').removeAttr("disabled"); 

2.jQuery 完整示例

 <!DOCTYPE html>
<html lang="en">

<body>
<h1>jQuery - How to disabled submit button after clicked</h1>

<form id="formABC" action="#" method="POST">
    <input type="submit" id="btnSubmit" value="Submit"></input>
</form>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>

<input type="button" value="i am normal abc" id="btnTest"></input>

<script>
    $(document).ready(function () {

        $("#formABC").submit(function (e) {

            //stop submitting the form to see the disabled button effect
            e.preventDefault();

            //disable the submit button
            $("#btnSubmit").attr("disabled", true);

            //disable a normal button
            $("#btnTest").attr("disabled", true);

            return true;

        });
    });
</script>

</body>
</html> 

参考

  1. jQuery 提交 API

如何显示 Spring Boot 加载的所有 beans

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/spring-boot/how-to-display-all-beans-loaded-by-spring-boot/

在 Spring Boot,您可以使用appContext.getBeanDefinitionNames()来获取 Spring 容器装载的所有 beans。

1.CommandLineRunner 作为接口

Application.java

 package com.mkyong;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;

import java.util.Arrays;

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    private ApplicationContext appContext;

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {

        String[] beans = appContext.getBeanDefinitionNames();
        Arrays.sort(beans);
        for (String bean : beans) {
            System.out.println(bean);
        }

    }

} 

输出

Console.java

 application
customerRepository
customerRepositoryImpl
dataSource
dataSourceInitializedPublisher
dataSourceInitializer
dataSourceInitializerPostProcessor
emBeanDefinitionRegistrarPostProcessor
entityManagerFactory
entityManagerFactoryBuilder
hikariPoolDataSourceMetadataProvider
jdbcTemplate
jpaContext
//... 

2.作为 Bean 的 CommandLineRunner

只是打印加载的 beans 的方式不同。

Application.java

 package com.mkyong;

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;

import java.util.Arrays;

@SpringBootApplication
public class Application {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CommandLineRunner run(ApplicationContext appContext) {
        return args -> {

            String[] beans = appContext.getBeanDefinitionNames();
            Arrays.stream(beans).sorted().forEach(System.out::println);

        };
    }

} 

参考

  1. 命令行运行程序 JavaDoc
  2. Spring Boot 非 web 应用实例

application context spring boot

如何在 JSF 显示数据表的行号

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/jsf2/how-to-display-datatable-row-numbers-in-jsf/

JSF 数据表不包含任何显示当前选定行号的方法。但是,您可以使用javax . faces . model . data model类来破解它,该类有一个 getRowIndex() 方法来返回当前选择的行号。

JSF +数据模型

下面是一个 JSF 2.0 的例子,展示了如何使用 DataModel 返回当前选中的行号。

freestar.config.enabled_slots.push({ placementName: "mkyong_incontent_1", slotId: "mkyong_incontent_1" });

1.受管 Bean

一个名为“person”的托管 bean,并展示了如何使用 DataModel 来保存 person 对象的列表。

 package com.mkyong;

import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.model.ArrayDataModel;
import javax.faces.model.DataModel;

@ManagedBean(name="person")
@SessionScoped
public class PersonBean implements Serializable{

	private static final long serialVersionUID = 1L;

	private static final Person[] personList = new Person[]{

		new Person("Person", "A", 10),
		new Person("Person", "B", 20),
		new Person("Person", "C", 30),
		new Person("Person", "D", 40),
		new Person("Person", "E", 50)

	};

	/* To get the row numbers, use dataModel instead
	public Person[] getPersonList() {

		return personList;

	}
	*/

	private DataModel<Person> person = new ArrayDataModel<Person>(personList);

	public DataModel<Person> getPersonList() {

		return person;

	}

	public static class Person{

		String firstName;
		String lastName;
		int age;

		public Person(String firstName, String lastName, int age) {
			super();
			this.firstName = firstName;
			this.lastName = lastName;
			this.age = age;
		}

		//getter and setter methods 
	}
} 

2.JSF·佩奇

显示使用 DataModel " rowIndex "返回当前选定行号的 0 索引的 JSP 页面。

 <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html    
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      >
    <h:head>
    	<h:outputStylesheet library="css" name="table-style.css"  />
    </h:head>
    <h:body>

    	<h1>Display dataTable row numbers in JSF</h1>

    	   <h:dataTable value="#{person.personList}" var="p"
    		styleClass="person-table"
    		headerClass="person-table-header"
    		rowClasses="person-table-odd-row,person-table-even-row"
    	   >

		<h:column>

    			<!-- display currently selected row number -->
    			<f:facet name="header">No</f:facet>
    			#{person.personList.rowIndex + 1}

    		</h:column>

    		<h:column>

    			<f:facet name="header">First Name</f:facet>
    			#{p.firstName}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Last Name</f:facet>
    			#{p.lastName}

    		</h:column>

    		<h:column>

    			<f:facet name="header">Age</f:facet>
    			#{p.age}

    		</h:column>

    	   </h:dataTable>

    </h:body>
</html> 

3.演示

jsf2-dataTable-RowNumbers-Example

下载源代码

Download It – JSF-2-DataTable-RowNumbers-Example.zip (10KB)

参考

  1. JSF 数据模型 JavaDoc
  2. JSF 数组数据模型 JavaDoc

Tags : datatable jsf2freestar.config.enabled_slots.push({ placementName: "mkyong_leaderboard_btf", slotId: "mkyong_leaderboard_btf" });

如何显示 hibernate sql 参数值–Log4j

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/how-to-display-hibernate-sql-parameter-values-log4j/

问题

Hibernate 具有基本的日志记录特性,可以显示带有 show_sql 配置属性的 SQL 生成语句。

 Hibernate: INSERT INTO mkyong.stock_transaction (CHANGE, CLOSE, DATE, OPEN, STOCK_ID, VOLUME) 
VALUES (?, ?, ?, ?, ?, ?) 

然而,这对于调试来说还不够,Hibernate SQL 参数值丢失了。

解决方案–Log4j

显示真实的 Hibernate SQL 参数值需要 Log4J。

1.在休眠模式下配置 Log4j

按照本文来在 Hibernate 中配置 Log4j

2.更改日志级别

修改 Log4j 属性文件,在“Log4j . logger . org . hibernate . type”属性中将日志级别改为“调试”或“跟踪”。

文件:log4j.properties

 # Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

# Root logger option
log4j.rootLogger=INFO, stdout

# Hibernate logging options (INFO only shows startup messages)
log4j.logger.org.hibernate=INFO

# Log JDBC bind parameter runtime arguments
log4j.logger.org.hibernate.type=trace 

3.完成的

现在显示休眠真实参数值

输出…

 Hibernate: INSERT INTO mkyong.stock_transaction (CHANGE, CLOSE, DATE, OPEN, STOCK_ID, VOLUME) 
VALUES (?, ?, ?, ?, ?, ?)
13:33:07,253 DEBUG FloatType:133 - binding '10.0' to parameter: 1
13:33:07,253 DEBUG FloatType:133 - binding '1.1' to parameter: 2
13:33:07,253 DEBUG DateType:133 - binding '30 December 2009' to parameter: 3
13:33:07,269 DEBUG FloatType:133 - binding '1.2' to parameter: 4
13:33:07,269 DEBUG IntegerType:133 - binding '11' to parameter: 5
13:33:07,269 DEBUG LongType:133 - binding '1000000' to parameter: 6 

Note
If this logging is still not detail enough for you to debug the SQL problem, you can use the P6Spy library to log the exact SQL statement that send to database. Check this article – How to display hibernate sql parameter values with P6Spy

如何显示 hibernate sql 参数值–P6Spy

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/how-to-display-hibernate-sql-parameter-values-solution/

问题

有很多开发人员询问关于 Hibernate SQL 参数值的问题。如何显示传递给数据库的 Hibernate SQL 参数值?Hibernate 只是将所有参数值显示为问号(?).利用 show_sql 属性,Hibernate 将显示所有生成的 sql 语句,但不显示 SQL 参数值。

例如

 Hibernate: insert into mkyong.stock_transaction (CHANGE, CLOSE, DATE, OPEN, STOCK_ID, VOLUME) 
values (?, ?, ?, ?, ?, ?) 

有办法记录或显示准确的 Hibernate SQL 参数值吗?

解决方案–P6Spy

嗯,有问题就有答案~

P6Spy 是一个有用的库,可以在将所有 SQL 语句和参数值发送到数据库之前记录它们。P6Spy 是免费的,它用于拦截和记录所有数据库 SQL 语句到一个日志文件中,它适用于任何使用 JDBC 驱动程序的应用程序。

1.下载 P6Spy 库

获取“ p6spy-install.jar ”,您可以从

  1. P6Spy 官网
  2. 在 Sourceforge.net 的间谍活动

2.提取它

提取 p6spy-install.jar 文件,查找 p6spy.jarspy.properties

3.添加库依赖项

p6spy.jar 添加到您的项目库依赖项中

4.修改 P6Spy 属性文件

修改您的数据库配置文件。您需要将您现有的 JDBC 驱动程序替换为 P6Spy JDBC 驱动程序-“T0”

原文是 MySQL JDBC 驱动——“com . MySQL . JDBC . driver”

 <session-factory>
  <property name="hibernate.bytecode.use_reflection_optimizer">false</property>
  <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
  <property name="hibernate.connection.password">password</property>
  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyong</property>
  <property name="hibernate.connection.username">root</property>
  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="show_sql">true</property>
</session-factory> 

改为 P6Spy JDBC 驱动——“com . P6Spy . engine . spy . p6spydriver”

 <session-factory>
  <property name="hibernate.bytecode.use_reflection_optimizer">false</property>
  <property name="hibernate.connection.driver_class">com.p6spy.engine.spy.P6SpyDriver
  </property>
  <property name="hibernate.connection.password">password</property>
  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/mkyong</property>
  <property name="hibernate.connection.username">root</property>
  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="show_sql">true</property>
</session-factory> 

5.修改 P6Spy 属性文件

修改 P6Spy 属性文件-"spy . properties"

用现有的 MySQL JDBC 驱动程序替换“真正的驱动程序”

 realdriver=com.mysql.jdbc.Driver

#specifies another driver to use
realdriver2=
#specifies a third driver to use
realdriver3= 

更改日志文件位置
logfile 属性中更改日志文件位置,所有 SQL 语句都会记录到这个文件中。

窗户

 logfile     = c:/spy.log 

*nix

 logfile     = /srv/log/spy.log 

6.将“spy.properties”复制到项目类路径中

将“spy.properties”复制到您的项目根文件夹中,确保您的项目可以找到“spy.properties”,否则将提示“spy.properties”文件未找到异常。

7.完成的

运行您的应用程序并执行一些数据库事务,您会注意到从应用程序发送到数据库的所有 SQL 语句都将被记录到您在“spy.properties”中指定的文件中。

示例日志文件如下。

 insert into mkyong.stock_transaction (CHANGE, CLOSE, DATE, OPEN, STOCK_ID, VOLUME) 
values (?, ?, ?, ?, ?, ?)|
insert into mkyong.stock_transaction (CHANGE, CLOSE, DATE, OPEN, STOCK_ID, VOLUME) 
values (10.0, 1.1, '2009-12-30', 1.2, 11, 1000000) 

结论

坦率地说,P6Spy 在减少开发人员的调试时间方面非常有用。只要您的项目使用 JDBC 驱动程序进行连接,P6Sqp 就可以进入它并为您记录所有 SQL 语句和参数值。

对于 Maven 用户

您可以使用 Maven 将 P6Spy 依赖项下载到您的pom.xml

 <dependency>
		<groupId>p6spy</groupId>
		<artifactId>p6spy</artifactId>
		<version>1.3</version>
	</dependency> 

然而,“spy.properties”文件不在包中,你必须自己创建它。你可以在这里下载模板-spy . properties

参考

  1. P6Spy 配置

Tags : hibernate parameter

相关文章

在 Wicket 中向 HTML 标签动态添加属性

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/wicket/how-to-dynamic-add-attribute-to-a-html-tag-in-wicket/

在 Wicket 中,您可以轻松地访问或操作 HTML 标记。比方说,您有一个 HTML 文本框组件,由一个 div 标记包装,如果文本框验证失败,div 标记应该以错误颜色突出显示。

在上面的例子中,你可以实现" AbstractBehavior "类,来动态地给 HTML 标签添加属性。请参见以下示例:

原始 HTML


    Hello ~ Wicket leaning curve is high, do you?

用 Wicket AbstractBehavior 修改

 WebMarkupContainerWithAssociatedMarkup divtest = 
        new WebMarkupContainerWithAssociatedMarkup("wicket_id_test");

    //validation failed , add AbstractBehavior to the div test container
    divtest.add(new AbstractBehavior() {

	    public void onComponentTag(Component component, ComponentTag tag) {
			tag.put("style", "background-color:red");
	    }
	}); 

结果如下:


    Hello ~ this is testing for adding attribute into above tag in Wicket ~

wicket (function (i,d,s,o,m,r,c,l,w,q,y,h,g) { var e=d.getElementById(r);if(e=null){ var t = d.createElement(o); t.src = g; t.id = r; t.setAttribute(m, s);t.async = 1;var n=d.getElementsByTagName(o)[0];n.parentNode.insertBefore(t, n); var dt=new Date().getTime(); try{i[l]w+y;}catch(er){i[h]=dt;} } else if(typeof i[c]!'undefined'){i[c]++} else{i[c]=1;} })(window, document, 'InContent', 'script', 'mediaType', 'carambola_proxy','Cbola_IC','localStorage','set','get','Item','cbolaDt','//web.archive.org/web/20190304031820/http://route.carambo.la/inimage/getlayer?pid=myky82&did=112239&wid=0')

如何在 Hibernate 查询中嵌入 Oracle 提示

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/hibernate/how-to-embed-oracle-hints-in-hibernate-query/

使用 Oracle 提示,您可以改变 Oracle 执行计划,以影响 Oracle 从数据库中检索数据的方式。请点击此处了解有关 Oracle 优化器提示的更多详细信息。

在 Hibernate 中,有可能将 Oracle 提示嵌入 Hibernate 查询中吗?

Hibernate setComment()?

能否用 Hibernate 自定义注释" setComment() "函数将 Oracle 提示嵌入到 HQL 中?让我们来看一个例子

1.原始 Hibernate 查询

这是一个简单的选择 HQL 检索股票代码的股票。

 String hql = "from Stock s where s.stockCode = :stockCode";
List result = session.createQuery(hql)
.setString("stockCode", "7277")
.list(); 

输出

 Hibernate: 
    select
        stock0_.STOCK_ID as STOCK1_0_,
        stock0_.STOCK_CODE as STOCK2_0_,
        stock0_.STOCK_NAME as STOCK3_0_ 
    from mkyong.stock stock0_ 
    where stock0_.STOCK_CODE=? 

2.尝试 Hibernate setComment()

在 hibernate 的配置文件(hibernate.cfg.xml)中启用Hibernate . use _ SQL _ comments,以便将自定义注释输出到日志文件或控制台。

 <!-- hibernate.cfg.xml -->
<?xml version="1.0" encoding="utf-8"?>
...
<hibernate-configuration>
 <session-factory>
    ...
    <property name="show_sql">true</property>
    <property name="format_sql">true</property>
    <property name="use_sql_comments">true</property>
    <mapping class="com.mkyong.common.Stock" />
  </session-factory>
</hibernate-configuration> 

使用 Hibernate setComment() 向查询中插入自定义注释。

 String hql = "from Stock s where s.stockCode = :stockCode";
List result = session.createQuery(hql)
.setString("stockCode", "7277")
.setComment("+ INDEX(stock idx_stock_code)")
.list(); 

输出

 Hibernate: 
    /* + INDEX(stock idx_stock_code) */ select
        stock0_.STOCK_ID as STOCK1_0_,
        stock0_.STOCK_CODE as STOCK2_0_,
        stock0_.STOCK_NAME as STOCK3_0_ 
    from mkyong.stock stock0_ 
    where stock0_.STOCK_CODE=? 

3.这是工作吗?

事实并非如此,Hibernate 自定义注释有两个问题。

1.Oracle 提示必须追加在“select”之后,而不是之前。

Hibernate 生成的查询

 /* + INDEX(stock idx_stock_code) */ select 

正确的做法应该是…

 select  /*+ INDEX(stock idx_stock_code) */ 

2.Hibernate 会自动在“/* +”之间添加一个额外的空格。

在 Hibernate 中,仍然没有官方方法将 Oracle 提示嵌入到 Hibernate 查询语言(HQL)中。

附注:感谢皮特对此的贡献。

工作液

唯一的解决方案是使用 Hibernate createSQLQuery 方法来执行原生 SQL 语句。

 String hql = "/*+ INDEX(stock idx_stock_code) */ 
    select * from stock s where s.stock_code = :stockCode";
List result = session.createQuery(hql)
.setString("stockCode", "7277")
.list(); 

输出

 Hibernate: 
    /*+ INDEX(stock idx_stock_code) */ select * 
    from stock s where s.stock_code = ? 

更多原生 SQL 查询示例

gson——如何实现漂亮的 JSON 输出

原文:http://web.archive.org/web/20230101150211/https://www.mkyong.com/java/how-to-enable-pretty-print-json-output-gson/

在本教程中,我们将向您展示如何在 Gson 框架中启用 JSON pretty print。

1.默认情况下,Gson 压缩打印 JSON 输出:

GsonExample1.java

 package com.mkyong;

import com.google.gson.Gson;

public class GsonExample1 {

    public static void main(String[] args) {

        Gson gson = new Gson();

        String[] lang = {"Java", "Node", "Kotlin", "JavaScript"};

        String json = gson.toJson(lang);

        System.out.println(json);

    }

} 

输出

 ["Java","Node","Kotlin","JavaScript"] 

2.要启用 JSON 漂亮打印,用GsonBuilder创建Gson对象

GsonExample2.java

 package com.mkyong;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class GsonExample2 {

    public static void main(String[] args) {

        Gson gson = new GsonBuilder().setPrettyPrinting().create();

        String[] lang = {"Java", "Node", "Kotlin", "JavaScript"};

        String json = gson.toJson(lang);

        System.out.println(json);

    }

} 

输出

 [
  "Java",
  "Node",
  "Kotlin",
  "JavaScript"
] 

Note
Read more Gson examples

参考

Jackson——如何实现漂亮的 JSON 输出

原文:http://web.archive.org/web/20230101150211/https://www.mkyong.com/java/how-to-enable-pretty-print-json-output-jackson/

在 Jackson 中,我们可以使用writerWithDefaultPrettyPrinter()来打印 JSON 输出。

用杰克逊 2.9.8 测试

1.漂亮的打印 JSON

1.1 默认情况下,Jackson 以紧凑格式打印:

 ObjectMapper mapper = new ObjectMapper();
	Staff staff = createStaff();
	String json = mapper.writeValueAsString(staff);
	System.out.println(json); 

输出

 {"name":"mkyong","age":38,"skills":["java","python","node","kotlin"]} 

1.2 实现按需打印。

 ObjectMapper mapper = new ObjectMapper();
	Staff staff = createStaff();
	// pretty print
	String json = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(staff);
	System.out.println(json); 

输出

 {
  "name" : "mkyong",
  "age" : 38,
  "skills" : [ "java", "python", "node", "kotlin" ]
} 

1.3 全球启用 pretty print。

 import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

	// pretty print
	ObjectMapper mapper = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
	Staff staff = createStaff();
	String json = mapper.writeValueAsString(staff);
	System.out.println(json); 

输出

 {
  "name" : "mkyong",
  "age" : 38,
  "skills" : [ "java", "python", "node", "kotlin" ]
} 

Note
To display the pretty print JSON output on an HTML page, wraps it with pre tags.
<pre>${pretty-print-json-output}</pre>Note – 12/12/2013
The article is updated to use writerWithDefaultPrettyPrinter(), the old defaultPrettyPrintingWriter() is deprecated.

参考

如何在 Maven 中启用代理设置

原文:http://web.archive.org/web/20230101150211/https://www.mkyong.com/maven/how-to-enable-proxy-setting-in-maven/

要在 Maven 中启用代理访问,请在{MAVEN_HOME}/conf/settings.xml中定义代理服务器细节

Note
There is a high chance your company is set up an HTTP proxy server to stop user connecting to the Internet directly. If you are behind a proxy, Maven will fail to download the project dependencies.

PS 用 Maven 3.6 测试过

1.代理访问

1.打开 Maven settings.xml,找到proxies标签:

{MAVEN_HOME}/conf/settings.xml

 <!-- proxies
   | This is a list of proxies which can be used on this machine to connect to the network.
   | Unless otherwise specified (by system property or command-line switch), the first proxy
   | specification in this list marked as active will be used.
   |-->
  <proxies>
    <!-- proxy
     | Specification for one proxy, to be used in connecting to the network.
     |
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>proxyuser</username>
      <password>proxypass</password>
      <host>proxy.host.net</host>
      <port>80</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
    -->
  </proxies> 

1.2 定义了如下的代理服务器设置:

 <proxies>
    <!-- proxy
     | Specification for one proxy, to be used in connecting to the network.
     |
    <proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>proxyuser</username>
      <password>proxypass</password>
      <host>proxy.host.net</host>
      <port>80</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>
    -->

	<proxy>
      <id>optional</id>
      <active>true</active>
      <protocol>http</protocol>
      <username>mkyong</username>
      <password>password</password>
      <host>proxy.mkyong.com</host>
      <port>8888</port>
      <nonProxyHosts>local.net|some.host.com</nonProxyHosts>
    </proxy>

  </proxies> 

1.3 完成后,Apache Maven 应该能够通过代理服务器连接到互联网。

参考

  1. 配置代理

如何用正则表达式提取 HTML 链接

原文:http://web.archive.org/web/20230101150211/http://www.mkyong.com/regular-expressions/how-to-extract-html-links-with-regular-expression/

在本教程中,我们将向您展示如何从 HTML 页面中提取超链接。例如,从以下内容中获取链接:

 this is text1 <a href='mkyong.com' target='_blank'>hello</a> this is text2... 
  1. 首先从a标签获取“值”——结果:a href='mkyong.com' target='_blank'
  2. 稍后从上面提取的值中获取“链接”——结果:mkyong.com

1.正则表达式模式

提取标签正则表达式模式

 (?i)<a([^>]+)>(.+?)</a> 

从标签正则表达式模式中提取链接

 \s*(?i)href\s*=\s*(\"([^"]*\")|'[^']*'|([^'">\s]+)); 

描述

 (		#start of group #1
 ?i		#  all checking are case insensive
)		#end of group #1
<a              #start with "<a"
  (		#  start of group #2
    [^>]+	#     anything except (">"), at least one character
   )		#  end of group #2
  >		#     follow by ">"
    (.+?)	#	match anything 
         </a>	#	  end with "</a> 
 \s*			   #can start with whitespace
  (?i)			   # all checking are case insensive
     href		   #  follow by "href" word
        \s*=\s*		   #   allows spaces on either side of the equal sign,
              (		   #    start of group #1
               "([^"]*")   #      allow string with double quotes enclosed - "string"
               |	   #	  ..or
               '[^']*'	   #        allow string with single quotes enclosed - 'string'
               |           #	  ..or
               ([^'">]+)   #      can't contains one single quotes, double quotes ">"
	      )		   #    end of group #1 

2.Java 链接提取器示例

下面是一个简单的 Java 链接提取器示例,从第一个模式中提取a标记值,并使用第二个模式从第一个模式中提取链接。

HTMLLinkExtractor.java

 package com.mkyong.crawler.core;

import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HTMLLinkExtractor {

	private Pattern patternTag, patternLink;
	private Matcher matcherTag, matcherLink;

	private static final String HTML_A_TAG_PATTERN = "(?i)<a([^>]+)>(.+?)</a>";
	private static final String HTML_A_HREF_TAG_PATTERN = 
		"\\s*(?i)href\\s*=\\s*(\"([^\"]*\")|'[^']*'|([^'\">\\s]+))";

	public HTMLLinkExtractor() {
		patternTag = Pattern.compile(HTML_A_TAG_PATTERN);
		patternLink = Pattern.compile(HTML_A_HREF_TAG_PATTERN);
	}

	/**
	 * Validate html with regular expression
	 * 
	 * @param html
	 *            html content for validation
	 * @return Vector links and link text
	 */
	public Vector<HtmlLink> grabHTMLLinks(final String html) {

		Vector<HtmlLink> result = new Vector<HtmlLink>();

		matcherTag = patternTag.matcher(html);

		while (matcherTag.find()) {

			String href = matcherTag.group(1); // href
			String linkText = matcherTag.group(2); // link text

			matcherLink = patternLink.matcher(href);

			while (matcherLink.find()) {

				String link = matcherLink.group(1); // link
				HtmlLink obj = new HtmlLink();
				obj.setLink(link);
				obj.setLinkText(linkText);

				result.add(obj);

			}

		}

		return result;

	}

	class HtmlLink {

		String link;
		String linkText;

		HtmlLink(){};

		@Override
		public String toString() {
			return new StringBuffer("Link : ").append(this.link)
			.append(" Link Text : ").append(this.linkText).toString();
		}

		public String getLink() {
			return link;
		}

		public void setLink(String link) {
			this.link = replaceInvalidChar(link);
		}

		public String getLinkText() {
			return linkText;
		}

		public void setLinkText(String linkText) {
			this.linkText = linkText;
		}

		private String replaceInvalidChar(String link){
			link = link.replaceAll("'", "");
			link = link.replaceAll("\"", "");
			return link;
		}

	}
} 

3.单元测试

用 TestNG 进行单元测试。通过@DataProvider模拟 HTML 内容。

TestHTMLLinkExtractor.java

 package com.mkyong.crawler.core;

import java.util.Vector;

import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import com.mkyong.crawler.core.HTMLLinkExtractor.HtmlLink;

/**
 * HTML link extrator Testing
 * 
 * @author mkyong
 * 
 */
public class TestHTMLLinkExtractor {

	private HTMLLinkExtractor htmlLinkExtractor;
	String TEST_LINK = "http://www.google.com";

	@BeforeClass
	public void initData() {
		htmlLinkExtractor = new HTMLLinkExtractor();
	}

	@DataProvider
	public Object[][] HTMLContentProvider() {
	  return new Object[][] {
	    new Object[] { "abc hahaha <a href='" + TEST_LINK + "'>google</a>" },
	    new Object[] { "abc hahaha <a HREF='" + TEST_LINK + "'>google</a>" },

	    new Object[] { "abc hahaha <A HREF='" + TEST_LINK + "'>google</A> , "
		+ "abc hahaha <A HREF='" + TEST_LINK + "' target='_blank'>google</A>" },

	    new Object[] { "abc hahaha <A HREF='" + TEST_LINK + "' target='_blank'>google</A>" },
	    new Object[] { "abc hahaha <A target='_blank' HREF='" + TEST_LINK + "'>google</A>" },
	    new Object[] { "abc hahaha <A target='_blank' HREF=\"" + TEST_LINK + "\">google</A>" },
	    new Object[] { "abc hahaha <a HREF=" + TEST_LINK + ">google</a>" }, };
	}

	@Test(dataProvider = "HTMLContentProvider")
	public void ValidHTMLLinkTest(String html) {

		Vector<HtmlLink> links = htmlLinkExtractor.grabHTMLLinks(html);

		//there must have something
		Assert.assertTrue(links.size() != 0);

		for (int i = 0; i < links.size(); i++) {
			HtmlLink htmlLinks = links.get(i);
			//System.out.println(htmlLinks);
			Assert.assertEquals(htmlLinks.getLink(), TEST_LINK);
		}

	}
} 

结果

 [TestNG] Running:
  /private/var/folders/w8/jxyz5pf51lz7nmqm_hv5z5br0000gn/T/testng-eclipse--530204890/testng-customsuite.xml

PASSED: ValidHTMLLinkTest("abc hahaha <a href='http://www.google.com'>google</a>")
PASSED: ValidHTMLLinkTest("abc hahaha <a HREF='http://www.google.com'>google</a>")
PASSED: ValidHTMLLinkTest("abc hahaha <A HREF='http://www.google.com'>google</A> , abc hahaha <A HREF='http://www.google.com' target='_blank'>google</A>")
PASSED: ValidHTMLLinkTest("abc hahaha <A HREF='http://www.google.com' target='_blank'>google</A>")
PASSED: ValidHTMLLinkTest("abc hahaha <A target='_blank' HREF='http://www.google.com'>google</A>")
PASSED: ValidHTMLLinkTest("abc hahaha <A target='_blank' HREF="http://www.google.com">google</A>")
PASSED: ValidHTMLLinkTest("abc hahaha <a HREF=http://www.google.com>google</a>") 

参考

  1. 测试文档
  2. 维基中的超链接

Tags : html regex

相关文章

如何在 Java 中查找文件扩展名为的文件

原文:http://web.archive.org/web/20230101150211/https://mkyong.com/java/how-to-find-files-with-certain-extension-only/

本文展示了如何用 Java 8 Files.walk遍历文件树和流操作filter来从文件夹及其子文件夹中查找与特定文件扩展名匹配的文件。

 // find files matched `png` file extension from folder C:\\test
  try (Stream<Path> walk = Files.walk(Paths.get("C:\\test"))) {
      result = walk
              .filter(p -> !Files.isDirectory(p))   // not a directory
              .map(p -> p.toString().toLowerCase()) // convert path to string
              .filter(f -> f.endsWith("png"))       // check end with
              .collect(Collectors.toList());        // collect all matched to a List
  } 

Files.walk方法中,第二个参数maxDepth定义了要访问的最大目录级别数。我们可以指定maxDepth = 1只从顶层文件夹中查找文件(排除它所有的子文件夹)

 try (Stream<Path> walk = Files.walk(Paths.get("C:\\test"), 1)) {
      //...
  } 

主题

  1. 查找指定文件扩展名的文件(Files.walk)
  2. 查找具有多个文件扩展名的文件(Files.walk)

1。查找具有指定文件扩展名

的文件

本示例查找与文件扩展名png匹配的文件。查找从顶层文件夹C:\\test开始,包括所有级别的子文件夹。

FindFileByExtension1.java

 package com.mkyong.io.howto;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FindFileByExtension1 {

    public static void main(String[] args) {

        try {

            List<String> files = findFiles(Paths.get("C:\\test"), "png");
            files.forEach(x -> System.out.println(x));

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static List<String> findFiles(Path path, String fileExtension)
        throws IOException {

        if (!Files.isDirectory(path)) {
            throw new IllegalArgumentException("Path must be a directory!");
        }

        List<String> result;

        try (Stream<Path> walk = Files.walk(path)) {
            result = walk
                    .filter(p -> !Files.isDirectory(p))
                    // this is a path, not string,
                    // this only test if path end with a certain path
                    //.filter(p -> p.endsWith(fileExtension))
                    // convert path to string first
                    .map(p -> p.toString().toLowerCase())
                    .filter(f -> f.endsWith(fileExtension))
                    .collect(Collectors.toList());
        }

        return result;
    }

} 

输出

Terminal

 c:\test\bk\logo-new.png
c:\test\bk\resize-default.png
c:\test\google.png
c:\test\test1\test2\java.png
... 

2。查找具有多个文件扩展名的文件

本示例查找与多个文件扩展名(.png.jpg.gif)匹配的文件。

FindFileByExtension2.java

 package com.mkyong.io.howto;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FindFileByExtension2 {

    public static void main(String[] args) {

        try {

            String[] extensions = {"png", "jpg", "gif"};
            List<String> files = findFiles(Paths.get("C:\\test"), extensions);
            files.forEach(x -> System.out.println(x));

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static List<String> findFiles(Path path, String[] fileExtensions) throws IOException {

        if (!Files.isDirectory(path)) {
            throw new IllegalArgumentException("Path must be a directory!");
        }

        List<String> result;
        try (Stream<Path> walk = Files.walk(path, 1)) {
            result = walk
                    .filter(p -> !Files.isDirectory(p))
                    // convert path to string
                    .map(p -> p.toString().toLowerCase())
                    .filter(f -> isEndWith(f, fileExtensions))
                    .collect(Collectors.toList());
        }
        return result;

    }

    private static boolean isEndWith(String file, String[] fileExtensions) {
        boolean result = false;
        for (String fileExtension : fileExtensions) {
            if (file.endsWith(fileExtension)) {
                result = true;
                break;
            }
        }
        return result;
    }

} 

输出

Terminal

 c:\test\bk\logo-new.png
c:\test\bk\resize-default.gif
c:\test\bk\resize-fast.gif
c:\test\bk\resize.png
c:\test\google.jpg
c:\test\google.png
c:\test\test1\test2\java.png
c:\test\test1\test2\java.jpg 

2.2 使用 Java 8 stream anyMatch可以缩短isEndWith()方法。

 private static boolean isEndWith(String file, String[] fileExtensions) {

    // Java 8, try this
    boolean result = Arrays.stream(fileExtensions).anyMatch(file::endsWith);
    return result;

    // old school style
    /*boolean result = false;
    for (String fileExtension : fileExtensions) {
        if (file.endsWith(fileExtension)) {
            result = true;
            break;
        }
    }
    return result;*/
} 

2.3 我们也可以去掉isEndWith()方法,直接将anyMatch放入filter中。

 public static List<String> findFiles(Path path, String[] fileExtensions)
      throws IOException {

      if (!Files.isDirectory(path)) {
          throw new IllegalArgumentException("Path must be a directory!");
      }

      List<String> result;
      try (Stream<Path> walk = Files.walk(path, 1)) {
          result = walk
                  .filter(p -> !Files.isDirectory(p))
                  // convert path to string
                  .map(p -> p.toString().toLowerCase())
                  //.filter(f -> isEndWith(f, fileExtensions))

                  // lambda
                  //.filter(f -> Arrays.stream(fileExtensions).anyMatch(ext -> f.endsWith(ext)))

                  // method reference
                  .filter(f -> Arrays.stream(fileExtensions).anyMatch(f::endsWith))     
                  .collect(Collectors.toList());
      }
      return result;

  } 

2.4 我们可以通过将不同的条件传递到流filter中来进一步增强程序;现在,该程序可以很容易地从文件夹中搜索或找到具有指定模式的文件。例如:

查找文件名以“abc”开头的文件。

 List<String> result;
  try (Stream<Path> walk = Files.walk(path)) {
      result = walk
              .filter(p -> !Files.isDirectory(p))
              // convert path to string
              .map(p -> p.toString())
              .filter(f -> f.startsWith("abc"))
              .collect(Collectors.toList());
  } 

查找文件名包含单词“mkyong”的文件。

 List<String> result;
  try (Stream<Path> walk = Files.walk(path)) {
      result = walk
              .filter(p -> !Files.isDirectory(p))
              // convert path to string
              .map(p -> p.toString())
              .filter(f -> f.contains("mkyong"))
              .collect(Collectors.toList());
  } 

下载源代码

$ git 克隆https://github.com/mkyong/core-java

$ cd java-io

参考文献

posted @ 2024-11-01 16:31  绝不原创的飞龙  阅读(2)  评论(0编辑  收藏  举报