Solutions to IT problems

Solutions I found when learning new IT stuff

Posts Tagged ‘Tutorial

Working with Swings JTable – An Example

leave a comment »

Introduction

In my previous article Fast random file access and line by line reading in Java I described how I created a random access file class that has a fast readLine() method. The reason for creating that class was, that I was implementing a random access reader for the chemistry file format sdf. Such files are ASCII text files and can contain tens of thousands of records. Each record consists of a variable amount of lines. For fast random access I wanted to index the offset where each record begins. With seek(offset) a requested record can then be accessed very quickly. To index the file it must be read line by line to search for the record separator $$$$. Therefore a fast readLine() method was crucial for performance.

After successfully implementing my reader for the sdf format, called sdf-reader, I wanted to create a GUI on top of it. I called it Free SDF Viewer. Records in sd-files contain a chemical structure and optionally associated data. Therefore sdf format is often used to exchange chemical databases and that’s also why it makes a lot of sense to use a table to visualize sd-files.

Requirements

Before diving in I created a short list of the requirements. sd-files can be huge and hence they should not have to be fully loaded into memory. If the user scrolls down in the table the rows above should not be kept in memory but the scrolling should be smooth meaning that some records should be cached.

The first column should display the chemical structure and that chemical structure must be resizable (height and width). This means that the columns width and the row height must be user adjustable. Because sd-files can contain thousands of records I also wanted to have a row header only containing the row number (1-based).

    • Usable with large files
    • smooth scrolling
    • user adjustable column width
    • user adjustable row height
    • row header containing the row number (1-based)

I had some additional requirement like a nice and easy to use file chooser but that is not related to working with a JTable. All in all I had the feeling that my list was reasonable and implementation should be easy. But boy, was I wrong. It turned out to be a very bumpy ride.

Accessing the data

My first problem was how to get the data from the file into the JTable. My sdf-reader returns an SdfRecord object that contains all the data. The access is index-based meaning the first record has index 0 and so forth. It turned out to be rather straight forward. A JTable gets the data from a TableModel. The solution is to create a custom TableModel implementation. The most important method of TableModel is getValueAt(int rowIndex, int columnIndex). In this method you define how the data is retrieved. The source can be anything. A naive implementation of this method for my case will look like this:

@Override
public Object getValueAt(int rowIndex, int columnIndex) {

	SdfRecord record = sdfReader.getRecord(rowIndex);

	if (columnIndex == 0) {
		//this column is always the chemical structure
		// display the chemical structure image
		String molfile = record.getMolfile();
		ImageIcon chemicalStructure = new ChemicalStructureIcon(molfile,indigo,renderer,imageWidth, imageHeight);
		return chemicalStructure;
	} else {
		// display data. currently everything is treated as String
		// inlucding numbers and dates
		String columnName = getColumnName(columnIndex);
		String value = record.getProperty(columnName);
		return value;
	}
}

This is naive because the underlying sd-file will be accessed several times for a single row while reading exactly the same data. In my actually implementation I’m caching 100 records around the current position. So if the user scrolls up or down the data will be read from the cache. If the users scrolls far enough some data will be evicted from the cache and new data loaded. This cache logic is omitted for simplicity. For the full source code see the Free SDF Viewers project page.

You might have noticed that in above code I’m creating an instance of ChemicalStructureIcon. I will discuss this in the next chapter.

Displaying the chemical structure

I thought that displaying an image in a JTable cell would be very straightforward and easy. However that was wrong. During my search I found that one should use the ImageIcon class because JTable can render that by default. This is not entirely true. You actually have to specifically tell JTable to render ImageIcon columns as image. This is done by a custom TableCellRenderer.

private class SdfTableCellRenderer extends DefaultTableCellRenderer {

	@Override
	public void setValue(Object value) {
		if (value instanceof ImageIcon) {
			setIcon((ImageIcon) value);
			setText("");
		} else {
			setIcon(null);
			super.setValue(value);
		}
	}
}

Note that I will later show you that I extended JTable to easily initialize all my added features. That is why this is a private inner class.

After being able to see images in table cells I realized that when changing the column width, the image is not automatically adjusted to be smaller or larger. Note that changing column width is supported by default. After another search session I realized that the only solution was to extend ImageIcon and I therefore created ChemicalStructureIcon. ChemicalStructureIcon uses the Indigo Chemistry Toolkit for rendering chemical structures. The most relevant code shown below is the paintIcon(Component c, Graphics g, int x, int y) method which is called when the image is drawn. If the column width changes, the image is re-rendered automatically.

@Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
	Image image = getImage();
	if (image == null) {
		return;
	}
	Insets insets = ((Container) c).getInsets();
	x = insets.left;
	y = insets.top;

	int w = c.getWidth() - x - insets.right;
	int h = c.getHeight() - y - insets.bottom;

	if (w != width || h != height) {
		if (w < 16 || h < 16) {
			// 16 pixels is minimum size supported by indigo
			return;
		}
		width = w;
		height = h;
		indigo.setOption("render-image-size", w, h);
		image = renderImage();
		setImage(image);
	}

	ImageObserver io = getImageObserver();
	g.drawImage(image, x, y, w, h, io == null ? c : io);
}

You can see the complete source code on the projects web page.

Adding a row header

JTable has no concept of a row header. But not all hope is lost because JScrollPane does and a JTable usually is inside a JScrollPane. In my solution I use an extra JTable as a row header. That table has a very simple, custom TableModel.

@Override
public Object getValueAt(int rowIndex, int columnIndex) {
	return rowIndex + 1;
}

Complete source code of RowHeaderModel

To actually use this I extended JTable, added the field headerTable.This row header is initialized in the constructor of this custom JTable implementation.

public SdfTable(JScrollPane scrollPane, SdfReader sdfReader, int rowHeight) {
	super();
	//snipped other initialization code
	headerModel = new RowHeaderModel(getRowCount());
	headerTable = new JTable(headerModel);
	headerTable.setRowHeight(getRowHeight());
	headerTable.setShowGrid(false);
	headerTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
        // 60 -> the width of the row header in px
	headerTable.setPreferredScrollableViewportSize(new Dimension(60, 0));
	headerTable.getColumnModel().getColumn(0).setPreferredWidth(60);
	headerTable.getColumnModel().getColumn(0).setCellRenderer(new RowHeaderCellRenderer());
	// synchronize selection by using the same selection model in both tables
	headerTable.setSelectionModel(this.getSelectionModel());
	scrollPane.setRowHeaderView(headerTable);
	setPreferredScrollableViewportSize(getPreferredSize());
}

There is a lot going on here. Also note that both tables use the same SelectionModel. This means that when the user clicks in the row header, that whole row will be selected and when the user clicks on a row, the row header will be selected too.

When the row height changes, the row header must have the same new height. Therefore my JTable implementation overrides the setRowHeight() methods.

@Override
public void setRowHeight(int rowHeight) {
	super.setRowHeight(rowHeight);
	if (headerTable != null) {
		headerTable.setRowHeight(rowHeight);
	}
}

@Override
public void setRowHeight(int row, int rowHeight) {
	super.setRowHeight(row, rowHeight);
	if (headerTable != null) {
		headerTable.setRowHeight(row, rowHeight);
	}
}

Complete source code of SdfTable

Change row height with mouse

One of the requirements was that the user can adjust the row height. This must be possible for individual rows and all rows at once. First we will look into adjusting a single rows height.

Change height of single row

To listen for mouse input we need to extend MouseInputAdapter. The idea is to show a resize cursor when the mouse it at the boundary of 2 rows.  When the user then presses the left button and drags the mouse, the upper row will be resized relatively to the distance traveled by the mouse. This requires us to use basic math and geometry knowledge.

private int getResizingRow(Point p) {
	return getResizingRow(p, table.rowAtPoint(p));
}

private int getResizingRow(Point p, int row) {
	if (row == -1) {
		return -1;
	}
	int col = table.columnAtPoint(p);
	if (col == -1) {
		return -1;
	}
	Rectangle r = table.getCellRect(row, col, true);
	r.grow(0, -3);
	if (r.contains(p)) {
		return -1;
	}

	int midPoint = r.y + r.height / 2;
	int rowIndex = (p.y < midPoint) ? row - 1 : row;

	return rowIndex;
}

@Override
public void mousePressed(MouseEvent e) {
	Point p = e.getPoint();
	resizingRow = getResizingRow(p);
	mouseYOffset = p.y - table.getRowHeight(resizingRow);
	if (resizingRow >= 0) {
		table.setRowSelectionAllowed(false);
		table.setAutoscrolls(false);
	}
}

The math is one thing, more problematic was that dragging the mouse could lead to weird behavior on screen with the 2 affected rows flickering as they are constantly being (de)-selected. Also if you drag the mouse upwards to the table header, the table begins to scroll. For these reasons these features are disabled while the resizing occurs.

The row height is then changed according to the distance covered (up or down, Y-Coordinate in Swing) by the mouse when dragging.

@Override
public void mouseDragged(MouseEvent e) {
	table.clearSelection();
	int mouseY = e.getY();

	if (resizingRow >= 0) {
		int newHeight = mouseY - mouseYOffset;
		if (newHeight > 0) {
			table.setRowHeight(resizingRow, newHeight);
		}
	}
}

Complete source code of TableRowResizer

Change row height for all rows

This could be solved easily with a prompt / input were the user types in a number. However that is not user friendly at all. The idea is that if the mouse is at the edge of the top row and the table header, a resize cursor should be shown and if the mouse is pressed and dragged, all rows height will be changed. This sounds identical to above solution, however it is not. The table header is an instance of JTableHeader. It has a different cursor than JTable. So depending on mouse location either the tables or the table headers cursor must be changed into a resize cursor.

private boolean isResizingHeader(MouseEvent e) {
	Point p = e.getPoint();

	Object source = e.getSource();
	JTableHeader header = table.getTableHeader();

	if (source instanceof JTableHeader) {

		int col = table.columnAtPoint(p);
		if (col == -1) {
			return false;
		}

		return ((header.getY() + header.getHeight()) - 5) < p.y;

	} else if (source instanceof JTable) {

		int topRow = getTopRow();
		int row = table.rowAtPoint(p);

		if (row == topRow) {
			int col = table.columnAtPoint(p);
			if (col == -1) {
				return false;
			}
			Rectangle r = table.getCellRect(row, col, true);
			r.grow(0, -5);
			return r.y > p.y;
		}
	}	
	return false;
}

and for changing the cursor

@Override
public void mouseMoved(MouseEvent e) {
	if (e.getSource() instanceof JTable) {
		if (isResizingHeader(e)
				!= (table.getCursor() == resizeCursor)) {
			swapTableCursor();
		}
	} else if (e.getSource() instanceof JTableHeader) {
		if (isResizingHeader(e)
				!= (table.getTableHeader().getCursor() == resizeCursor)) {
			swapHeaderCursor();
		}
	}
}

Another issue is when the top row is only partially visible this resizing should work too. Also when changing the row height of all rows, the total height of the table changes dramatically. Without taking precautions this leads to erratic auto scrolling to different records. To prevent that, the current top row is programmatically kept at the top of the viewport.

@Override
public void mouseDragged(MouseEvent e) {
	int mouseY = e.getYOnScreen();
	if (isResizing) {
		int newHeight = table.getRowHeight() + (mouseY - yOffset);
		if (newHeight > 0) {
			yOffset = e.getYOnScreen();
			table.setRowHeight(newHeight);
			JViewport viewport = (JViewport) table.getParent();
			JScrollPane scrollPane = (JScrollPane) viewport.getParent();
			// This rectangle is relative to the table where the
			// northwest corner of cell (0,0) is always (0,0).
			Rectangle rect = table.getCellRect(topRow, 0, true);
			scrollPane.getVerticalScrollBar().setValue(rect.y);
		}
	}
}

This code has one downside, that as soon as the user drags the mouse, the top row will jump down fully into view but else it works very nicely.

See complete source code of AllRowsResizer.

Putting it all together

To put all the features together I extended JTable in my custom class SdfTable. The full constructor for SdfTable can be seen below.

public SdfTable(JScrollPane scrollPane, SdfReader sdfReader, int rowHeight) {
	super();
	DefaultTableCellRenderer r = new SdfTableCellRenderer();
	setDefaultRenderer(Object.class, r);
	TableRowResizer rowResizer = new TableRowResizer(this);
	AllRowsResizer allRowsResizer = new AllRowsResizer(this);

	TableModel tableModel = new SdfTableModel(sdfReader);
	setModel(tableModel);
	super.setRowHeight(rowHeight);
	getColumnModel().getColumn(0).setPreferredWidth(STRUCTURE_COLUMN_WIDTH);

	headerModel = new RowHeaderModel(getRowCount());
	headerTable = new JTable(headerModel);
	headerTable.setRowHeight(getRowHeight());
	headerTable.setShowGrid(false);
	headerTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
	headerTable.setPreferredScrollableViewportSize(new Dimension(60, 0));
	headerTable.getColumnModel().getColumn(0).setPreferredWidth(60);
	headerTable.getColumnModel().getColumn(0).setCellRenderer(new RowHeaderCellRenderer());
	// synchronize selection by using the same selection model in both tables
	headerTable.setSelectionModel(this.getSelectionModel());
	scrollPane.setRowHeaderView(headerTable);
	setPreferredScrollableViewportSize(getPreferredSize());
}

The JScrollPane argument is required for correctly setting the row header.

Complete source code of SdfTable.

Additional Features and Comments

Free SDF Viewer has some additional features not directly related to JTable and there are other issues I encounter that apply to everything in Swing.

Showing a wait cursor when loading sd-file

I created a menu with a single entry “Load SD-files…”. Clicking on it will display a FileChooser and then load and initialize an SdfTable object. This means that the code runs under the so called Event Dispatch Thread (EDT). Changes to UI Elements done in this thread will not become visible until the action completes. This means that if you change the cursor to a wait cursor and then load the sd-file in the EDT, the user will never see the wait cursor. Changing the cursor must be done in a different thread but not just any thread. You are required to use a SwingWorker thread. The take-away message is, that simple things become unexpectedly complex.

First the code executed after clicking on the “Load SD-File…” menu option:

private void loadFileMenuItemActionPerformed(java.awt.event.ActionEvent evt) {

        int returnVal = fileChooser.showOpenDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fileChooser.getSelectedFile();
            logger.debug("Opening SD-File '{}'.", file.getAbsoluteFile());
            SdfLoader loader = new SdfLoader(this, file);
            loader.execute();
        } else {
            logger.debug("Opening of SD-file cancelled by the user.");
        }
    }

This creates and SdfLoader instance. SdfLoader extends SwingWorker. In the constructor it changes the cursor to a wait cursor, then in the doInBackground()-method the sd-file is loaded and finally in the done()-method the SdfTable is created and the cursor reverted back to its previous state.

private class SdfLoader extends SwingWorker<JTable, Void> {

	private final JFrame frame;
	private final File sdFile;
	private JTable table;
	private IOException ioException;

	public SdfLoader(JFrame frame, File sdFile) {
		this.frame = frame;
		frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		this.sdFile = sdFile;
	}

	@Override
	public JTable doInBackground() {
		try {
			//close old file
			if (sdfReader != null) {
				sdfReader.close();
			}
			sdfReader = new SdfReader(sdFile);
			lastOpenDir = sdFile.getParentFile();
			table = new SdfTable(jScrollPane1, sdfReader, 200);
		} catch (IOException ex) {
			logger.catching(ex);
			ioException = ex;
			table = jTable1;
		}
		return table;
	}

	@Override
	public void done() {
		if (ioException == null) {
			jTable1 = table;
			jScrollPane1.setViewportView(table);
			frame.setCursor(Cursor.getDefaultCursor());
		} else {
			JOptionPane.showMessageDialog(SdfViewer.this,
					ioException.getMessage(),
					"Error opening file",
					JOptionPane.ERROR_MESSAGE);
		}
	}
}

Remembering Settings

  One of them is remembering the last directory I sd-files was opened from. This information is written into a properties file and loaded at start-up. Future versions might make additonal use of this to store other settings like rendering options for the chemical structure. I’m mentioning this so that you are not confused by unexplained code in the main class SdfViewer.

Screenshots

First a screenshot showing a row with a different row height.

The second screen shot shows the SdfTableModel in action. The user currently is at row 139045 in a large sd-file and there are no performance issues on a standard laptop.

The full project Free SDF Viewer is available on bitbucket. There is also a download for an executable jar file in the projects downloads section.

Advertisements

Written by kienerj

January 17, 2014 at 10:00

Posted in Chemistry, Java, Programming

Tagged with , ,

Spring MVC 3 Tutorial for beginners

with one comment

Introduction

I was trying to create a very simple Web Application using Spring MVC as a usage example for MoleculeDatabaseFramework. Now Spring MVC is obviously overkill for this but it was a good change to learn it. However this endeavor turned out to be much more complicated than I had anticipated. Also it is very hard to find actual tutorials and information on how to use Spring MVC 3. A lot of stuff is from older versions and often it is not mentioned for which version a code or configuration snippet is.

Starting Point

I created a maven web application using netbeans IDE. I added MoleculeDatabaseFramework-1.0.0-SNAPSHOT as dependency. To use the same configuration as my integration test of MoleculeDatabaseFramework use, I had to add further dependencies as they are in the test-scope and not included in MoleculeDatabaseFramework-1.0.0. I also added dependencies spring-web and spring-webmvc, both are required!

Dependency Issues

MoleculeDatabaseFramework project has a dependency on spring-context-3.1.4. In the web application I chose spring-web-3.2.2 and spring-webmvc-3.2.2 which replaced spring-context-3.1.4 with spring-context-3.2.2. The problem is that spring-context-3.2.2 contains less packages than spring-context-3.1.4 and hence I kept getting an exception that EhCacheCacheManager class was missing. After wasting over an hour on this I figured out that since spring-3.2.x this is in a separate dependency spring-context-support-3.2.2. That solved this issue and here the according maven dependencies:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-web</artifactId>
	<version>3.2.2.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-webmvc</artifactId>
	<version>3.2.2.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
	<version>3.2.2.RELEASE</version>
</dependency>

Note: this is only required if you are using caching with ehcache in your application.

A second problem was that I was missing class javax.servlet.jsp.jstl.core.Config. This was weird because I could clearly see in netbeans that this was provided by javaee-web-api-6.0 which was added to the project automatically at creation from netbeans. However it is in the provided scope and hence not included in the war-file. To resolve this you need to manually add dependency

<dependency>
	<groupId>javax.servlet</groupId>
	<artifactId>jstl</artifactId>
	<version>1.2</version>
</dependency>

Putting it all togehter here all dependencies for the project:

    <dependencies>
        <dependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>MoleculeDatabaseFramework</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>0.11.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>3.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.6.5</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>4.2.1.Final</version>
        </dependency>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-web-api</artifactId>
            <version>6.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>com.jolbox</groupId>
            <artifactId>bonecp-spring</artifactId>
            <version>0.8.0-rc2-SNAPSHOT</version>
        </dependency>
    </dependencies>

Configuration issues

Configuration File Locations

web.xml Spring configuration file(s) go into the myApp/src/main/webapp/WEB-INF directory or in netbeans it is displayed in myApp/Web Pages/WEB-INF (but the actual path is the prior one). If you have a properties file, that is used by the spring context, like for database config, you need to put it in myApp/src/main/resources and it then can be found with the path classpath:Application.properties. The same is true for the ehcache.xml configuration file. if you use hibernates import.sql file to load data on application start, that belongs there too.

Configuration Files

I created 2 spring configuration files, ApplicationContext.xml and mvc-config.xml. The later is used to configure Spring MVC and ApplicationContext.xml is used to configure
MoleculeDatabaseFramework and requires this entry:

<import resource="mvc-config.xml" />

web.xml

You also need to create a web.xml file in WEB-INF. web.xml contains a reference to the DispatcherServlet that accepts all request:

<servlet>
	<servlet-name>myApp</servlet-name>
	<servlet-class>
		org.springframework.web.servlet.DispatcherServlet
	</servlet-class>
	<init-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>
			/WEB-INF/ApplicationContext.xml
		</param-value>
	</init-param>
	<load-on-startup>1</load-on-startup>
</servlet>

Note that if you do not specify the contextConfigLocation then the application context must be named -servlet.xml and hence in this case myApp-servlet.xml. load-on-startup must be 1 so that the Spring Context loads on application start.

You also need to specify which URLs you want to map:

<servlet-mapping>
	<servlet-name>MDFSimpleWebApp</servlet-name>
	<url-pattern>/*</url-pattern>
</servlet-mapping>

This means we want to map all request to myApp.

URL Mapping Issue

After the application actually loaded and my Controller was found and accessed correctly i kept getting an error that my views are not found. Even weirder was that the path to the view displayed in the log was correct!

The solution can be found in this stackoverflow question. However it is not the accepted answer but the one from sourcedelica. You need to add an additional mapping to your web.xml.

<servlet-mapping>
	<servlet-name>jsp</servlet-name>
	<url-pattern>/WEB-INF/jsp/*</url-pattern>
</servlet-mapping>

were the url-pattern is the path to the directory of all your web pages. This leads to following complete web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         id="WebApp_ID" version="2.5">
    <display-name>MDF Simple Web Application</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

    <servlet>
        <servlet-name>MDFSimpleWebApp</servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>
                /WEB-INF/ApplicationContext.xml
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>MDFSimpleWebApp</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>jsp</servlet-name>
        <url-pattern>/WEB-INF/jsp/*</url-pattern>
    </servlet-mapping>
</web-app>

mvc-config.xml

I chose an annotation based approach for controllers. But all in all the mvc configuration file did not pose any troubles:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
          http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">

    <mvc:annotation-driven />
    <mvc:view-controller path="/" view-name="index"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

</beans>

ApplicationContext.xml

This file is huge and configures MoleculeDatabaseframework inlcuding spring-data, hibernate, connection pooling and so forth. It is not really relevant to Spring-MVC and hence I’m intentionally omitting it to avoid any confusion. However it must contain at least

<import resource="mvc-config.xml" />

so that the mvc configuration is loaded.

Controller

I created 1 very simple controller and I’m just going to post the source code and explain id:

@Controller
@RequestMapping(value = "/compound")
public class SimpleCompoundController {

    @Autowired
    private SimpleCompoundService compoundService;

    @RequestMapping(value = "/{compoundId}", method = RequestMethod.GET)
    public String getSimpleCompound(@PathVariable Long compoundId, Model model) {
        SimpleCompound compound = compoundService.getById(compoundId);
        model.addAttribute("compound", compound);
        return "compound";
    }
}

@Controller marks the class as controller and hence spring can detect it using component scanning.

@RequestMapping(value = "/compound") on the class level tells this controller handles all request to that url. Note that it is relative to the url specified in the DispatcherServlet in web.xml. In this case this would mean myApp/compound.

@Autowired injects a service loaded in ApplicationContext.xml. This service loads data from the database.

@RequestMapping(value = "/{compoundId}", method = RequestMethod.GET) tells that the annotated method ist repsonsible for handling all request to myApp/compound/{compoundId}. So the value is relative to the mapping of the controller class (if specified). @PathVariable will then take {compoundId} from the URL and pass it into the method in which it is used to fetch the compound wit the given id form the database.

The the compound is added to the model and we return the name of the view to render. This return value is handled by InternalResourceViewResolver configured in mvc-config.xml. The resolver appends the file ending specified (.jsp) and adds the path to the views directory. The actual request will then be for myApp//WEB-INF/jsp/compound.jsp and that jsp page will be rendered.

View

The view is a very simple jsp page. It shows how you can then access the Model in your web page using expressions.

<%@page language="java" contentType="text/html" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib prefix="s" uri="http://www.springframework.org/tags" %>
<!DOCTYPE html>

<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Compound ${compound.getId()}</title>
    </head>
    <body>
        <div><label>Compound Name:</label><label>${compound.getCompoundName()}</label></div>
    </body>
</html>

Written by kienerj

May 24, 2013 at 12:10

Posted in Java, Programming

Tagged with ,

Spring 3 with JPA 2.0 (Hibernate) for beginners

leave a comment »

Introduction

This is a follow-up post to Spring 3 and Hibernate 4 for beginners.

After some considerations I decided to change my application to use JPA instead of “native Hibernate”. The configuration here assumes a standalone application, meaning outside of application container like Tomcat. However changing to JNDI and JTA is “only” a matter of configuration.

Changes

Entity Classes

If you used JPA annotations for your entity classes you don’t have to change anything. If you want to be fully independent of the JPA Implementation you will need to remove all Hibernate specific annotations which can be problematic in certain cases.

Abstract DAO

This remains almost the same. You need to exchange the sessionFactory with entityManager and thats it.

@Repository
public abstract class AbstractJpaDAO< T extends Serializable> {

    private Class< T> clazz;
    @PersistenceContext
    private EntityManager entityManager;

    public AbstractJpaDAO(final Class< T> clazzToSet) {
        this.clazz = clazzToSet;
    } 

    public T getById(final Long id) {
        Preconditions.checkArgument(id != null);
        return getEntityManager().find(clazz, id);
    }

    public List< T> getAll() {
        return getEntityManager().createQuery("from " + clazz.getName())
                .getResultList();
    }

    public void create(final T entity) {
        Preconditions.checkNotNull(entity);
        getEntityManager().persist(entity);
    }

    public T update(final T entity) {
        Preconditions.checkNotNull(entity);
        return (T) getEntityManager().merge(entity);
    }

    public void delete(final T entity) {
        Preconditions.checkNotNull(entity);
        getEntityManager().remove(entity);
    }

    public void deleteById(final Long entityId) {
        final T entity = getById(entityId);
        Preconditions.checkState(entity != null);
        delete(entity);
    }

    /**
     * @return the entityManager
     */
    public EntityManager getEntityManager() {
        return entityManager;
    }

    /**
     * @param entityManager the entityManager to set
     */
    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }
}

Configuration

First you will not need to create a persistence unit! You can but since spring 3.1 this is not required anymore. If you are using Netbeans and see the warning “The Project does not contain a persistence unit” you can safely ignore it.

Below the new Spring Application-Context file. The important changes are replacing session factory with entity manager factory and changing transaction manager to jpa transaction manager. The datasource and connection pool remains exactly the same.

I’ve also added Service classes to the configuration which I created after writing the previous post.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">      

    <context:component-scan base-package="org.bitbucket.kienerj.moleculedatabaseframework" />
    <context:annotation-config />    
    
    <bean id="entityManagerFactory" autowire="autodetect"
          class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!-- This scan the packages for entity classes an hence no need for persistence unit -->
        <property name="packagesToScan" value="org.bitbucket.kienerj.moleculedatabaseframework.entity" />
        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false" />
                <!-- if this is true it can override hibernate.hbm2ddl.auto settings -->
                <property name="generateDdl" value="false" />
                <property name="databasePlatform" value="org.hibernate.dialect.PostgreSQLDialect" />
            </bean>
        </property>
        <!-- put any ORM specific stuff here -->
        <property name="jpaProperties">
            <props>
                <!-- for test config only --> 
                <prop key="hibernate.hbm2ddl.auto">create-drop</prop> <
            </props>
        </property>
    </bean>
       
    
    <!-- Spring bean configuration. Tell Spring to bounce off BoneCP -->
    <bean id="dataSource"
          class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy">
        <property name="targetDataSource">
            <ref local="mainDataSource" />
        </property>
    </bean>
            
    <!-- BoneCP configuration -->
    <bean id="mainDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close">
        <property name="driverClass" value="org.postgresql.Driver" />
        <property name="jdbcUrl" value="jdbc:postgresql:MolDB" />
        <property name="username" value="postgres"/>
        <property name="password" value="123456"/>
        <property name="idleConnectionTestPeriod" value="60"/>
        <property name="idleMaxAge" value="240"/>      
        <property name="maxConnectionsPerPartition" value="60"/>
        <property name="minConnectionsPerPartition" value="20"/>
        <property name="partitionCount" value="3"/>
        <property name="acquireIncrement" value="10"/>                              
        <property name="statementsCacheSize" value="50"/>
        <property name="releaseHelperThreads" value="3"/>
    </bean>
    
    <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="entityManagerFactory" ref="entityManagerFactory" />
    </bean>
    <tx:annotation-driven transaction-manager="txManager" />   
    
    
    <bean id="chemicalCompoundService" 
          class="org.bitbucket.kienerj.moleculedatabaseframework.service.ChemicalCompoundServiceImpl"/>
    
    <bean id="chemicalStructureService" autowire="autodetect"
          class="org.bitbucket.kienerj.moleculedatabaseframework.service.ChemicalStructureServiceImpl"/>
    
    <bean id="abstractJpaDAO" abstract="true"
          class="org.bitbucket.kienerj.moleculedatabaseframework.dao.AbstractJpaDAO"/>
    
    <bean id="chemicalStructureDAO" parent="abstractJpaDAO" autowire="autodetect"
          class="org.bitbucket.kienerj.moleculedatabaseframework.dao.ChemicalStructureDAO"/>
    <bean id="chemicalCompoundDAO" parent="abstractJpaDAO" autowire="autodetect"
          class="org.bitbucket.kienerj.moleculedatabaseframework.dao.ChemicalCompoundDAO"/>
</beans>

Quering

Since last post I have created some methods in the DAOs and created a Service Layer. Anyway I had to change my Queries too. The most notable change is that in queries that return 1 result only you need to change uniqueResult() to getSingleResult() but much more important is, that getSingleResult() will throw a NoResultException in case no result was found. In my case this is expected to happen and a common scenario. In my case I simulate behaviour of uniqueResult() by catching the exception and returning null.

public ChemicalStructure getByStructureKey(String structureKey) {

	try {
		ChemicalStructure structure = getEntityManager()
				.createQuery(
					"FROM ChemicalStructure structure "
				  + "WHERE structure.structureKey = :structureKey", ChemicalStructure.class)
				.setParameter("structureKey", structureKey)
				.getSingleResult();
		return structure;
	} catch (NoResultException nre) {
		return null;
	}        
}

Keep in mind you will need to change the application and test config! I wasted a lot of time because I changed application config and then ran tests which failed…Beginners mistake. 🙂

Hope this was helpful.

Written by kienerj

October 16, 2012 at 13:53

Posted in Database, Java, Programming

Tagged with , , ,

Spring 3 and Hibernate 4 for beginners

with 6 comments

Introduction Comments

One would assume there are tons of tutorials and blog post out there about this topic. This is true. There are actually so many it is hard to find one that is up to date and fits your level of knowledge. Most of them assume you are already rather good in using Spring, Hibernate or both. So some very fundamental stuff is never mentioned and that stuff will make your application throw exceptions all over the place. I mainly had trouble with the configuration file and were to put what annotation.

Scope of this Article

This article covers my first steps in using Spring and Hibernate together to create a very basic Data Access Layer. I have never used Spring or Hibernate before in a real application. During my studies in a project I once made a simple app using JPA with TopLink. Some very basic knowledge is required like you should probably have seen an @Entity annotation before (else google it) but in general this is an article from a beginner for beginners. You will need to now basic relational database terms and design principles. The project uses maven and hence you should now some basics of it. Also some knowledge in chemistry will make it easier to understand the example but that is not required. Feel free to comment if you have improvements or suggestions to the design and code shown in this article.

The Problem

The DAL I created should be able to store “chemical compounds”. What is a “chemical compound”? It’s a mixture of substances and each substance occurs in a compound in a percentage. Just like whiskey is a mixture of 40 % ethanol and 60 % water (of course this is a simplified view but you get what I mean). Note that a “substance” has a specific “chemical structure”, eg. its known of which atoms it consists and how they are linked together. To summarize we can say a chemical compound consist multiple “chemical structures” (substances) and a “chemical structure” (substance) can occur in multiple compounds. This is a classic many-to-many relationship.

Hibernate and many-to-many with percentage

It turned out that there is no simple way to create a many-to-many relationship in hibernate where the “link table” has an additional column (the percentage of the substance in the compound). I followed this tutorial and therefore created the java classes as shown in the next section. As a summary you will need to create two one-to-many relationships and a linker class. But in practice it is even more complicated.

The Entity classes

ChemicalStructure

Nothing special here, eg. documentation and comments are sparse ;)..funny comments aside the important part is the @OneToMany annotation towards the bottom of the class and its “mappedBy = “pk.chemicalStructure” attribute.

<br />package org.bitbucket.kienerj.moleculedatabaseframework.entityclasses;<br /><br />import java.io.Serializable;<br />import java.util.Set;<br />import javax.persistence.CascadeType;<br />import javax.persistence.Column;<br />import javax.persistence.Entity;<br />import javax.persistence.FetchType;<br />import javax.persistence.GeneratedValue;<br />import javax.persistence.GenerationType;<br />import javax.persistence.Id;<br />import javax.persistence.OneToMany;<br />import javax.persistence.SequenceGenerator;<br />import javax.persistence.Table;<br /><br />@Entity<br />@Table(name = "chemical_structure")<br />public class ChemicalStructure implements Serializable {<br /><br /><%%KEEPWHITESPACE%%>    private Long id;<br /><%%KEEPWHITESPACE%%>    private String structureKey;<br /><%%KEEPWHITESPACE%%>    private String structureData;<br /><%%KEEPWHITESPACE%%>    private Set occurence;<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @return the id<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    @Id<br /><%%KEEPWHITESPACE%%>    @GeneratedValue(strategy = GenerationType.AUTO, generator = "chem_structure_seq_gen")<br /><%%KEEPWHITESPACE%%>    @SequenceGenerator(name = "chem_structure_seq_gen", sequenceName = "seq_chemical_structure")<br /><%%KEEPWHITESPACE%%>    @Column(name = "structure_id")<br /><%%KEEPWHITESPACE%%>    public Long getId() {<br /><%%KEEPWHITESPACE%%>        return id;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @param id the id to set<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    public void setId(Long id) {<br /><%%KEEPWHITESPACE%%>        this.id = id;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @return the structureKey<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    @Column(name = "structure_key", unique = true)<br /><%%KEEPWHITESPACE%%>    public String getStructureKey() {<br /><%%KEEPWHITESPACE%%>        return structureKey;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @param structureKey the structureKey to set<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    public void setStructureKey(String structureKey) {<br /><%%KEEPWHITESPACE%%>        this.structureKey = structureKey;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @return the structureData<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    @Column(name = "chemical_structure")<br /><%%KEEPWHITESPACE%%>    public String getStructureData() {<br /><%%KEEPWHITESPACE%%>        return structureData;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @param structureData the structureData to set<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    public void setStructureData(String structureData) {<br /><%%KEEPWHITESPACE%%>        this.structureData = structureData;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.chemicalStructure",<br /><%%KEEPWHITESPACE%%>            cascade = CascadeType.ALL)<br /><%%KEEPWHITESPACE%%>    public Set getOccurence() {<br /><%%KEEPWHITESPACE%%>        return occurence;<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>    public void setOccurence(Set occurence) {<br /><%%KEEPWHITESPACE%%>        this.occurence = occurence;<br /><%%KEEPWHITESPACE%%>    }<br />}<br />

ChemicalCompound

Very similar to ChemicalStructure. Also note the @OneToMany annotation. Here there is some additonal stuff going on. This entity class is configured to be sub-classed. All subclasses will be stored in the same table were the column “compound_type” stores to what entity class a certain row belongs to.

<br />package org.bitbucket.kienerj.moleculedatabaseframework.entityclasses;<br /><br />import java.io.Serializable;<br />import java.util.HashSet;<br />import java.util.Set;<br />import javax.persistence.CascadeType;<br />import javax.persistence.Column;<br />import javax.persistence.DiscriminatorColumn;<br />import javax.persistence.DiscriminatorType;<br />import javax.persistence.DiscriminatorValue;<br />import javax.persistence.Entity;<br />import javax.persistence.FetchType;<br />import javax.persistence.GeneratedValue;<br />import javax.persistence.GenerationType;<br />import javax.persistence.Id;<br />import javax.persistence.Inheritance;<br />import javax.persistence.InheritanceType;<br />import javax.persistence.OneToMany;<br />import javax.persistence.SequenceGenerator;<br />import javax.persistence.Table;<br />import org.bitbucket.kienerj.moleculedatabaseframework.HashCodeUtil;<br /><br />@Entity<br />@Inheritance(strategy = InheritanceType.SINGLE_TABLE)<br />@Table(name = "chemical_compound")<br />@DiscriminatorColumn(name = "compound_type", discriminatorType=DiscriminatorType.STRING)<br />@DiscriminatorValue("ChemicalCompound")<br />public class ChemicalCompound implements Serializable {<br /><br /><%%KEEPWHITESPACE%%>    private Long id;<br /><%%KEEPWHITESPACE%%>    private String compoundName;<br /><%%KEEPWHITESPACE%%>    private Set composition = new HashSet();<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @return the id<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    @Id<br /><%%KEEPWHITESPACE%%>    @GeneratedValue(strategy = GenerationType.AUTO, generator = "chem_compound_seq_gen")<br /><%%KEEPWHITESPACE%%>    @SequenceGenerator(name = "chem_compound_seq_gen", sequenceName = "seq_chem_compound")<br /><%%KEEPWHITESPACE%%>    @Column(name = "chem_compound_id")<br /><%%KEEPWHITESPACE%%>    public Long getId() {<br /><%%KEEPWHITESPACE%%>        return id;<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @param id the id to set<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    public void setId(Long id) {<br /><%%KEEPWHITESPACE%%>        this.id = id;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @return the compoundName<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    @Column(name = "compound_name")<br /><%%KEEPWHITESPACE%%>    public String getCompoundName() {<br /><%%KEEPWHITESPACE%%>        return compoundName;<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @param compoundName the compoundName to set<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    public void setCompoundName(String compoundName) {<br /><%%KEEPWHITESPACE%%>        this.compoundName = compoundName;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    @OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.compound", cascade = CascadeType.ALL)<br /><%%KEEPWHITESPACE%%>    public Set getComposition() {<br /><%%KEEPWHITESPACE%%>        return composition;<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>    public void setComposition(Set composition) {<br /><%%KEEPWHITESPACE%%>        this.composition = composition;<br /><%%KEEPWHITESPACE%%>    }<br />}<br />

Now the special or should I say non-intuitive part for which you need to either read the whole hibernate manual or find a tutorial to be able to figure it out. Maybe there is a simpler way but I was lazy and went with the tutorial.

You need the “linker class” defining the “linker table”. That class contains an “ID Class”, ChemicalCompoundCompositionID, which is annotated with @Embeddable. This “ID Class” is used as the primary key (pk) in the “linker class”. But I suggest you look at the code. It’s rather easy to understand but to figure it out yourself…not so much.

ChemicalCompoundComposition

<br />package org.bitbucket.kienerj.moleculedatabaseframework.entityclasses;<br /><br />import java.io.Serializable;<br />import javax.persistence.AssociationOverride;<br />import javax.persistence.AssociationOverrides;<br />import javax.persistence.Column;<br />import javax.persistence.EmbeddedId;<br />import javax.persistence.Entity;<br />import javax.persistence.JoinColumn;<br />import javax.persistence.Table;<br />import javax.persistence.Transient;<br /><br />@Entity<br />@Table(name = "compound_composition")<br />@AssociationOverrides({<br /><%%KEEPWHITESPACE%%>    @AssociationOverride(name = "pk.chemicalStructure",<br /><%%KEEPWHITESPACE%%>    joinColumns =<br /><%%KEEPWHITESPACE%%>    @JoinColumn(name = "chemical_structure_id")),<br /><%%KEEPWHITESPACE%%>    @AssociationOverride(name = "pk.compound",<br /><%%KEEPWHITESPACE%%>    joinColumns =<br /><%%KEEPWHITESPACE%%>    @JoinColumn(name = "chem_compound_id"))})<br />public class ChemicalCompoundComposition implements Serializable {<br /><br /><%%KEEPWHITESPACE%%>    private ChemicalCompoundCompositionID pk = new ChemicalCompoundCompositionID();<br /><%%KEEPWHITESPACE%%>    private Double percentage;<br /><br /><%%KEEPWHITESPACE%%>    @EmbeddedId<br /><%%KEEPWHITESPACE%%>    public ChemicalCompoundCompositionID getPk() {<br /><%%KEEPWHITESPACE%%>        return pk;<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>    public void setPk(ChemicalCompoundCompositionID pk){<br /><%%KEEPWHITESPACE%%>        this.pk = pk;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    @Transient<br /><%%KEEPWHITESPACE%%>    public ChemicalCompound getCompound(){<br /><%%KEEPWHITESPACE%%>        return pk.getCompound();<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>    public void setCompound(ChemicalCompound compound){<br /><%%KEEPWHITESPACE%%>       pk.setCompound(compound);<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    @Transient<br /><%%KEEPWHITESPACE%%>    public ChemicalStructure getChemicalStructure(){<br /><%%KEEPWHITESPACE%%>        return pk.getChemicalStructure();<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>    public void setChemicalStructure(ChemicalStructure structure){<br /><%%KEEPWHITESPACE%%>       pk.setChemicalStructure(structure);<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @return the percentage<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    @Column(name = "percentage", nullable = false)<br /><%%KEEPWHITESPACE%%>    public Double getPercentage() {<br /><%%KEEPWHITESPACE%%>        return percentage;<br /><%%KEEPWHITESPACE%%>    }<br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * @param percentage the percentage to set<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    public void setPercentage(Double percentage) {<br /><%%KEEPWHITESPACE%%>        this.percentage = percentage;<br /><%%KEEPWHITESPACE%%>    }<br />}<br /><br />

ChemicalCompoundCompositionID

<br />package org.bitbucket.kienerj.moleculedatabaseframework.entityclasses;<br /><br />import java.io.Serializable;<br />import javax.persistence.Embeddable;<br />import javax.persistence.ManyToOne;<br /><br />@Embeddable<br />public class ChemicalCompoundCompositionID implements Serializable {<br /><br /><%%KEEPWHITESPACE%%>    private ChemicalCompound compound;<br /><%%KEEPWHITESPACE%%>    private ChemicalStructure chemicalStructure;<br /><br /><%%KEEPWHITESPACE%%>    @ManyToOne(fetch = FetchType.LAZY)<br /><%%KEEPWHITESPACE%%>    public ChemicalCompound getCompound() {<br /><%%KEEPWHITESPACE%%>        return compound;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public void setCompound(ChemicalCompound compound) {<br /><%%KEEPWHITESPACE%%>        this.compound= compound;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    @ManyToOne(fetch = FetchType.LAZY)<br /><%%KEEPWHITESPACE%%>    public ChemicalStructure getChemicalStructure() {<br /><%%KEEPWHITESPACE%%>        return chemicalStructure;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public void setChemicalStructure(ChemicalStructure structure) {<br /><%%KEEPWHITESPACE%%>        this.chemicalStructure = structure;<br /><%%KEEPWHITESPACE%%>    }<br />}<br />

As you can see this is far more complex than you would have imaged. We also need to create an entity (ChemicalCompoundComposition) that has no real meaning in the real world and for each compound you will create you will have to create as many ChemicalCompoundComposition instances as the compound contains substances. Note that fetch = FetchType.LAZY is absolutely required in the @Embeddable ID because else session.merge(chemicalCompound) will lead to a StackOverflowError (endless references).

The Data Access Objects

Now we need to create the layer that actually persists the entities to the database. To create that layer I followed the design outlined in The Persistence Layer with Spring 3.1 and Hibernate. We create a generic abstract class AbstractHibernateDAO that contains the basic database operations. The concrete classes are very simple as the only point of them is to specify which type of Entity they save. Of course you can add methods or override some if required.

AbstractHibernateDAO

The important part are the annotations @Repository, @Transactional and @Autowired

<br />package org.bitbucket.kienerj.moleculedatabaseframework.dao;<br /><br />import com.google.common.base.Preconditions;<br />import java.io.Serializable;<br />import java.util.List;<br />import org.hibernate.Session;<br />import org.hibernate.SessionFactory;<br />import org.springframework.beans.factory.annotation.Autowired;<br />import org.springframework.stereotype.Repository;<br />import org.springframework.transaction.annotation.Transactional;<br /><br />@Repository<br />@Transactional<br />public abstract class AbstractHibernateDAO&lt; T extends Serializable&gt; {<br /><br /><%%KEEPWHITESPACE%%>    private final Class&lt; T&gt; clazz;<br /><%%KEEPWHITESPACE%%>    @Autowired<br /><%%KEEPWHITESPACE%%>    SessionFactory sessionFactory;<br /><br /><%%KEEPWHITESPACE%%>    public AbstractHibernateDAO(final Class&lt; T&gt; clazzToSet) {<br /><%%KEEPWHITESPACE%%>        this.clazz = clazzToSet;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public T getById(final Long id) {<br /><%%KEEPWHITESPACE%%>        Preconditions.checkArgument(id != null);<br /><%%KEEPWHITESPACE%%>        return (T) this.getCurrentSession().get(this.clazz, id);<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public List&lt; T&gt; getAll() {<br /><%%KEEPWHITESPACE%%>        return this.getCurrentSession()<br /><%%KEEPWHITESPACE%%>                .createQuery("from " + this.clazz.getName()).list();<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public void create(final T entity) {<br /><%%KEEPWHITESPACE%%>        Preconditions.checkNotNull(entity);<br /><%%KEEPWHITESPACE%%>        this.getCurrentSession().persist(entity);<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public void update(final T entity) {<br /><%%KEEPWHITESPACE%%>        Preconditions.checkNotNull(entity);<br /><%%KEEPWHITESPACE%%>        this.getCurrentSession().merge(entity);<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public void delete(final T entity) {<br /><%%KEEPWHITESPACE%%>        Preconditions.checkNotNull(entity);<br /><%%KEEPWHITESPACE%%>        this.getCurrentSession().delete(entity);<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public void deleteById(final Long entityId) {<br /><%%KEEPWHITESPACE%%>        final T entity = this.getById(entityId);<br /><%%KEEPWHITESPACE%%>        Preconditions.checkState(entity != null);<br /><%%KEEPWHITESPACE%%>        this.delete(entity);<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public void setSessionFactory(SessionFactory sessionFactory) {<br /><%%KEEPWHITESPACE%%>        this.sessionFactory = sessionFactory;<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    protected final Session getCurrentSession() {<br /><%%KEEPWHITESPACE%%>        return this.sessionFactory.getCurrentSession();<br /><%%KEEPWHITESPACE%%>    }<br />}<br />

ChemicalStructureDAO

<br />package org.bitbucket.kienerj.moleculedatabaseframework.dao;<br /><br />import org.bitbucket.kienerj.moleculedatabaseframework.entityclasses.ChemicalStructure;<br />import org.springframework.stereotype.Repository;<br /><br />@Repository<br />public class ChemicalStructureDAO extends AbstractHibernateDAO {<br /><br /><%%KEEPWHITESPACE%%>    public ChemicalStructureDAO() {<br /><%%KEEPWHITESPACE%%>        super(ChemicalStructure.class);<br /><%%KEEPWHITESPACE%%>    }<br />}<br />

ChemicalCompoundDAO

<br />package org.bitbucket.kienerj.moleculedatabaseframework.dao;<br /><br />import org.bitbucket.kienerj.moleculedatabaseframework.entityclasses.ChemicalCompound;<br />import org.springframework.stereotype.Repository;<br /><br />@Repository<br />public class ChemicalCompoundDAO extends AbstractHibernateDAO{<br /><br /><%%KEEPWHITESPACE%%>    public ChemicalCompoundDAO() {<br /><%%KEEPWHITESPACE%%>        super(ChemicalCompound.class);<br /><%%KEEPWHITESPACE%%>    }<br />}<br />

The Spring configuration file

I had the most trouble with this. Keep in mind that this highly depends on what dependencies you plan on using, eg. witch database, connection pool or not and so forth. The configuration I use is for PostgreSQL using BoneCP connection pool. The configuration for BoneCP was taken from the BoneCP web page. The spring configuration file is under src/main/resources/ApplicationContext.xml. See the comments within the file.

<br />&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br />&lt;beans xmlns="http://www.springframework.org/schema/beans"<br /><%%KEEPWHITESPACE%%>       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br /><%%KEEPWHITESPACE%%>       xmlns:aop="http://www.springframework.org/schema/aop"<br /><%%KEEPWHITESPACE%%>       xmlns:context="http://www.springframework.org/schema/context"<br /><%%KEEPWHITESPACE%%>       xmlns:tx="http://www.springframework.org/schema/tx"<br /><br /><%%KEEPWHITESPACE%%>       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd<br /><%%KEEPWHITESPACE%%>          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd<br /><%%KEEPWHITESPACE%%>          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd<br /><%%KEEPWHITESPACE%%>          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd<br />"&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" autowire="autodetect"&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="dataSource" ref="dataSource" /&gt;<br /><%%KEEPWHITESPACE%%>        &lt;!-- Don't forget to list all your entity classes here --&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="annotatedClasses"&gt;<br /><%%KEEPWHITESPACE%%>            &lt;list&gt;<br /><%%KEEPWHITESPACE%%>                &lt;value&gt;org.bitbucket.kienerj.moleculedatabaseframework.entityclasses.ChemicalStructure&lt;/value&gt;<br /><%%KEEPWHITESPACE%%>                &lt;value&gt;org.bitbucket.kienerj.moleculedatabaseframework.entityclasses.ChemicalCompound&lt;/value&gt;<br /><%%KEEPWHITESPACE%%>                &lt;value&gt;org.bitbucket.kienerj.moleculedatabaseframework.entityclasses.ChemicalCompoundComposition&lt;/value&gt;<br /><%%KEEPWHITESPACE%%>            &lt;/list&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/property&gt;<br /><br /><%%KEEPWHITESPACE%%>        &lt;property name="hibernateProperties"&gt;<br /><%%KEEPWHITESPACE%%>            &lt;props&gt;<br /><%%KEEPWHITESPACE%%>                &lt;prop key="hibernate.dialect"&gt;org.hibernate.dialect.PostgreSQLDialect&lt;/prop&gt;<br /><%%KEEPWHITESPACE%%>                &lt;prop key="hibernate.show_sql"&gt;true&lt;/prop&gt;<br /><%%KEEPWHITESPACE%%>                &lt;prop key="hibernate.hbm2ddl.auto"&gt;create&lt;/prop&gt;<br /><%%KEEPWHITESPACE%%>            &lt;/props&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/property&gt;<br /><%%KEEPWHITESPACE%%>    &lt;/bean&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;!-- Spring bean configuration. Tell Spring to bounce off BoneCP --&gt;<br /><%%KEEPWHITESPACE%%>    &lt;bean id="dataSource"<br /><%%KEEPWHITESPACE%%>          class="org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy"&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="targetDataSource"&gt;<br /><%%KEEPWHITESPACE%%>            &lt;ref local="mainDataSource" /&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/property&gt;<br /><%%KEEPWHITESPACE%%>    &lt;/bean&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;!-- BoneCP configuration --&gt;<br /><%%KEEPWHITESPACE%%>    &lt;bean id="mainDataSource" class="com.jolbox.bonecp.BoneCPDataSource" destroy-method="close"&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="driverClass" value="org.postgresql.Driver" /&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="jdbcUrl" value="jdbc:postgresql:MolDB" /&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="username" value="postgres"/&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="password" value="123456"/&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="idleConnectionTestPeriod" value="60"/&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="idleMaxAge" value="240"/&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="maxConnectionsPerPartition" value="60"/&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="minConnectionsPerPartition" value="20"/&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="partitionCount" value="3"/&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="acquireIncrement" value="10"/&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="statementsCacheSize" value="50"/&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="releaseHelperThreads" value="3"/&gt;<br /><%%KEEPWHITESPACE%%>    &lt;/bean&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;bean id="txManager"<br /><%%KEEPWHITESPACE%%>          class="org.springframework.orm.hibernate4.HibernateTransactionManager"&gt;<br /><%%KEEPWHITESPACE%%>        &lt;property name="sessionFactory" ref="sessionFactory" /&gt;<br /><%%KEEPWHITESPACE%%>    &lt;/bean&gt;<br /><%%KEEPWHITESPACE%%>    &lt;tx:annotation-driven transaction-manager="txManager" /&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;context:annotation-config /&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;!-- Note the usage of abstract="true" and parent="AbstractHibernateDAO" in the implementations--&gt;<br /><%%KEEPWHITESPACE%%>    &lt;bean id="AbstractHibernateDAO" abstract="true"<br /><%%KEEPWHITESPACE%%>          class="org.bitbucket.kienerj.moleculedatabaseframework.dao.AbstractHibernateDAO"/&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;bean id="ChemicalStructureDAO" parent="AbstractHibernateDAO"<br /><%%KEEPWHITESPACE%%>          class="org.bitbucket.kienerj.moleculedatabaseframework.dao.ChemicalStructureDAO"/&gt;<br /><%%KEEPWHITESPACE%%>    &lt;bean id="ChemicalCompoundDAO" parent="AbstractHibernateDAO"<br /><%%KEEPWHITESPACE%%>          class="org.bitbucket.kienerj.moleculedatabaseframework.dao.ChemicalCompoundDAO"/&gt;<br />&lt;/beans&gt;<br />

More Configuration: Maven pom.xml

<br />&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br /><%%KEEPWHITESPACE%%>  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;<br /><%%KEEPWHITESPACE%%>    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;groupId&gt;org.bitbucket.kienerj&lt;/groupId&gt;<br /><%%KEEPWHITESPACE%%>    &lt;artifactId&gt;MoleculeDatabaseFramework&lt;/artifactId&gt;<br /><%%KEEPWHITESPACE%%>    &lt;version&gt;0.1-SNAPSHOT&lt;/version&gt;<br /><%%KEEPWHITESPACE%%>    &lt;packaging&gt;jar&lt;/packaging&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;name&gt;MoleculeDatabaseFramework&lt;/name&gt;<br /><%%KEEPWHITESPACE%%>    &lt;url&gt;http://maven.apache.org&lt;/url&gt;<br /><%%KEEPWHITESPACE%%>    &lt;build&gt;<br /><%%KEEPWHITESPACE%%>        &lt;plugins&gt;<br /><%%KEEPWHITESPACE%%>            &lt;plugin&gt;<br /><%%KEEPWHITESPACE%%>                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;<br /><%%KEEPWHITESPACE%%>                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;<br /><%%KEEPWHITESPACE%%>                &lt;version&gt;2.3.2&lt;/version&gt;<br /><%%KEEPWHITESPACE%%>                &lt;configuration&gt;<br /><%%KEEPWHITESPACE%%>                    &lt;source&gt;1.7&lt;/source&gt;<br /><%%KEEPWHITESPACE%%>                    &lt;target&gt;1.7&lt;/target&gt;<br /><%%KEEPWHITESPACE%%>                    &lt;showDeprecation&gt;true&lt;/showDeprecation&gt;<br /><%%KEEPWHITESPACE%%>                &lt;/configuration&gt;<br /><%%KEEPWHITESPACE%%>            &lt;/plugin&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/plugins&gt;<br /><%%KEEPWHITESPACE%%>    &lt;/build&gt;<br /><%%KEEPWHITESPACE%%>    &lt;properties&gt;<br /><%%KEEPWHITESPACE%%>        &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;<br /><%%KEEPWHITESPACE%%>    &lt;/properties&gt;<br /><br /><%%KEEPWHITESPACE%%>    &lt;dependencies&gt;<br /><%%KEEPWHITESPACE%%>        &lt;dependency&gt;<br /><%%KEEPWHITESPACE%%>            &lt;groupId&gt;junit&lt;/groupId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;artifactId&gt;junit&lt;/artifactId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;version&gt;3.8.1&lt;/version&gt;<br /><%%KEEPWHITESPACE%%>            &lt;scope&gt;test&lt;/scope&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/dependency&gt;<br /><%%KEEPWHITESPACE%%>        &lt;dependency&gt;<br /><%%KEEPWHITESPACE%%>            &lt;groupId&gt;org.springframework&lt;/groupId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;artifactId&gt;spring-orm&lt;/artifactId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;version&gt;3.1.2.RELEASE&lt;/version&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/dependency&gt;<br /><%%KEEPWHITESPACE%%>        &lt;dependency&gt;<br /><%%KEEPWHITESPACE%%>            &lt;groupId&gt;org.hibernate&lt;/groupId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;artifactId&gt;hibernate-entitymanager&lt;/artifactId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;version&gt;4.1.7.Final&lt;/version&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/dependency&gt;<br /><%%KEEPWHITESPACE%%>        &lt;dependency&gt;<br /><%%KEEPWHITESPACE%%>            &lt;groupId&gt;com.jolbox&lt;/groupId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;artifactId&gt;bonecp-provider&lt;/artifactId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;version&gt;0.8.0-alpha1&lt;/version&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/dependency&gt;<br /><%%KEEPWHITESPACE%%>        &lt;dependency&gt;<br /><%%KEEPWHITESPACE%%>            &lt;groupId&gt;postgresql&lt;/groupId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;artifactId&gt;postgresql&lt;/artifactId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;version&gt;9.1-901.jdbc4&lt;/version&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/dependency&gt;<br /><%%KEEPWHITESPACE%%>        &lt;dependency&gt;<br /><%%KEEPWHITESPACE%%>            &lt;groupId&gt;cglib&lt;/groupId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;artifactId&gt;cglib&lt;/artifactId&gt;<br /><%%KEEPWHITESPACE%%>            &lt;version&gt;2.2.2&lt;/version&gt;<br /><%%KEEPWHITESPACE%%>        &lt;/dependency&gt;<br /><%%KEEPWHITESPACE%%>    &lt;/dependencies&gt;<br />&lt;/project&gt;<br />

You need BoneCP-0.8.0-alpha1 for Hibernate 4! The release version does not work. This was also one of the many issues i had to figure out (thanks google). Maybe other connection pools have similar issues.

How to use

<br />package org.bitbucket.kienerj.moleculedatabaseframework;<br /><br />import org.bitbucket.kienerj.moleculedatabaseframework.dao.ChemicalCompoundDAO;<br />import org.bitbucket.kienerj.moleculedatabaseframework.dao.ChemicalStructureDAO;<br />import org.bitbucket.kienerj.moleculedatabaseframework.entityclasses.ChemicalCompound;<br />import org.bitbucket.kienerj.moleculedatabaseframework.entityclasses.ChemicalCompoundComposition;<br />import org.bitbucket.kienerj.moleculedatabaseframework.entityclasses.ChemicalStructure;<br />import org.springframework.context.ApplicationContext;<br />import org.springframework.context.support.ClassPathXmlApplicationContext;<br /><br />public class App<br />{<br /><%%KEEPWHITESPACE%%>    public static void main( String[] args )<br /><%%KEEPWHITESPACE%%>    {<br /><br /><%%KEEPWHITESPACE%%>        // Yes,  I should now and then actually read manuals and documentations (=boring) but then<br /><%%KEEPWHITESPACE%%>        // I only have so much time and examples help me so much more<br /><%%KEEPWHITESPACE%%>        // THIS IS THE IMPORTANT LINE I DID NOT SEE IN ANY TUTORIAL<br /><%%KEEPWHITESPACE%%>        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");<br /><br /><%%KEEPWHITESPACE%%>        // You need to create the DAOs from Spring Context else the "Spring magic" will not be available<br /><%%KEEPWHITESPACE%%>        ChemicalStructureDAO structureDAO = (ChemicalStructureDAO) context.getBean("ChemicalStructureDAO");<br /><%%KEEPWHITESPACE%%>        ChemicalCompoundDAO compoundDAO = (ChemicalCompoundDAO) context.getBean("ChemicalCompoundDAO");<br /><br /><%%KEEPWHITESPACE%%>        // From here on it is pretty straight forward.<br /><%%KEEPWHITESPACE%%>        // For me there are a bit too many lines to make it look nice but it works...<br /><%%KEEPWHITESPACE%%>        ChemicalStructure structure1 = new ChemicalStructure();<br /><%%KEEPWHITESPACE%%>        structure1.setStructureKey("c1ccccc1");<br /><%%KEEPWHITESPACE%%>        structure1.setStructureData("c1ccccc1");<br /><br /><%%KEEPWHITESPACE%%>        ChemicalStructure structure2 = new ChemicalStructure();<br /><%%KEEPWHITESPACE%%>        structure2.setStructureKey("CC=O");<br /><%%KEEPWHITESPACE%%>        structure2.setStructureData("CC=O");<br /><br /><%%KEEPWHITESPACE%%>        structureDAO.create(structure1);<br /><%%KEEPWHITESPACE%%>        structureDAO.create(structure2);<br /><br /><%%KEEPWHITESPACE%%>        ChemicalCompound compound = new ChemicalCompound();<br /><%%KEEPWHITESPACE%%>        compound.setCompoundName("Test Compound");<br /><br /><%%KEEPWHITESPACE%%>        ChemicalCompoundComposition composition1 = new ChemicalCompoundComposition();<br /><%%KEEPWHITESPACE%%>        composition1.setCompound(compound);<br /><%%KEEPWHITESPACE%%>        composition1.setChemicalStructure(structure1);<br /><%%KEEPWHITESPACE%%>        composition1.setPercentage(70.0);<br /><br /><%%KEEPWHITESPACE%%>        ChemicalCompoundComposition composition2 = new ChemicalCompoundComposition();<br /><%%KEEPWHITESPACE%%>        composition2.setCompound(compound);<br /><%%KEEPWHITESPACE%%>        composition2.setChemicalStructure(structure2);<br /><%%KEEPWHITESPACE%%>        composition2.setPercentage(30.0);<br /><br /><%%KEEPWHITESPACE%%>        compound.getComposition().add(composition1);<br /><%%KEEPWHITESPACE%%>        compound.getComposition().add(composition2);<br /><br /><%%KEEPWHITESPACE%%>        compoundDAO.create(compound);<br /><%%KEEPWHITESPACE%%>    }<br />}<br />

After running above snippet the database looks like this:

Conclusion

I hope this article can help some people to do their first steps with Spring and Hibernate. After all it is not hard if you know how it works..if…

UPDATE 1

You will need to watch out for LazyInitializationException. With above code of you call compound.getComposition().get(0).getChemicalStructure() (eg. get the first ChemicalStructure linked to this compound) outside of the DAO, meaning Hibernate Session is closed, you will get a LazyInitializationException. This happens because the data for the ChemicalStructure was not loaded (FetchType.LAZY) when loading the compound. To avoid this you will need to explicitly load the data within the DAO (Hibernate Session). Example

<br /><%%KEEPWHITESPACE%%>    public ChemicalCompound getById(Long id) {<br /><br /><%%KEEPWHITESPACE%%>        ChemicalCompound compound = compoundDAO.getById(id);<br /><%%KEEPWHITESPACE%%>        for (ChemicalCompoundComposition composition : compound.getComposition()) {<br /><%%KEEPWHITESPACE%%>            composition.getChemicalStructure().getId();<br /><%%KEEPWHITESPACE%%>        }<br /><%%KEEPWHITESPACE%%>        return compound;<br /><%%KEEPWHITESPACE%%>    }<br />

UPDATE Sep 2013

Above example of a many-to-many relationship with an additional column, namely ChemicalCompoundComposition can be achieved much easier and this easier solution is also more flexible especially in terms up updating. The solution is actually pretty obvious. No idea why I did not see it back then. Rookie mistake. Below the new code, with JPA 2.0 annotations and using project lombok annotations for auto generation of getters, setters and equals() and hashCode() methods.

<br />@Entity<br />@Table(name = "compound_composition", uniqueConstraints=<br /><%%KEEPWHITESPACE%%>        @UniqueConstraint(columnNames = {"chem_compound_id", "chemical_structure_id"}))<br />@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")<br />@Data<br />@EqualsAndHashCode(callSuper = false, of = {"compound", "chemicalStructure"})<br />public class ChemicalCompoundComposition extends BaseEntity {<br /><br /><%%KEEPWHITESPACE%%>    @Id<br /><%%KEEPWHITESPACE%%>    @GeneratedValue(strategy = GenerationType.AUTO,<br /><%%KEEPWHITESPACE%%>            generator = "composition_seq_gen")<br /><%%KEEPWHITESPACE%%>    @SequenceGenerator(name = "composition_seq_gen",<br /><%%KEEPWHITESPACE%%>            sequenceName = "seq_compound_composition", allocationSize = 1000)<br /><%%KEEPWHITESPACE%%>    @Column(name = "composition_id")<br /><%%KEEPWHITESPACE%%>    private Long id;<br /><br /><%%KEEPWHITESPACE%%>    @Column(name = "percentage")<br /><%%KEEPWHITESPACE%%>    private Double percentage;<br /><br /><%%KEEPWHITESPACE%%>    @JoinColumn(name = "chem_compound_id", nullable = false)<br /><%%KEEPWHITESPACE%%>    @ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})<br /><%%KEEPWHITESPACE%%>    private ChemicalCompound compound;<br /><%%KEEPWHITESPACE%%>    @JoinColumn(name = "chemical_structure_id", nullable = false)<br /><%%KEEPWHITESPACE%%>    @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})<br /><%%KEEPWHITESPACE%%>    private ChemicalStructure chemicalStructure;<br /><br /><%%KEEPWHITESPACE%%>    /**<br /><%%KEEPWHITESPACE%%>     * For JPA Provider<br /><%%KEEPWHITESPACE%%>     */<br /><%%KEEPWHITESPACE%%>    public ChemicalCompoundComposition() {<br /><%%KEEPWHITESPACE%%>    }<br /><br /><%%KEEPWHITESPACE%%>    public ChemicalCompoundComposition(ChemicalCompound compound,<br /><%%KEEPWHITESPACE%%>            ChemicalStructure structure,<br /><%%KEEPWHITESPACE%%>            Double percentage) {<br /><br /><%%KEEPWHITESPACE%%>        this.percentage = percentage;<br /><%%KEEPWHITESPACE%%>        this.chemicalStructure = structure;<br /><%%KEEPWHITESPACE%%>        this.compound = compound;<br /><%%KEEPWHITESPACE%%>    }<br />}<br />

Written by kienerj

October 5, 2012 at 16:38

Posted in Database, Java, Programming

Tagged with , ,