Persisting many to many Relationship (JPA and Spring Strategy)

This post is inspired by the “Netbeans Generate Entity Class From Database” feature which literally spits out vanilla JPA Entity classes for you while you have some coffee. For many to many relationship types Netbeans reads and generates an JPA Entity class for the join table and an extra class which holds the Primary key values for the two tables involved in a many to many relationship. This class is annotated with the @Embeddable JPA annotation. This i found very cool.

In this post i would like to show how to persist data from the respective tables to the join table.

The database create statement for the chosen table goes as follows:

create table Author (author_id int(10) not null auto_increment, name varchar(55) not null, email varchar(55) not null, primary key (author_id));
create table Book (book_id int(10) not null auto_increment, name varchar(55) not null, publisher varchar(55) not null, primary key (book_id));
create table Author_Book (author_id int(10) not null, book_id int(10) not null, primary key (author_id, book_id));
alter table Author_Book add index FKAuthor_Boo670752 (author_id), add constraint FKAuthor_Boo670752 foreign key (author_id) references Author (author_id);
alter table Author_Book add index FKAuthor_Boo981679 (book_id), add constraint FKAuthor_Boo981679 foreign key (book_id) references Book (book_id);

Netbeans generates the following for you.. No skin pain :)

Class Author Entity

@Entity
@Table(name = "Author")
@NamedQueries({
    @NamedQuery(name = "Author.findAll", query = "SELECT a FROM Author a"),
    @NamedQuery(name = "Author.findByAuthorId", query = "SELECT a FROM Author a WHERE a.authorId = :authorId"),
    @NamedQuery(name = "Author.findByName", query = "SELECT a FROM Author a WHERE a.name = :name"),
    @NamedQuery(name = "Author.findByEmail", query = "SELECT a FROM Author a WHERE a.email = :email")})
public class Author implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "author_id")
    private Integer authorId;
    @Basic(optional = false)
    @Column(name = "name")
    private String name;
    @Basic(optional = false)
    @Column(name = "email")
    private String email;

    public Author() {
    }

    public Author(Integer authorId) {
        this.authorId = authorId;
    }

    public Author(Integer authorId, String name, String email) {
        this.authorId = authorId;
        this.name = name;
        this.email = email;
    }

    public Integer getAuthorId() {
        return authorId;
    }

    public void setAuthorId(Integer authorId) {
        this.authorId = authorId;
    }

    public String getName() {
        return name;
    }

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

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (authorId != null ? authorId.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Author)) {
            return false;
        }
        Author other = (Author) object;
        if ((this.authorId == null && other.authorId != null) || (this.authorId != null && !this.authorId.equals(other.authorId))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.testing.jsf.Author[authorId=" + authorId + "]";
    }

}

Class For Book Entity..

@Entity
@Table(name = "Book")
@NamedQueries({
    @NamedQuery(name = "Book.findAll", query = "SELECT b FROM Book b"),
    @NamedQuery(name = "Book.findByBookId", query = "SELECT b FROM Book b WHERE b.bookId = :bookId"),
    @NamedQuery(name = "Book.findByName", query = "SELECT b FROM Book b WHERE b.name = :name"),
    @NamedQuery(name = "Book.findByPublisher", query = "SELECT b FROM Book b WHERE b.publisher = :publisher")})
public class Book implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "book_id")
    private Integer bookId;
    @Basic(optional = false)
    @Column(name = "name")
    private String name;
    @Basic(optional = false)
    @Column(name = "publisher")
    private String publisher;

    public Book() {
    }

    public Book(Integer bookId) {
        this.bookId = bookId;
    }

    public Book(Integer bookId, String name, String publisher) {
        this.bookId = bookId;
        this.name = name;
        this.publisher = publisher;
    }

    public Integer getBookId() {
        return bookId;
    }

    public void setBookId(Integer bookId) {
        this.bookId = bookId;
    }

    public String getName() {
        return name;
    }

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

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(String publisher) {
        this.publisher = publisher;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (bookId != null ? bookId.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Book)) {
            return false;
        }
        Book other = (Book) object;
        if ((this.bookId == null && other.bookId != null) || (this.bookId != null && !this.bookId.equals(other.bookId))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.testing.jsf.Book[bookId=" + bookId + "]";
    }

}

Class for the join Table Author_Book

@Entity
@Table(name = "Author_Book")
@NamedQueries({
    @NamedQuery(name = "AuthorBook.findAll", query = "SELECT a FROM AuthorBook a"),
    @NamedQuery(name = "AuthorBook.findByAuthorId", query = "SELECT a FROM AuthorBook a WHERE a.authorBookPK.authorId = :authorId"),
    @NamedQuery(name = "AuthorBook.findByBookId", query = "SELECT a FROM AuthorBook a WHERE a.authorBookPK.bookId = :bookId")})
public class AuthorBook implements Serializable {
    private static final long serialVersionUID = 1L;
    @EmbeddedId
    protected AuthorBookPK authorBookPK;

    public AuthorBook() {
    }

    public AuthorBook(AuthorBookPK authorBookPK) {
        this.authorBookPK = authorBookPK;
    }

    public AuthorBook(int authorId, int bookId) {
        this.authorBookPK = new AuthorBookPK(authorId, bookId);
    }

    public AuthorBookPK getAuthorBookPK() {
        return authorBookPK;
    }

    public void setAuthorBookPK(AuthorBookPK authorBookPK) {
        this.authorBookPK = authorBookPK;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (authorBookPK != null ? authorBookPK.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof AuthorBook)) {
            return false;
        }
        AuthorBook other = (AuthorBook) object;
        if ((this.authorBookPK == null && other.authorBookPK != null) || (this.authorBookPK != null && !this.authorBookPK.equals(other.authorBookPK))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.testing.jsf.AuthorBook[authorBookPK=" + authorBookPK + "]";
    }

}

Below we have the class that holds the primary key values of the participating tables, also generated for you by netbeans:

@Embeddable
public class AuthorBookPK implements Serializable {
    @Basic(optional = false)
    @Column(name = "author_id")
    private int authorId;
    @Basic(optional = false)
    @Column(name = "book_id")
    private int bookId;

    public AuthorBookPK() {
    }

    public AuthorBookPK(int authorId, int bookId) {
        this.authorId = authorId;
        this.bookId = bookId;
    }

    public int getAuthorId() {
        return authorId;
    }

    public void setAuthorId(int authorId) {
        this.authorId = authorId;
    }

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (int) authorId;
        hash += (int) bookId;
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof AuthorBookPK)) {
            return false;
        }
        AuthorBookPK other = (AuthorBookPK) object;
        if (this.authorId != other.authorId) {
            return false;
        }
        if (this.bookId != other.bookId) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "com.testing.jsf.AuthorBookPK[authorId=" + authorId + ", bookId=" + bookId + "]";
    }

}

At this point in order to persist, we require a component, service and repository (DAO) objects to do this.
For the view component, what ever is your web framework of choice, be it jsf, spring mvc, gwt or wicket, etc all you need to do is inject the service and call the respective method for saving that you have defined in your service object. the below is based on JSF.

Note: at this point you need to persist both sides of the divide at the same time (the join table entity in this case AuthorBook and anyone of the participating sides). For the AuthorBook Entity you instantiate the constructor for the join table entity class that accepts the ids of the participating table (for this example this is achieved by passing to the constructor the get method for retrieving the ids for the participating tables for the join table.) Like this

authorBookService.save(new AuthorBook(book.getBookId(), author.getAuthorId())); 

The code for this goes as follows:

@Component
@Scope("request")
public class ComponentBean implements Serializable {

    private AuthorBook authorbook = new AuthorBook();
    private Author author = new Author();
    private Book book = new Book();
    private AuthorService authorSerivce;
    private AuthorBookService authorbookSerivce;

    @Autowired
    public ComponentBean(AuthorService authorSerivce, AuthorBookService authorbookSerivce) {
        this.authorSerivce = authorSerivce;
        this.authorbookSerivce = authorbookSerivce;
    }

    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }
public void setAuthorbook(AuthorBook authorbook) {
        this.authorbook = authorbook;
    }
 public AuthorBook getAuthorbook() {
 return authorbook;
 }

 public void setBook(Book book) {
 this.book = book;
 }
public Book getBook() {
        return book;
    }
public void save(ActionEvent actionEvent) {
 authorService.save(author);
 authorBookService.save(new AuthorBook(book.getBookId(), author.getAuthorId())); // you instantiate the constructor for the join table entity  class and pass it the get method for retrieving the ids for the participating tables for the join table

 }
}

Service interface to define the the save operation:

public interface AuthorBookService {

public void save(AuthorBook authorBook);
}

Next the implementation for the AuthorBookService Service Interface:


@Service("authorBookService")
public AuthorBookServiceImpl implements AuthorBookService  {
private AuthorBookDAO authorbookdao;
@Autowired
public ModelServiceImpl(AuthorBookDAO authorbookdao) {
        this. authorbookdao = authorbookdao;
    }
    @Transactional
    public void save(AuthorBook authorBook) {
        authorbookdao.persist(AuthorBook authorBook);
    }
}

The Data Access object interface:

public interface AuthorBookDAO {

public void persist(AuthorBook authorBook);
}

The Data Access object implementation proper:

@Repository
public class AuthorBookDAOImpl implements ModelDAO {

    @PersistenceContext
    protected EntityManager entityMgr;

    public EntityManager getEntityMgr() {
        return entityMgr;
    }

    public void setEntityMgr(EntityManager entityMgr) {
        this.entityMgr = entityMgr;
    }

    public void persist(AuthorBook authorBook) {

      this.entityMgr.persist(authorBook)

    }
}

Note: the service and repository for Author and Book will follow the same pattern. See the my previous blog post for more hints.. Ciao for now..

Populating JSF Combo Box with Database values. (or drop down list)…

I am presently working on my AISD project, and i am using jsf(primefaces) spring and jpa to build the system. i would like to commend the efforts of the brains behind jsf 2.0 and primefaces (1.* and 2.*) :) in particular for making the life of java web devs much easier. I remember back in the days the first versions of jsf were simply nightmares, which lead my cohorts and i  to move to frameworks like wicket :) .

This blog post seeks to show how to populate a jsf Combo box with values from a database table and also you would notice that the implementation for this case includes both jsf 1.* and jsf 2.*. I assume some familiarity with spring jpa and jsf.

Most relational database records have a primary key to uniquely identify it. This key appears on other tables as foreign keys. In my application i needed to display one or more of the attributes of a record from a table on a form and persist its unique identifier .

Below i provide some sample code that gives some hint on how this can be achieved using plain old java objects..

First lets paint the screen.. For JSF 1.*:


<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.prime.com.tr/ui">
<head>
<p:resources />
</head>
<body>
<h:form prependId="false" styleClass="cmxform">
<fieldset>
  <legend> </legend>
<p:panel id="panel" header="Combo Box Example">
<h:panelGrid columns="2"  columnClasses="label,value" styleClass="grid">
<h:outputLabel for="cb" value="comboItem" />
<h:selectOneMenu id="selectOneCb" value="#{pageBean.model.modelid}">
<f:selectItem itemLabel="Select Model" itemValue="" />
<f:selectItems value="#{pageBean.myModelValues}" />
</h:selectOneMenu>
</h:panelGrid>
</p:panel>
</fieldset>
</h:form>
</body>
</html>

For JSF 2.* this is even easier (thanks to Çağatay Çivici the primefaces lead for pointing this out to me :) ):


<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:p="http://primefaces.prime.com.tr/ui">
<head>
<p:resources />
</head>
<body>
<h:form prependId="false" styleClass="cmxform">
<fieldset>
  <legend> </legend>
<p:panel id="panel" header="Combo Box Example">
<h:panelGrid columns="2"  columnClasses="label,value" styleClass="grid">
<h:outputLabel for="cb" value="comboItem" />
<h:selectOneMenu id="selectOneCb" value="#{pageBean.model.modelid}">
<f:selectItem itemLabel="Select Model" itemValue="" />
<f:selectItems value="#{pageBean.mlist}" var="model" itemLabel="#{model.modelvalue}" itemValue="#{model.modelId}"/>
</h:selectOneMenu>
</h:panelGrid>
</p:panel>
</fieldset>
</h:form>
</body>
</html>

Next lets define the backing page bean… For JSF 1.* this is ok :

@Component("pageBean")
@Scope("request")
public class PageBean implements Serializable {
private model = new Model();
private ModelService modelService;
private Map<String, String> myModelValues = new HashMap<String, String>();
private List<Model> mList;
public PageBean(){
}

 @Autowired
public PageBean(ModelService modelService){
this. modelService = modelService;
        mList = modelService.findAll();
        for (Model m : mList) {
            myModelValues.put(m.getmyModelValue(), m.getmyModelId());
     }
}
public Map<String, String> getMyModelValues() {
return myModelValues;
    }
public void setMyModelValues(Map<String, String> myModelValues) {
this.myModelValues= myModelValues;
 }
public Model getModel() {
return model;
}
public void setModel(Model model) {
this.model = model;
}
}

For JSF 2.* it is even better => You don’t need  use a Map or Hash Map as the case may be above (thanks to Çağatay Çivici the primefaces lead for pointing this out ):

@Component("pageBean")
@Scope("request")
public class PageBean implements Serializable {
private model = new Model();
private ModelService modelService;
private List<Model> list;
public PageBean(){
}

@Autowired
public PageBean(ModelService modelService){
        this. modelService = modelService;
        list = modelService.findAll();
}

public List<Model> getList() {
	return list;
}
public Model getModel() {
return model;
}
public void setModel(Model model) {
this.model = model;
}
}

Below is a service interface to define the the findAll method:

public interface ModelService {

public List< Model> findAll();
}

Next the implementation for the Model Service Interface:


@Service("modelService")
public ModelServiceImpl implements ModelService  {
private ModelDAO modeldao;
@Autowired
public ModelServiceImpl(ModelDAO modeldao) {
        this. modeldao = modeldao;
    }
    @Transactional(readOnly = true)
    public List<Model> findAll() {
        return modeldao.findAll();
    }
}

The Data Access object interface:

public interface ModelDAO {

public List< Model> findAll();
}

The Data Access object implementation proper:

@Repository
public class ModelDAOImpl implements ModelDAO {

    @PersistenceContext
    protected EntityManager entityMgr;

    public EntityManager getEntityMgr() {
        return entityMgr;
    }

    public void setEntityMgr(EntityManager entityMgr) {
        this.entityMgr = entityMgr;
    }

    public List<Model> findAll() {
        Query query = entityMgr.createNamedQuery("Model.findAll");
        return query.getResultList();
    }
}

Finally lets define the jpa model object:

@Entity
@Table(name = "MODEL")
@NamedQuery(name = "Model.findAll", query = "SELECT m FROM Model m") // this query returns distinct values from the database.
public class Model implements Serializable {

    @Id
    @Basic(optional = false)
    @Column(name = "MODEL_ID")
    private String modelId;
    @Basic(optional = false)
    @Column(name = "MODEL_VALUE")
private String modelValue;
 public Model() {
    }
 public Model(String modelId, String modelValue) {
this.modelId = modelId;
this.model = modelValue;
}
 public String getModelId() {
        return modelId;
    }

    public void setModelId(String modelId) {
        this.modelId = modelId;
    }

    public String getModelValue() {
        return modelValue;
    }

    public void setModelValue(String modelValue) {
        this. modelValue = modelValue;
    }
    }

The code above steps through it all… Ciao for now..

Unix “PS” command

Today, i meet a senior colleague in the field of software named JOE WALNES.

While i was showing Joe what i had done earlier today with WICKET.

I was unable to run the my small WICKET app, because tomcat was still running, and i was unable to  shutdown the tomcat instance that i had earlier on started on my MAC Book.

He simply opened my eyes to the PS command on unix systems.

The PS command showes the unique id and related information of the currently running processes on the computer.

Since tomcat is java based and uses a bootstrap to run, he also exposed me to the “JPS” command.

This performs a similar function like the PS command, only selects processes that are java based.

This blog post i hope would be useful to someone out there and also serve as a reminder in case i need to come back to this in the future.

By issuing the PS command it generates a unique id for the system alone, so take note that the ID for processes vary from system to system, so when you apply the “kill” command remember to append the immediate ID of the process you want to shutdown.

London Wicket User Group Next meet-up on the 21st of November 2009.

Hi Everyone,

The London Wicket User Group has scheduled her next meet-up for 21st of November 2009 at “The Gallery, Foyles Bookshop, 113-119 Charing Cross Road, London, WC2H 0EB”. it is billed to start at 15:00.

Featuring talks from:

  • Jeremy Thomerson(USA): Custom JavaScript Integrations with Wicket + Auto Resolvers
  • Lionel Amrmanet(FR): WiQuery – Announcing 1.0, Introduction + Demo
  • Matej Knopp(SK): BRIX CMS + Wicket 1.5 Developments Q&A
  • Alastair Maw(UK): The Al Talk
  • And a trip to the Pub… for more talks and refreshments.

JWeekend are the organizers of this event and require you to sign-up here.

Cheers and hope to meet U there. Ciao for now.

Using the Wicket Framework to Update style tag.

In this post, Wicket Framework is used to update the embedded style tag.

An already working sample can be found here.

Let’s begin by configuring the web application (i.e. web.xml)


<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">

<display-name>CSS and Wicket</display-name>
<filter>
<filter-name>css_wicket</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>com.jw.ike.pages.CssApplication</param-value>
</init-param>
</filter>

<filter-mapping>
<filter-name>css_wicket</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>

 

The wicket application class go as follows:


public class CssApplication extends WebApplication {

public CssApplication() {
}

public Class getHomePage() {
return CssDemo.class;
}

}

The getHomePage() method returns this Page:

public class CssDemo extends WebPage implements Serializable {
	private String uStyle;

	public CssDemo() {
		add(new Label("style"));
		add(new CssForm("addform"));

	}

	public CssDemo(String userStyle) {
		this.uStyle = userStyle;
		this.add(new Label("style", new PropertyModel(this, "uStyle")));
add(new CssForm("addform"));

	}

}

And the accompanying HTML web page is:

<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="../css/style/style.css">
<title>Wicket Updating Style Sheet</title>
<style type="text/css" wicket:id="style"></style>
</head>
<body>

<div id="bmain">
<form wicket:id="addform">
<h2>Update Style Sheet...</h2>

<span>Select Pre-Defined CSS Rules </span>

<select wicket:id="combo">

</select>

<textarea id="ta" wicket:id="txta" rows="20" cols="60"> rich text </textarea>

<input id="btn" type="submit" value="::Post Style Sheet::"></form>
<a href="http://wicket.apache.org/" >
<img id="logo" alt="Wicket" src="../images/wicket-logo.png" border="0" width='111' height="155"/></a>

</div>

<div id="side">

<h1>Change Me @!#%.</h1>

 </div>
</body>
</html>

And then the Form Class:


public class CssForm extends Form {

private String style1 = "Choose a style";
private String style2 = "h1{ font-size: 4.0em; font-family: Mistral; color: #dc512b; border:10px solid #bb342d; background-color: #098762;text-align: center;}";
private String style3 = "h1{ font-size: 5.0em; font-family: Comic Sans MS; color: #0066CC; border:10px solid #330066; background-color: green;text-align: center;}";
private String style4 = "h1{ font-size: 6.0em; font-family: Curlz MT; color: #CCFFFF; border:10px solid #445698; background-color: #6666CC;text-align: center;}";

private static String css;
private List cssList;
private String selectedCss = style1;
private DropDownChoice dropDownMenu;
private TextArea textArea;

public CssForm(String componentid) {
super(componentid);

cssList = Arrays.asList(new String[]{style1, style2, style3, style4});

dropDownMenu = new DropDownChoice("combo", new PropertyModel(this, "selectedCss"),
     cssList) {

 @Override
 protected void onSelectionChanged(Object newSelection) {

     textArea.setModelObject(newSelection);

 }

 @Override
 protected boolean wantOnSelectionChangedNotifications() {

     return true;
 }

};
add(dropDownMenu);

textArea = new TextArea("txta", new PropertyModel(this, "css"));

add(textArea);

}

@Override
protected void onSubmit() {
String definedCssRule =  textArea.getModelObject().toString();
definedCssRule = "\n\n\n" + definedCssRule + "\n\n\n";
setResponsePage(new CssDemo(definedCssRule));
textArea.setModelObject(null);
}
}

The External Style Sheet :


body {
	background-image: url("../images/cl.jpg");
	background-repeat: no-repeat;
}

h1 {
	font-size: 4.5em;
	color: #4088b8;
	margin: -300px 0 0 0;
	position: relative;
	left: -20px;
	top: 10px;
	position: relative
}

h2 {
	color: #4088b8;
}

span {
	font-size: 1.0em;
	color: #4088b8;
	padding:0 33px 0 2px;
}

#logo {
	position: relative;
	left: 0px;
	top: 50px;
	z-index: 1;
}
select {

	position: relative;
	left: 0px;
	bottom: 2px;
	width: 230px;
}

#jw {
	position: relative;
	left: 900px;
	top: -50px;
	z-index: 1;
}

#bmain {
	width: 510px;
	margin: 10px 0 0 0;
	position: inherit;
}

#side {
	float: right;
	width: 600px;
	margin: -150px 0 0 0;
	padding: 1px 0 0 0;
}

#ta {
	padding: 0 20px 0 0;
}

#btn {
	background-color: #4088b8;
	border-color: 4088b8;
	color: white;
	position: relative;
	left: 180px;
	top: 50px;
}

 

Why i like Apache Wicket… ?

“Apache Wicket” is the simplest to use java front end framework available on the web.

For the following reasons:

- Wicket encourages total separation between plain Java code and HTML, with the help of meaningful abstractions. (i.e. allows the java code to handle all the logic)

- Wicket provides excellent support and management of server side state transparently.

- Very little XML configuration is required to get your web application going. this is not the case when compared to some other frameworks that are largely dependent on XML config kind of files.

- And finally, it does not have any special mark up that you would have to learn afresh (i.e. just HTML tags are marked with a simple and very unnoticeable “wicket:id” tag, and you are done).