Loading...
 

Harezmi IT Solutions Blog

Published by ksevindik on 2016-01-18 ksevindik

Spring documentation states that both autowire byType and constructor modes expect at most one bean definition in the ApplicationContext, so that it can be autowired into the depending bean. Here is the excerpt taken from Spring Reference Documentation Table 6.2. Autowiring modes;

byTypeAllows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set.
constructorAnalogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.


However, they don't show exact behaviour if there are more than one bean in the ApplicationContext and name of one of those beans matches with the name of constructor parameter. Let's see what is the difference with the following code samples;

First, XML based configuration

public class Foo {
	private Bar bar;

	public void setBar(Bar bar) {
		this.bar = bar;
	}
	
	public Bar getBar() {
		return bar;
	}
}

<beans...>
	<bean id="foo" class="examples.Foo" autowire="byType"/>
	
	<bean id="bar1" class="examples.Bar"/>
	
	<bean id="bar2" class="examples.Bar"/>
</beans>


The above sample will produce following error as expected:

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [examples.Bar] is defined: expected single matching bean but found 2: bar1,bar2


Now we change the Foo class so that Bar is to be injected as a constructor parameter, change autowire mode to constructor, and give a try;

public class Foo {
	private Bar bar;

	public Foo(Bar bar) {
		this.bar = bar;
	}
}

<beans...>
	<bean id="foo" class="examples.Foo" autowire="constructor"/>
	
	<bean id="bar1" class="examples.Bar"/>
	
	<bean id="bar2" class="examples.Bar"/>
</beans>


As expected, we got the same error as above;

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [examples.Bar] is defined: expected single matching bean but found 2: bar1,bar2


Now, here comes the difference. When we add another name one of those two Bar beans with an element, for example, autowire="constructor" starts working! It injects the bean with name matching with the name of the constructor parameter.

<beans...>
	<bean id="foo" class="examples.Foo" autowire="constructor"/>
	
	<bean id="bar1" class="examples.Bar"/>
	
	<bean id="bar2" class="examples.Bar"/>

	<alias name="bar2" alias="bar"/>
</beans>


Practically, autowire="constructor" turns into "byName", in which it is stated that only bean with the matching name is injected. However, when we run the code with autowire="byType", it still gives the error as listed above.

Now, annotation based configuration


At this point, let's give annotation based configuration a try, and see what happens there as well.

@Component
public class Foo {
	private Bar bar;

	@Autowired
	public Foo(Bar bar) {
		this.bar = bar;
	}	
}

<beans...>
	<context:component-scan base-package="examples"/>
	
	<bean id="bar1" class="examples.Bar"/>
	
	<bean id="bar2" class="examples.Bar"/>

	<alias name="bar2" alias="bar"/>
</beans>


When @Autowired annotation is placed on constructor, it works as expected.

Now, I change the code so that autowire will be performed with setter injection as follows;

@Component
public class Foo {
	private Bar bar;
	
	@Autowired
	public void setBar(Bar bar) {
		this.bar = bar;
	}
	
	public Bar getBar() {
		return bar;
	}
}


I would expect it wouldn't work when @Autowired annotation is placed over setter method, but, it works!

Unfortunately, the result of xml based autowiring with byType mode prevents us from concluding that autowire byType and constructor modes give precedence to bean name - property/constrcutor parameter name correspondence when more than one bean of matching type found in the ApplicationContext. There is clearly a behavioural inconsistency between xml based and annotation based autowiring in Spring.

Published by ksevindik on 2016-01-16 ksevindik

Spring's transaction propagation rule SUPPORTS states that if there exists a transaction when the method is called, it should work within that transaction, otherwise it should work outside any transaction. Similarly, transaction propagation rule NOT_SUPPORTED states that if there exists any active transaction when the method is called, that active transaction should be suspended before method is run, and method should run outside any active transaction.

However, things work a bit odd when Spring transaction mechanism is used with Hibernate as persistence technology at the backend. First of all we should know that although transaction propagation is set as SUPPORTS or NOT_SUPPORTED, Spring still creates a logical transaction so that it can execute if there exists any registered TransactionSynchronization objects at the end of the method call. Another thing to know that, we also need an active physical Hibernate Transaction object if we want to use contextual session feature of Hibernate in our DAO objects, and actually problem starts here. If you use contextual session of Hibernate within your DAO objects by calling sessionFactory.getCurrentSession(), SpringSessionContext which is in charge of implementing contextual session capability within Spring environment, registers a SpringSessionSynchronization object when getCurrentSession() is called and switches Hibernate Session flush mode to AUTO if it is MANUAL unless transaction is marked as readOnly=true.

When method completes with success, TransactionInterceptor performs commit! Yes, that is correct, Spring transaction support performs a commit whenever method returns with success even no actual transaction exists. Although HibernateTransactionManager which is actually in change of managing transactions, won't commit as there is no actiual transaction, it still executes registered TransactionSynchronization objects consecutively. SpringSessionSynchroization, at this point invokes Hibernate Session flush, and our SQL DML statements are sent to DB. Unfortunately, Hibernate physical transaction commits when Session is closed, even though transaction.commit() is not invoked explicitly. Hence, our changes become permanent in DB.

This scenario is valid both for propagation SUPPORTS and NOT_SUPPORTED. One workaround is to mark transaction as readOnly=true. This causes Spring to set Hibernate Session flush mode to MANUAL, and no flush occurs during SpringSessionSchronization.beforeCommit() method call. The other one is to change transactionSynchronization behaviour of Spring's PlatformTransactionManager to ON_ACTUAL_TRANSACTION. If you change it so, Spring won't register any TransactionSynchronization object including SpringSessionSynchronization. In that case, you won't be able to use contextual session capability within your DAOs. Therefore, it is better to go with the first option, and DON'T forget to mark your transactions as readOnly=true if their propagation rule is either SUPPORTS or NOT_SUPPORTED on your service methods.

Published by ksevindik on 2016-01-11 ksevindik

In our book about Spring, we discuss about transaction management mechanisms available in the Spring Framework, namely declarative and programmatic transaction mechanisms. Inside programmatic transaction demarcation section, it is further explained that how programmatic transaction can be achieved using different methods. One of the ways programmatic transaction can be achieved is, using TransactionTemplate utility class provided by Spring itself. It is based on Template Method pattern which constantly emerges over several different parts of the Spring Framework to encapsulate steps of a recurring operation, such as data access, transaction management, REST or JMS communication logic etc. In that case, main transaction demarcation logic is fixed within the specific template method, but varying steps can be given into it later on, so that specifics of the algorithm can be customized by the calling body. In our case transactional code block which needs to be run within an active transaction is given as a method parameter.

TransactionTemplate tt = new TransactionTemplate(transactionManager);
tt.execute(new TransactionCallbackWithoutResult() {
        public void doInTransaction(TransactionStatus status) {
                entityManager.persist(pet);
        }
});


Recently, one of our book readers asked about whether mentioning about TransactionTemplate as an example of Template Method is really correct. He asked that shouldn't we call it as Strategy instead. Well, actually both Strategy and Template Method patterns are very similar in behavioural aspect. Indeed, we can call them as cousins with some slightly different characteristics. They both try to encapsulate an algorithm. However, they varies in how that encasulation is achieved.

Image


Strategy pattern encapsulates the algorithm using object composition or delegation. Subject depends on Strategy interface, and a concrete implementation of the Strategy is chosen and invoked based on decisions made at runtime. That way, specific algorithm which will be executed can be determined and changed dynamically. Context information of the client environment which might be consulted at during execution of the algorithm is usually passed in as method argument in the Strategy pattern.

public class AuthenticationProvider {
	private UserService userService;
	
	public AuthenticationProvider(UserService userService) {
		this.userService = userService;
	}

	public boolean authenticate(String username, String password) {
		User user = userService.loadUserByUsername(username);
		return user != null && user.getPassword().equals(password);
	}
}

public interface UserService {
	public User loadUserByUsername(String username);
}


In the above code sample, AuthenticationProvider depends on UserService in order to obtain User object through it. UserSerice, an interface, is in role of Strategy here, and it may have several concrete implementations, each of which queries User by username from different user realms. Any of those concrete implementations can be injected into AuthenticationProvider at runtime.

Image

On the other hand, Template Method pattern handles encapsuation of the algorithm and selection of it at compile time, usually based on inheritance. It fixes main steps of algorithm in a base class, called as Template Method class, and expects varying steps to be provided by the specific subclass which extends from the Template method class. Therefore, algorithm selection is finished long before app is running.

public abstract class QueryTemplate<T> {

	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
 
	public final List<T> execute() {
		Connection connection = null;
		PreparedStatement statement = null;
		try {
			connection = dataSource.getConnection();
			String sql = getSql();
			statement = connection.prepareStatement(sql);
			Object[] args = getArgs();
			for(int i = 0; i < args.length; i++) {
				statement.setObject(i+1, args[i]);
			}
			ResultSet resultSet = statement.executeQuery();
			List<T> resultList = new ArrayList<T>();
			while (resultSet.next()) {
				T record = process(resultSet);
				resultList.add(record);
			}
			return resultList;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			if (statement != null)
				try {
					statement.close();
				} catch (SQLException e) {}
			if (connection != null)
				try {
					connection.close();
				} catch (SQLException e) {}
		}
	}

	protected abstract String getSql();
	protected abstract Object[] getArgs();
	protected abstract T process(ResultSet resultSet) throws SQLException ;
}


In the above code sample, QueryTemplate is a base class in which execute() method is implemented along with three other abstract methods which are called by the execute() method at specific steps during query execution process. Sub classes extending QueryTemplate override those abstract methods implementing expected behaviours within them. Clients don't need to know exact sub type during query execution, they simply depend on QueryTemplate to perform their task.

Inheriting Template method class is not the most effective way from a framework perspective to provide varying parts. In Java environment, it cannot be feasible as well, especially class which needs to extend from Template Method class is already extending from another class, hitting against multiple inheritance limitation. Instead, frameworks follow another way to feed Template Method with those varying steps; as method input arguments.

public class QueryTemplate<T> {

	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public final List<T> execute(String sql, RowExtractor<T> rowExtractor, Object...args) {
		Connection connection = null;
		PreparedStatement statement = null;
		try {
			connection = dataSource.getConnection();
			statement = connection.prepareStatement(sql);
			for(int i = 0; i < args.length; i++) {
				statement.setObject(i+1, args[i]);
			}
			ResultSet resultSet = statement.executeQuery();
			List<T> resultList = new ArrayList<T>();
			while (resultSet.next()) {
				T record = rowExtractor.process(resultSet);
				resultList.add(record);
			}
			return resultList;
		} catch (SQLException e) {
			throw new RuntimeException(e);
		} finally {
			if (statement != null)
				try {
					statement.close();
				} catch (SQLException e) {}
			if (connection != null)
				try {
					connection.close();
				} catch (SQLException e) {}
		}
	}

	public static interface RowExtractor<T> {
		public T process(ResultSet rs);
	}
}


That code sample shows variation in Template Method mentioned previously. QueryTemplate is concrete in this case, and RowExtractor is interface which declares a method to process each row in the ResultSet return from the query executed. Both query to be executed, query parameters if there exists any, and rowExtractor object are given as method parameters to execute method of QueryTemplate. That way, application code doesn't need to extend from Template Method class over and over again. It just calls the method in order to execute the algorithm, and passes varying steps as method arguments into it.

If the varying parts are code blocks that need to be executed at a specific point in the algorithm, the only possible way prior to Java 8 was anonymous classes. Some contextual information, like TransactionStatus in our TransactionTemplate example, can be passed in as method parameters of anonymous class similar to Strategy pattern as well. Probably, this variation in the implementation of the Template Method pattern causes some people to think that it more resembles to the Strategy pattern.

Published by ksevindik on 2015-11-13 ksevindik

Hibernate'in dokümante edilmemiş özelliklerden birisi de import.sql kabiliyetidir. Eğer root classpath'e import.sql isimli bir dosya eklerseniz ve bu dosya içerisine de SQL ifadeleri yazarsanız Hibernate şema export adımından sonra bu SQL ifadelerini çalıştıracaktır. Bu yöntem ile test veya development sırasında örnek verinin DB'ye eklenmesi mümkündür. import.sql kullanırken dikkat edilmesi gereken bir iki husus söz konusudur. Bunlardan birisi yazılacak SQL ifadeleri DB spesifik olacaktır. Diğeri ise import.sql'in çalıştırılabilmesi için hibernate.hbm2ddl.auto property değerinin ya "create" ya da "create-drop" olmasıdır.

Tabi Spring kullanıcıları için bu tür örnek veri populate etme kabiliyetleri çok primitif gelecektir. Spring tarafında jdbc:embedded-database veya jdbc:initialize-database gibi namespace elemanları ile bırakın uygulama düzeyinde veri yüklemeyi, test sınıflarına özel örnek veri yüklemek bile çok kolay bir iştir.

<jdbc:embedded-database id="dataSource">
    <jdbc:script location="classpath:schema.sql"/>
    <jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>

<jdbc:initialize-database data-source="dataSource">
    <jdbc:script location="classpath:com/foo/sql/db-schema.sql"/>
    <jdbc:script location="classpath:com/foo/sql/db-test-data.sql"/>
</jdbc:initialize-database>
Published by ksevindik on 2015-11-12 ksevindik

ULAKBİM tarafından düzenlenen Kamuda Açık Kaynak Konferansı'ında Spring Framework'ü etraflıca tanıttığımız Spring Ekosisteminde Kurumsal Yazılım Geliştirme isimli bir sunum yaptık. Katılım oldukça yoğundu. Sunumu Spring, kurumsal uygulamaların özellikleri ve orta katman ihtiyaçları ile ilgili kısa bir bilgilendirmenin ardından demo ağırlıklı gerçekleştirdik.

12.11.2015 tarihinde ULAKBİM tarafından düzenlenen Kamuda Açık Kaynak kullanımı ile ilgili etkinlikte yaptığımız Spring Ekosisteminde Kurumsal Yazılım Geliştirme isimli sunumdan bir görüntü


Kurumsal Java Eğitimleri'mizde sürekli olarak kullandığımız petclinic örneği üzerinde sıfırdan bir Spring konfigürasyonu nasıl yapılır, en basit biçimde standalone ortamda Spring ApplicationContext nasıl yaratılır ve kullanılır ile başlayarak Spring'in sağladığı pek çok kabiliyeti adım adım örnekledik.

12.11.2015 tarihinde ULAKBİM tarafından düzenlenen Kamuda Açık Kaynak kullanımı ile ilgili etkinlikte yaptığımız Spring Ekosisteminde Kurumsal Yazılım Geliştirme isimli sunumdan bir görüntü


Bu örnekler arasında Spring ile entegrasyon birim testleri, JDBC ile veri erişim kabiliyetleri nelerdir, transaction yönetimi, servis metotları düzeyinde cache'leme ve validasyon kabiliyetleri, aspect oriented programlamada Spring'in sağladığı kolaylıklar, custom bir aspect'in yazılması ve çalıştırılması, standalone ortamda yapılan bütün bu konfigürasyonun web ortamına taşınarak uygulamanın çalıştırılması, MVC ve REST kabiliyetleri, Spring Security'nin form ve HTTP basic authentication kabiliyetleri mevcuttu. Katılımcılar 3 saate yakın süren oturum boyunca bütün bu özelliklere sahip kurumsal bir web uygulamasının Spring kullanılarak nasıl geliştirilebileceğini gözleme şansına sahip oldular.

20151112 ULAKBIM KAK Spring Framework Sunumu

Published by ksevindik on 2015-09-08 ksevindik

Some things have been changed in Spring Security 4.x compared to previous 3.2.x branches. They are not overwhelming but you may have to deal with them so that your application can work without any problem after upgrading to Spring 4.x release. I noted them down during my upgrade process, and post here in case you need.

  • For a long time login processing url, username and password request parameter names of UsernamePasswordAuthenticationFilter were j_spring_security_check, j_username and j_password consecutively. They are now replaced with login, username and password by default.

  • CSRF protection feature is available for sometime, but it was disabled by default. However, Spring Security 4.x comes with CSRF protection enabled by default. This change has consequences to your web requests, especially pages which perform form submission with HTTP POST method. You need to add an hidden input parameter as following;

<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">

  • element had use-expressions="false" in Spring 3.2.x series. Therefore l&gt; elements were usually being configured with ROLE_xxx access attributes by default. This has changed in Spring 4.x as well. From now on, anyone who starts using Spring Security, should provide intercept-url access attributes with expressions returning boolean value.

  • logout processing url has been also changed to logout from spring_security_logout. LogoutFilter is now only accepting POST requests. Therefore, you need to add a simple logout form which is calling logout with HTTP POST method.

<form action="logout" method="post">
	<input type="submit" value="Logout"> 
	<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}">
</form>


However, it is not currently possible to change configuration of LogoutFilter so that it should work with HTTP GET requests.

  • RememberMeAuthenticationFilter were querying _spring_security_remember_me request parameter to save initiate remember-me mechanism. This has changed to remember-me in Spring 4.x.

  • Some classes in acl packages were also changed as well. Therefore you may need to change your acl bean configuration if you are using ACL in your project.
Published by ksevindik on 2015-09-06 ksevindik

Bir önceki yazımızda 1:M entity-bileşen türü ilişkileri incelemiştik. Bu yazımızda ise M:N ilişkileri incelemeye başlayacağız.

M:N ilişkiler sadece entity'ler arasında olabilir. İlişkili entity'lerin bilgisi veritabanında bir "association tablo"da tutulur. İlişkiler tek veya çift yönlü olabilirler. Eğer çift yönlü bir M:N ilişki varsa, taraflardan birisi bu ilişkiyi yöneten olarak tanımlanmalıdır. Hedef entity'lerin tutulduğu Collection sınıfının türüne  göre de M:N ilişki List,Set,Bag veya Map şeklinde olabilir.

M:N ilişki kurmak için @ManyToMany anotasyonu kullanılır. Eğer ilişki çift yönlü ise ilişkinin diğer tarafına da @ManyToMany anotasyonu yerleştirilir. Çift yönlü ilişkilerde yöneten taraf mappedBy attribute ile tanımlanır. İlişki ister tek, isterse çift yönlü olsun association tablo tanımı yapılmalıdır. Bunun için @JoinTable anotasyonu kullanılır. @JoinTable anotasyonunda tablo isminin yanı sıra her iki entity'nin tablolarına doğru kurulacak FK ilişkilerinin bilgilerinin de belirtilmesi gerekir. joinColumns attribute'una source entity'nin PK'sına, inverseJoinColumns attribute'unda ise target entity'nin PK'sına doğru referans verilmesi gerekir. Bu referanslar association tablo'da iki ayrı sütun olarak karşılık bulur.

M:N ilişkilerinde genellikle association tablo içerisinde ilişkili tablolara FK veren bu iki sütun dışında ilave başka bilgilerin de tutulması söz konusudur. Bu ilave bilgiler ilişki ile ilgili bilgilerdir. Örneğin, ilişkinin kim tarafından kurulduğu, ne zaman kurulduğu, geçerlilik süresi vb. Böyle bir durumda association tablonun da ayrı bir entity veya bileşen sınıf ile eşleştirilmesi gerekir. Association tablo da ayrı bir entity ile eşleştirildiği vakit bu durumda M:N ilişki iki tane 1:M ilişkiye dönüşmektedir. Association tablo bir entity veya embedabble ile eşleştirilebilir. Her ikisinin de kendine göre artı ve eksileri vardır.

Association tablo entity ile eşleştirildiği vakit asıl M:N ilişkisinin her iki yanından da bu "association entity"ye 1:M referanslar verilebilir. Association entity'den de bu source ve target entity'lere M:1 referans verilebilir. Association tablonun entity ile eşleştirilmesinin en önemli artısı M:N ilişkideki her iki entity'den de association entity'ye referanslar verilebilmesidir. Association entity kendi başına Hibernate'deki entity'lerin yaşam döngüsüne dahil olacaktır. Bu durumda da association entity'nin collection'dan çıkarılması entity'nin silinmesi anlamına da geleceği için genellikle bu tür 1:M ilişkiler orphanRemoval=true attribute ile parent-child şeklinde tanımlanmaktadır.

Diğer senaryo ise association tablo'nun entity yerine embeddable bir bileşen sınıf ile eşleştirilmesidir. Association tablo bir bileşen sınıf ile eşleştirildiği takdirde asıl M:N ilişkideki source ve target entity'lerin hangisinde bu bileşenleri içeren collection'ın tanımlanacağına karar vermek gerekir. Çünkü bileşenler sadece ve sadece tek bir entity instance'ına ait olabilirler. Ancak bileşen içerisinden bir veya daha fazla farklı türde entity ile M:1 ilişki kurulması mümkündür. Bu şekilde association tablo bir embeddable ile eşleştirilerek M:N ilişki association tablosuna karşılık gelen bileşen ile birlikte tanımlanmış olur.

M:N ilişkiler lazy veya eager olarak tanımlanabilir. Default olarak M:N ilişkiler lazy'dir. Ancak @ManyToMany anotasyonundaki fetchType attribute'una değer olarak FetchType.EAGER değeri verilerek M:N ilişki eager yüklenebilir.

Bu yazı ile birlikte, Hibernate'de sınıf ilişkilerini incelediğimiz altı bölümlük yazı dizimizi tamamlamış oluyoruz. Sonuç olarak, Hibernate'de veya JPA'da sınıflar arası ilişkilerin iş mantığının ve veri modelin ihtiyaçlarına göre uygun ve doğru biçimde oluşturulmasının çalışma zamanındaki persistence işlemlerinin sağlıklı ve hızlı biçimde yürütülmesi için oldukça önemli olduğunu söyleyebiliriz.

Published by ksevindik on 2015-08-30 ksevindik

Hibernate'de sınıf ilişkilerini incelediğimiz yazı dizimizin bir önceki bölümünde entity'ler arasındaki 1:M türünden ilişkileri ele almıştık. Bu bölümde ise target sınıfı component yani bileşen olan 1:M ilişkileri inceleyeceğiz. Bildiğimiz gibi bileşenler sadece tek bir entity instance'a ait olabilirler ve kendi başlarına var olamazlar. Başka bir ifade ile ait oldukları parent entity instance'ın yaratılmasından sonraki bir zamanda onunla ilişkilendirilerek yaratılırlar, ve parent entity ile olan ilişkileri ortadan kalktığı anda da veritabanından da silinirler.

entity - component 1:M ilişkisi source entitydeki collection property'si üzerinde @ElementCollection anotasyonu ile tanımlanır. Hedef bileşen sınıfı basic Java tipinden bir sınıf, yani Integer, Long, String, Boolean, Date vb, veya @Embeddable anotasyonu ile işaretli custom bir sınıf olabilir.

Varsayılan durumda 1:M ilişkiler LAZY tanımlandıkları için parent entity yüklendiğinde ilişkili bileşenler yüklenmezler. Parent entity yüklendiği anda ilişkili bileşenlerin de yüklenmesi için @ElementCollection'ın fetch attribute'unun değerinin FetchType.EAGER olarak set edilmesi gerekir. 1:M bileşen içeren ilişkiler tek yönlü veya çift yönlü olarak tanımlanabilir. Hibernate'e özel @Parent anotasyonu bileşen sınıfının içerisinde parent entity property'si üzerinde kullanılarak bileşen içerisinden parent entity'ye erişilmesi sağlanabilir. JPA tarafından bu anotasyon desteklenmemektedir. JPA'da ise bileşenden ait olduğu parent entity'ye referans normal bir M:1 ilişki tanımı ile gerçekleştirilebilir. Bileşen içerisinde M:1 ilişkileri sadece parent entity'ye doğru değil herhangi türden bir entity'ye doğru da kurulabilir.

1:M bileşen içeren ilişkilerde bileşenlere ait veri ayrı bir tabloda tutulur. Bu tablonun yönetilmesi için JPA @CollectionTable anotasyonunu sunmaktadır. @CollectionTable anotasyonu ile tablonun ismi, bileşen tablosundan parent entity'nin tablosuna olan join column'un ismi vs yönetilebilir. Bileşen tablosundaki diğer sütun veya sütunlar ise bileşenin sınıfı üzerinden elde edilmektedir. Eğer bileşen sınıfı basic bir Java tipinden ise sütun ismi @CollectionTable anotasyonu ile birlikte kullanılan @Column anotasyonundan elde edilecektir. Eğer bileşen sınıfı @Embeddable anotasyonu ile işaretlenmiş custom bir tip ise bu durumda sütun isimleri hedef sınıfın property'lerindeki @Column tanımlarından elde edilecektir.

Collection tipi bag, list, set veya map olabilir. List tanımı için entity-entity ilişkilerinde olduğu gibi @OrderColumn anotasyonu kullanılması gerekir. Map için ise key değerlerinin tutulduğu sütun @MapKeyColumn anotasyonu ile belirtilmelidir. Burada eğer bileşen tipi embeddable bir sınıf ise  key column embeddable sınıfın persistent property'lerinin dışında bir sütuna karşılık gelmelidir. Aksi takdirde Hibernate hata verecektir.

Yazı dizimize M:N ilişkilerle devam edeceğiz.

Published by ksevindik on 2014-06-20 ksevindik

If you  plan to move repeating libraries of your several web applications depend on, to a shared folder of your application server, I would definitely say NO! to this request. One of my clients asked me about to centralise such  repeating libraries into a shared folder. Actually, I had written another blog post in which I mentioned about the problems that may arise, and had concluded that "it is much more preferable to keep application specific classes or jars isolated in each web application’s private classpath." in it. However, he insisted on seeing what will happen if we try to move them out...

I mentioned to him that main problem arise with some libraries/frameworks located in common/shared folder that need to load classes/resources which need to be placed in web application specific classpath locations, either WEB-INF/classes, or WEB-INF/lib. Unfortunately, due to ClassLoader hierarchy in web containers, such libraries/frameworks could easily fail to load application specific resources/classes located in web app specific classpath locations.

Web containers use ClassLoaders as well as WebAppClassLoaders, whose behaviour is a bit different than ordinary ClassLoaders, in order to load and serve web applications deployed. Each web application has its own WebAppClassLoader and they first look at those app specific locations (WEB-INF/classes and jars in WEB-INF/lib) to load required classes/resources. They consult to their parent ClassLoader after they fail to find them in those locations. Parent ClassLoaders show ordinary Java ClassLoader behaviour. They first ask their parent, and only if their parent returns null they will check their classpath locations.  Parents will also never ask to their children to resolve classes/resources.

In the end, we have hit by several different parts which need to load resources, located in WEB-INF/classes or WEB-INF/lib folders. One was Vaadin. It was located in shared folder, but it tried to load application specific class derived from com.vaadin.Application. Another problem arose from hibernate and ehcache pair. net.sf.ehcache.hibernate.EhCacheRegionFactory class which is in ehcache-core.jar tried to load ehcache.xml, located in WEB-INF/classes. Actually, those libraries should have been more careful about loading/accessing such resources/classes. However, in reality it is very easy to be hit by such a problem when you try to centralise common libraries into a shared folder. I consider that even one of the main obstacles for OSGI to be main stream was such class loading issues and incompatibilities of libraries in OSGI environments.